Add maintence options to blueprints

This commit is contained in:
Owen
2025-12-22 14:00:34 -05:00
parent 71386d3b05
commit 2e60ecec87
3 changed files with 93 additions and 32 deletions

View File

@@ -2,7 +2,8 @@ import {
domains, domains,
orgDomains, orgDomains,
Resource, Resource,
resourceHeaderAuth, resourceHeaderAuthExtendedCompatibility, resourceHeaderAuth,
resourceHeaderAuthExtendedCompatibility,
resourcePincode, resourcePincode,
resourceRules, resourceRules,
resourceWhitelist, resourceWhitelist,
@@ -30,7 +31,8 @@ import {pickPort} from "@server/routers/target/helpers";
import { resourcePassword } from "@server/db"; import { resourcePassword } from "@server/db";
import { hashPassword } from "@server/auth/password"; import { hashPassword } from "@server/auth/password";
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators"; import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
import {get} from "http"; import { isLicensedOrSubscribed } from "../isLicencedOrSubscribed";
import { build } from "@server/build";
export type ProxyResourcesResults = { export type ProxyResourcesResults = {
proxyResource: Resource; proxyResource: Resource;
@@ -209,6 +211,16 @@ export async function updateProxyResources(
resource = existingResource; resource = existingResource;
} else { } else {
// Update existing resource // Update existing resource
const isLicensed = await isLicensedOrSubscribed(orgId);
if (build == "enterprise" && !isLicensed) {
logger.warn(
"Server is not licensed! Clearing set maintenance screen values"
);
// null the maintenance mode fields if not licensed
resourceData.maintenance = undefined;
}
[resource] = await trx [resource] = await trx
.update(resources) .update(resources)
.set({ .set({
@@ -233,7 +245,14 @@ export async function updateProxyResources(
: false, : false,
headers: headers || null, headers: headers || null,
applyRules: applyRules:
resourceData.rules && resourceData.rules.length > 0 resourceData.rules && resourceData.rules.length > 0,
maintenanceModeEnabled:
resourceData.maintenance?.enabled,
maintenanceModeType: resourceData.maintenance?.type,
maintenanceTitle: resourceData.maintenance?.title,
maintenanceMessage: resourceData.maintenance?.message,
maintenanceEstimatedTime:
resourceData.maintenance?.["estimated-time"]
}) })
.where( .where(
eq(resources.resourceId, existingResource.resourceId) eq(resources.resourceId, existingResource.resourceId)
@@ -303,8 +322,13 @@ export async function updateProxyResources(
const headerAuthPassword = const headerAuthPassword =
resourceData.auth?.["basic-auth"]?.password; resourceData.auth?.["basic-auth"]?.password;
const headerAuthExtendedCompatibility = const headerAuthExtendedCompatibility =
resourceData.auth?.["basic-auth"]?.extendedCompatibility; resourceData.auth?.["basic-auth"]
if (headerAuthUser && headerAuthPassword && headerAuthExtendedCompatibility !== null) { ?.extendedCompatibility;
if (
headerAuthUser &&
headerAuthPassword &&
headerAuthExtendedCompatibility !== null
) {
const headerAuthHash = await hashPassword( const headerAuthHash = await hashPassword(
Buffer.from( Buffer.from(
`${headerAuthUser}:${headerAuthPassword}` `${headerAuthUser}:${headerAuthPassword}`
@@ -315,9 +339,12 @@ export async function updateProxyResources(
resourceId: existingResource.resourceId, resourceId: existingResource.resourceId,
headerAuthHash headerAuthHash
}), }),
trx.insert(resourceHeaderAuthExtendedCompatibility).values({ trx
.insert(resourceHeaderAuthExtendedCompatibility)
.values({
resourceId: existingResource.resourceId, resourceId: existingResource.resourceId,
extendedCompatibilityIsActivated: headerAuthExtendedCompatibility extendedCompatibilityIsActivated:
headerAuthExtendedCompatibility
}) })
]); ]);
} }
@@ -622,6 +649,15 @@ export async function updateProxyResources(
); );
} }
const isLicensed = await isLicensedOrSubscribed(orgId);
if (build == "enterprise" && !isLicensed) {
logger.warn(
"Server is not licensed! Clearing set maintenance screen values"
);
// null the maintenance mode fields if not licensed
resourceData.maintenance = undefined;
}
// Create new resource // Create new resource
const [newResource] = await trx const [newResource] = await trx
.insert(resources) .insert(resources)
@@ -643,7 +679,13 @@ export async function updateProxyResources(
ssl: resourceSsl, ssl: resourceSsl,
headers: headers || null, headers: headers || null,
applyRules: applyRules:
resourceData.rules && resourceData.rules.length > 0 resourceData.rules && resourceData.rules.length > 0,
maintenanceModeEnabled: resourceData.maintenance?.enabled,
maintenanceModeType: resourceData.maintenance?.type,
maintenanceTitle: resourceData.maintenance?.title,
maintenanceMessage: resourceData.maintenance?.message,
maintenanceEstimatedTime:
resourceData.maintenance?.["estimated-time"]
}) })
.returning(); .returning();
@@ -674,9 +716,14 @@ export async function updateProxyResources(
const headerAuthUser = resourceData.auth?.["basic-auth"]?.user; const headerAuthUser = resourceData.auth?.["basic-auth"]?.user;
const headerAuthPassword = const headerAuthPassword =
resourceData.auth?.["basic-auth"]?.password; resourceData.auth?.["basic-auth"]?.password;
const headerAuthExtendedCompatibility = resourceData.auth?.["basic-auth"]?.extendedCompatibility; const headerAuthExtendedCompatibility =
resourceData.auth?.["basic-auth"]?.extendedCompatibility;
if (headerAuthUser && headerAuthPassword && headerAuthExtendedCompatibility !== null) { if (
headerAuthUser &&
headerAuthPassword &&
headerAuthExtendedCompatibility !== null
) {
const headerAuthHash = await hashPassword( const headerAuthHash = await hashPassword(
Buffer.from( Buffer.from(
`${headerAuthUser}:${headerAuthPassword}` `${headerAuthUser}:${headerAuthPassword}`
@@ -688,10 +735,13 @@ export async function updateProxyResources(
resourceId: newResource.resourceId, resourceId: newResource.resourceId,
headerAuthHash headerAuthHash
}), }),
trx.insert(resourceHeaderAuthExtendedCompatibility).values({ trx
.insert(resourceHeaderAuthExtendedCompatibility)
.values({
resourceId: newResource.resourceId, resourceId: newResource.resourceId,
extendedCompatibilityIsActivated: headerAuthExtendedCompatibility extendedCompatibilityIsActivated:
}), headerAuthExtendedCompatibility
})
]); ]);
} }
} }

View File

@@ -1,5 +1,6 @@
import { z } from "zod"; import { z } from "zod";
import { portRangeStringSchema } from "@server/lib/ip"; import { portRangeStringSchema } from "@server/lib/ip";
import { MaintenanceSchema } from "#dynamic/lib/blueprints/types";
export const SiteSchema = z.object({ export const SiteSchema = z.object({
name: z.string().min(1).max(100), name: z.string().min(1).max(100),
@@ -156,7 +157,8 @@ export const ResourceSchema = z
"host-header": z.string().optional(), "host-header": z.string().optional(),
"tls-server-name": z.string().optional(), "tls-server-name": z.string().optional(),
headers: z.array(HeaderSchema).optional(), headers: z.array(HeaderSchema).optional(),
rules: z.array(RuleSchema).optional() rules: z.array(RuleSchema).optional(),
maintenance: MaintenanceSchema.optional()
}) })
.refine( .refine(
(resource) => { (resource) => {

View File

@@ -0,0 +1,9 @@
import { z } from "zod";
export const MaintenanceSchema = z.object({
enabled: z.boolean().optional(),
type: z.enum(["forced", "automatic"]).optional(),
title: z.string().max(255).nullable().optional(),
message: z.string().max(2000).nullable().optional(),
"estimated-time": z.string().max(100).nullable().optional()
});