From e7828a43fa055fc7d53d51b64a9acd436a894442 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 13 Oct 2025 10:32:41 -0700 Subject: [PATCH] Add flag for generate own certs --- server/lib/traefik/TraefikConfigManager.ts | 151 ++++++----- server/lib/traefik/getTraefikConfig.ts | 94 ++++--- server/private/lib/config.ts | 4 + server/private/lib/rateLimitStore.ts | 2 +- server/private/lib/readConfigFile.ts | 254 +++++++++--------- server/private/lib/redis.ts | 2 +- .../private/lib/traefik/getTraefikConfig.ts | 63 ++--- .../routers/certificates/createCertificate.ts | 18 +- .../resources/[niceId]/rules/page.tsx | 4 +- src/components/ResourceInfoBox.tsx | 61 +++-- src/components/private/AuthPageSettings.tsx | 5 +- src/lib/pullEnv.ts | 6 +- src/lib/types/env.ts | 1 + 13 files changed, 362 insertions(+), 303 deletions(-) diff --git a/server/lib/traefik/TraefikConfigManager.ts b/server/lib/traefik/TraefikConfigManager.ts index 017678f1..4898c4ac 100644 --- a/server/lib/traefik/TraefikConfigManager.ts +++ b/server/lib/traefik/TraefikConfigManager.ts @@ -8,9 +8,7 @@ import { db, exitNodes } from "@server/db"; import { eq } from "drizzle-orm"; import { getCurrentExitNodeId } from "@server/lib/exitNodes"; import { getTraefikConfig } from "#dynamic/lib/traefik"; -import { - getValidCertificatesForDomains, -} from "#dynamic/lib/certificates"; +import { getValidCertificatesForDomains } from "#dynamic/lib/certificates"; import { sendToExitNode } from "#dynamic/lib/exitNodes"; import { build } from "@server/build"; @@ -311,84 +309,92 @@ export class TraefikConfigManager { this.lastActiveDomains = new Set(domains); } - // Scan current local certificate state - this.lastLocalCertificateState = - await this.scanLocalCertificateState(); + if ( + process.env.GENERATE_OWN_CERTIFICATES === "true" && + build != "oss" + ) { + // Scan current local certificate state + this.lastLocalCertificateState = + await this.scanLocalCertificateState(); - // Only fetch certificates if needed (domain changes, missing certs, or daily renewal check) - let validCertificates: Array<{ - id: number; - domain: string; - wildcard: boolean | null; - certFile: string | null; - keyFile: string | null; - expiresAt: number | null; - updatedAt?: number | null; - }> = []; + // Only fetch certificates if needed (domain changes, missing certs, or daily renewal check) + let validCertificates: Array<{ + id: number; + domain: string; + wildcard: boolean | null; + certFile: string | null; + keyFile: string | null; + expiresAt: number | null; + updatedAt?: number | null; + }> = []; - if (this.shouldFetchCertificates(domains)) { - // Filter out domains that are already covered by wildcard certificates - const domainsToFetch = new Set(); - for (const domain of domains) { - if ( - !isDomainCoveredByWildcard( - domain, - this.lastLocalCertificateState - ) - ) { - domainsToFetch.add(domain); - } else { - logger.debug( - `Domain ${domain} is covered by existing wildcard certificate, skipping fetch` - ); + if (this.shouldFetchCertificates(domains)) { + // Filter out domains that are already covered by wildcard certificates + const domainsToFetch = new Set(); + for (const domain of domains) { + if ( + !isDomainCoveredByWildcard( + domain, + this.lastLocalCertificateState + ) + ) { + domainsToFetch.add(domain); + } else { + logger.debug( + `Domain ${domain} is covered by existing wildcard certificate, skipping fetch` + ); + } } - } - if (domainsToFetch.size > 0) { - // Get valid certificates for domains not covered by wildcards - validCertificates = - await getValidCertificatesForDomains(domainsToFetch); - this.lastCertificateFetch = new Date(); - this.lastKnownDomains = new Set(domains); + if (domainsToFetch.size > 0) { + // Get valid certificates for domains not covered by wildcards + validCertificates = + await getValidCertificatesForDomains( + domainsToFetch + ); + this.lastCertificateFetch = new Date(); + this.lastKnownDomains = new Set(domains); - logger.info( - `Fetched ${validCertificates.length} certificates from remote (${domains.size - domainsToFetch.size} domains covered by wildcards)` - ); + logger.info( + `Fetched ${validCertificates.length} certificates from remote (${domains.size - domainsToFetch.size} domains covered by wildcards)` + ); - // Download and decrypt new certificates - await this.processValidCertificates(validCertificates); + // Download and decrypt new certificates + await this.processValidCertificates(validCertificates); + } else { + logger.info( + "All domains are covered by existing wildcard certificates, no fetch needed" + ); + this.lastCertificateFetch = new Date(); + this.lastKnownDomains = new Set(domains); + } + + // Always ensure all existing certificates (including wildcards) are in the config + await this.updateDynamicConfigFromLocalCerts(domains); } else { - logger.info( - "All domains are covered by existing wildcard certificates, no fetch needed" - ); - this.lastCertificateFetch = new Date(); - this.lastKnownDomains = new Set(domains); + const timeSinceLastFetch = this.lastCertificateFetch + ? Math.round( + (Date.now() - + this.lastCertificateFetch.getTime()) / + (1000 * 60) + ) + : 0; + + // logger.debug( + // `Skipping certificate fetch - no changes detected and within 24-hour window (last fetch: ${timeSinceLastFetch} minutes ago)` + // ); + + // Still need to ensure config is up to date with existing certificates + await this.updateDynamicConfigFromLocalCerts(domains); } - // Always ensure all existing certificates (including wildcards) are in the config - await this.updateDynamicConfigFromLocalCerts(domains); - } else { - const timeSinceLastFetch = this.lastCertificateFetch - ? Math.round( - (Date.now() - this.lastCertificateFetch.getTime()) / - (1000 * 60) - ) - : 0; + // Clean up certificates for domains no longer in use + await this.cleanupUnusedCertificates(domains); - // logger.debug( - // `Skipping certificate fetch - no changes detected and within 24-hour window (last fetch: ${timeSinceLastFetch} minutes ago)` - // ); - - // Still need to ensure config is up to date with existing certificates - await this.updateDynamicConfigFromLocalCerts(domains); + // wait 1 second for traefik to pick up the new certificates + await new Promise((resolve) => setTimeout(resolve, 500)); } - // Clean up certificates for domains no longer in use - await this.cleanupUnusedCertificates(domains); - - // wait 1 second for traefik to pick up the new certificates - await new Promise((resolve) => setTimeout(resolve, 500)); - // Write traefik config as YAML to a second dynamic config file if changed await this.writeTraefikDynamicConfig(traefikConfig); @@ -690,7 +696,12 @@ export class TraefikConfigManager { for (const cert of validCertificates) { try { - if (!cert.certFile || !cert.keyFile) { + if ( + !cert.certFile || + !cert.keyFile || + cert.certFile.length === 0 || + cert.keyFile.length === 0 + ) { logger.warn( `Certificate for domain ${cert.domain} is missing cert or key file` ); diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index 7e1ce562..2de7b413 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -105,7 +105,12 @@ export async function getTraefikConfig( const priority = row.priority ?? 100; // Create a unique key combining resourceId, path config, and rewrite config - const pathKey = [targetPath, pathMatchType, rewritePath, rewritePathType] + const pathKey = [ + targetPath, + pathMatchType, + rewritePath, + rewritePathType + ] .filter(Boolean) .join("-"); const mapKey = [resourceId, pathKey].filter(Boolean).join("-"); @@ -120,13 +125,15 @@ export async function getTraefikConfig( ); if (!validation.isValid) { - logger.error(`Invalid path rewrite configuration for resource ${resourceId}: ${validation.error}`); + logger.error( + `Invalid path rewrite configuration for resource ${resourceId}: ${validation.error}` + ); return; } resourcesMap.set(key, { resourceId: row.resourceId, - name: resourceName, + name: resourceName, fullDomain: row.fullDomain, ssl: row.ssl, http: row.http, @@ -239,21 +246,18 @@ export async function getTraefikConfig( preferWildcardCert = configDomain.prefer_wildcard_cert; } - let tls = {}; - if (build == "oss") { - tls = { - certResolver: certResolver, - ...(preferWildcardCert - ? { - domains: [ - { - main: wildCard - } - ] - } - : {}) - }; - } + const tls = { + certResolver: certResolver, + ...(preferWildcardCert + ? { + domains: [ + { + main: wildCard + } + ] + } + : {}) + }; const additionalMiddlewares = config.getRawConfig().traefik.additional_middlewares || []; @@ -264,11 +268,12 @@ export async function getTraefikConfig( ]; // Handle path rewriting middleware - if (resource.rewritePath && + if ( + resource.rewritePath && resource.path && resource.pathMatchType && - resource.rewritePathType) { - + resource.rewritePathType + ) { // Create a unique middleware name const rewriteMiddlewareName = `rewrite-r${resource.resourceId}-${key}`; @@ -287,7 +292,10 @@ export async function getTraefikConfig( } // the middleware to the config - Object.assign(config_output.http.middlewares, rewriteResult.middlewares); + Object.assign( + config_output.http.middlewares, + rewriteResult.middlewares + ); // middlewares to the router middleware chain if (rewriteResult.chain) { @@ -298,9 +306,13 @@ export async function getTraefikConfig( routerMiddlewares.push(rewriteMiddlewareName); } - logger.debug(`Created path rewrite middleware ${rewriteMiddlewareName}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})`); + logger.debug( + `Created path rewrite middleware ${rewriteMiddlewareName}: ${resource.pathMatchType}(${resource.path}) -> ${resource.rewritePathType}(${resource.rewritePath})` + ); } catch (error) { - logger.error(`Failed to create path rewrite middleware for resource ${resource.resourceId}: ${error}`); + logger.error( + `Failed to create path rewrite middleware for resource ${resource.resourceId}: ${error}` + ); } } @@ -316,7 +328,9 @@ export async function getTraefikConfig( value: string; }[]; } catch (e) { - logger.warn(`Failed to parse headers for resource ${resource.resourceId}: ${e}`); + logger.warn( + `Failed to parse headers for resource ${resource.resourceId}: ${e}` + ); } headersArr.forEach((header) => { @@ -482,14 +496,14 @@ export async function getTraefikConfig( })(), ...(resource.stickySession ? { - sticky: { - cookie: { - name: "p_sticky", // TODO: make this configurable via config.yml like other cookies - secure: resource.ssl, - httpOnly: true - } - } - } + sticky: { + cookie: { + name: "p_sticky", // TODO: make this configurable via config.yml like other cookies + secure: resource.ssl, + httpOnly: true + } + } + } : {}) } }; @@ -590,13 +604,13 @@ export async function getTraefikConfig( })(), ...(resource.stickySession ? { - sticky: { - ipStrategy: { - depth: 0, - sourcePort: true - } - } - } + sticky: { + ipStrategy: { + depth: 0, + sourcePort: true + } + } + } : {}) } }; diff --git a/server/private/lib/config.ts b/server/private/lib/config.ts index a0dbf9a3..8d746960 100644 --- a/server/private/lib/config.ts +++ b/server/private/lib/config.ts @@ -148,6 +148,10 @@ export class PrivateConfig { if (parsedPrivateConfig.stripe?.s3Region) { process.env.S3_REGION = parsedPrivateConfig.stripe.s3Region; } + if (parsedPrivateConfig.flags?.generate_own_certificates) { + process.env.GENERATE_OWN_CERTIFICATES = + parsedPrivateConfig.flags.generate_own_certificates.toString(); + } } this.rawPrivateConfig = parsedPrivateConfig; diff --git a/server/private/lib/rateLimitStore.ts b/server/private/lib/rateLimitStore.ts index 11e1a9d6..20355125 100644 --- a/server/private/lib/rateLimitStore.ts +++ b/server/private/lib/rateLimitStore.ts @@ -17,7 +17,7 @@ import { MemoryStore, Store } from "express-rate-limit"; import RedisStore from "#private/lib/redisStore"; export function createStore(): Store { - if (build != "oss" && privateConfig.getRawPrivateConfig().flags?.enable_redis) { + if (build != "oss" && privateConfig.getRawPrivateConfig().flags.enable_redis) { const rateLimitStore: Store = new RedisStore({ prefix: "api-rate-limit", // Optional: customize Redis key prefix skipFailedRequests: true, // Don't count failed requests diff --git a/server/private/lib/readConfigFile.ts b/server/private/lib/readConfigFile.ts index c1847ba5..1228a7d7 100644 --- a/server/private/lib/readConfigFile.ts +++ b/server/private/lib/readConfigFile.ts @@ -20,141 +20,151 @@ import { build } from "@server/build"; const portSchema = z.number().positive().gt(0).lte(65535); -export const privateConfigSchema = z - .object({ - app: z.object({ +export const privateConfigSchema = z.object({ + app: z + .object({ region: z.string().optional().default("default"), base_domain: z.string().optional() - }).optional().default({ + }) + .optional() + .default({ region: "default" }), - server: z.object({ + server: z + .object({ encryption_key_path: z .string() .optional() .default("./config/encryption.pem") .pipe(z.string().min(8)), resend_api_key: z.string().optional(), - reo_client_id: z.string().optional(), - }).optional().default({ + reo_client_id: z.string().optional() + }) + .optional() + .default({ encryption_key_path: "./config/encryption.pem" }), - redis: z - .object({ - host: z.string(), - port: portSchema, - password: z.string().optional(), - db: z.number().int().nonnegative().optional().default(0), - replicas: z - .array( - z.object({ - host: z.string(), - port: portSchema, - password: z.string().optional(), - db: z.number().int().nonnegative().optional().default(0) + redis: z + .object({ + host: z.string(), + port: portSchema, + password: z.string().optional(), + db: z.number().int().nonnegative().optional().default(0), + replicas: z + .array( + z.object({ + host: z.string(), + port: portSchema, + password: z.string().optional(), + db: z.number().int().nonnegative().optional().default(0) + }) + ) + .optional() + // tls: z + // .object({ + // reject_unauthorized: z + // .boolean() + // .optional() + // .default(true) + // }) + // .optional() + }) + .optional(), + gerbil: z + .object({ + local_exit_node_reachable_at: z + .string() + .optional() + .default("http://gerbil:3003") + }) + .optional() + .default({}), + flags: z + .object({ + enable_redis: z.boolean().optional().default(false), + generate_own_certificates: z.boolean().optional().default(false) + }) + .optional() + .default({}), + branding: z + .object({ + app_name: z.string().optional(), + background_image_path: z.string().optional(), + colors: z + .object({ + light: colorsSchema.optional(), + dark: colorsSchema.optional() + }) + .optional(), + logo: z + .object({ + light_path: z.string().optional(), + dark_path: z.string().optional(), + auth_page: z + .object({ + width: z.number().optional(), + height: z.number().optional() }) - ) - .optional() - // tls: z - // .object({ - // reject_unauthorized: z - // .boolean() - // .optional() - // .default(true) - // }) - // .optional() - }) - .optional(), - gerbil: z - .object({ - local_exit_node_reachable_at: z.string().optional().default("http://gerbil:3003") - }) - .optional() - .default({}), - flags: z - .object({ - enable_redis: z.boolean().optional(), - }) - .optional(), - branding: z - .object({ - app_name: z.string().optional(), - background_image_path: z.string().optional(), - colors: z - .object({ - light: colorsSchema.optional(), - dark: colorsSchema.optional() - }) - .optional(), - logo: z - .object({ - light_path: z.string().optional(), - dark_path: z.string().optional(), - auth_page: z - .object({ - width: z.number().optional(), - height: z.number().optional() - }) - .optional(), - navbar: z - .object({ - width: z.number().optional(), - height: z.number().optional() - }) - .optional() - }) - .optional(), - favicon_path: z.string().optional(), - footer: z - .array( - z.object({ - text: z.string(), - href: z.string().optional() + .optional(), + navbar: z + .object({ + width: z.number().optional(), + height: z.number().optional() }) - ) - .optional(), - login_page: z - .object({ - subtitle_text: z.string().optional(), - title_text: z.string().optional() + .optional() + }) + .optional(), + favicon_path: z.string().optional(), + footer: z + .array( + z.object({ + text: z.string(), + href: z.string().optional() }) - .optional(), - signup_page: z - .object({ - subtitle_text: z.string().optional(), - title_text: z.string().optional() - }) - .optional(), - resource_auth_page: z - .object({ - show_logo: z.boolean().optional(), - hide_powered_by: z.boolean().optional(), - title_text: z.string().optional(), - subtitle_text: z.string().optional() - }) - .optional(), - emails: z - .object({ - signature: z.string().optional(), - colors: z - .object({ - primary: z.string().optional() - }) - .optional() - }) - .optional() - }) - .optional(), - stripe: z - .object({ - secret_key: z.string(), - webhook_secret: z.string(), - s3Bucket: z.string(), - s3Region: z.string().default("us-east-1"), - localFilePath: z.string() - }) - .optional(), - }); + ) + .optional(), + login_page: z + .object({ + subtitle_text: z.string().optional(), + title_text: z.string().optional() + }) + .optional(), + signup_page: z + .object({ + subtitle_text: z.string().optional(), + title_text: z.string().optional() + }) + .optional(), + resource_auth_page: z + .object({ + show_logo: z.boolean().optional(), + hide_powered_by: z.boolean().optional(), + title_text: z.string().optional(), + subtitle_text: z.string().optional() + }) + .optional(), + emails: z + .object({ + signature: z.string().optional(), + colors: z + .object({ + primary: z.string().optional() + }) + .optional() + }) + .optional() + }) + .optional(), + stripe: z + .object({ + secret_key: z.string(), + webhook_secret: z.string(), + s3Bucket: z.string(), + s3Region: z.string().default("us-east-1"), + localFilePath: z.string() + }) + .optional() +}); export function readPrivateConfigFile() { if (build == "oss") { @@ -182,9 +192,7 @@ export function readPrivateConfigFile() { } if (!environment) { - throw new Error( - "No private configuration file found." - ); + throw new Error("No private configuration file found."); } return environment; diff --git a/server/private/lib/redis.ts b/server/private/lib/redis.ts index e74874f2..324a6a74 100644 --- a/server/private/lib/redis.ts +++ b/server/private/lib/redis.ts @@ -46,7 +46,7 @@ class RedisManager { this.isEnabled = false; return; } - this.isEnabled = privateConfig.getRawPrivateConfig().flags?.enable_redis || false; + this.isEnabled = privateConfig.getRawPrivateConfig().flags.enable_redis || false; if (this.isEnabled) { this.initializeClients(); } diff --git a/server/private/lib/traefik/getTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts index 161ef48c..dffaf807 100644 --- a/server/private/lib/traefik/getTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -21,11 +21,10 @@ import { } from "@server/db"; import { and, eq, inArray, or, isNull, ne, isNotNull, desc } from "drizzle-orm"; import logger from "@server/logger"; -import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; -import { build } from "@server/build"; import { sanitize } from "@server/lib/traefik/utils"; +import privateConfig from "#private/lib/config"; const redirectHttpsMiddlewareName = "redirect-to-https"; const redirectToRootMiddlewareName = "redirect-to-root"; @@ -79,7 +78,7 @@ export async function getTraefikConfig( path: targets.path, pathMatchType: targets.pathMatchType, priority: targets.priority, - + // Site fields siteId: sites.siteId, siteType: sites.type, @@ -234,12 +233,13 @@ export async function getTraefikConfig( continue; } - if (resource.certificateStatus !== "valid") { - logger.debug( - `Resource ${resource.resourceId} has certificate stats ${resource.certificateStats}` - ); - continue; - } + // TODO: for now dont filter it out because if you have multiple domain ids and one is failed it causes all of them to fail + // if (resource.certificateStatus !== "valid" && privateConfig.getRawPrivateConfig().flags.generate_own_certificates) { + // logger.debug( + // `Resource ${resource.resourceId} has certificate stats ${resource.certificateStats}` + // ); + // continue; + // } // add routers and services empty objects if they don't exist if (!config_output.http.routers) { @@ -264,18 +264,21 @@ export async function getTraefikConfig( const configDomain = config.getDomain(resource.domainId); - let certResolver: string, preferWildcardCert: boolean; - if (!configDomain) { - certResolver = config.getRawConfig().traefik.cert_resolver; - preferWildcardCert = - config.getRawConfig().traefik.prefer_wildcard_cert; - } else { - certResolver = configDomain.cert_resolver; - preferWildcardCert = configDomain.prefer_wildcard_cert; - } - let tls = {}; - if (build == "oss") { + if ( + !privateConfig.getRawPrivateConfig().flags + .generate_own_certificates + ) { + let certResolver: string, preferWildcardCert: boolean; + if (!configDomain) { + certResolver = config.getRawConfig().traefik.cert_resolver; + preferWildcardCert = + config.getRawConfig().traefik.prefer_wildcard_cert; + } else { + certResolver = configDomain.cert_resolver; + preferWildcardCert = configDomain.prefer_wildcard_cert; + } + tls = { certResolver: certResolver, ...(preferWildcardCert @@ -419,7 +422,7 @@ export async function getTraefikConfig( return ( (targets as TargetWithSite[]) - .filter((target: TargetWithSite) => { + .filter((target: TargetWithSite) => { if (!target.enabled) { return false; } @@ -440,7 +443,7 @@ export async function getTraefikConfig( ) { return false; } - } else if (target.site.type === "newt") { + } else if (target.site.type === "newt") { if ( !target.internalPort || !target.method || @@ -448,10 +451,10 @@ export async function getTraefikConfig( ) { return false; } - } - return true; - }) - .map((target: TargetWithSite) => { + } + return true; + }) + .map((target: TargetWithSite) => { if ( target.site.type === "local" || target.site.type === "wireguard" @@ -459,14 +462,14 @@ export async function getTraefikConfig( return { url: `${target.method}://${target.ip}:${target.port}` }; - } else if (target.site.type === "newt") { + } else if (target.site.type === "newt") { const ip = target.site.subnet!.split("/")[0]; return { url: `${target.method}://${ip}:${target.internalPort}` }; - } - }) + } + }) // filter out duplicates .filter( (v, i, a) => @@ -709,4 +712,4 @@ export async function getTraefikConfig( } return config_output; -} \ No newline at end of file +} diff --git a/server/private/routers/certificates/createCertificate.ts b/server/private/routers/certificates/createCertificate.ts index 210878ef..463a7b88 100644 --- a/server/private/routers/certificates/createCertificate.ts +++ b/server/private/routers/certificates/createCertificate.ts @@ -15,15 +15,19 @@ import { Certificate, certificates, db, domains } from "@server/db"; import logger from "@server/logger"; import { Transaction } from "@server/db"; import { eq, or, and, like } from "drizzle-orm"; -import { build } from "@server/build"; +import privateConfig from "#private/lib/config"; /** * Checks if a certificate exists for the given domain. * If not, creates a new certificate in 'pending' state. * Wildcard certs cover subdomains. */ -export async function createCertificate(domainId: string, domain: string, trx: Transaction | typeof db) { - if (build !== "saas") { +export async function createCertificate( + domainId: string, + domain: string, + trx: Transaction | typeof db +) { + if (!privateConfig.getRawPrivateConfig().flags.generate_own_certificates) { return; } @@ -39,7 +43,7 @@ export async function createCertificate(domainId: string, domain: string, trx: T let existing: Certificate[] = []; if (domainRecord.type == "ns") { - const domainLevelDown = domain.split('.').slice(1).join('.'); + const domainLevelDown = domain.split(".").slice(1).join("."); existing = await trx .select() .from(certificates) @@ -49,7 +53,7 @@ export async function createCertificate(domainId: string, domain: string, trx: T eq(certificates.wildcard, true), // only NS domains can have wildcard certs or( eq(certificates.domain, domain), - eq(certificates.domain, domainLevelDown), + eq(certificates.domain, domainLevelDown) ) ) ); @@ -67,9 +71,7 @@ export async function createCertificate(domainId: string, domain: string, trx: T } if (existing.length > 0) { - logger.info( - `Certificate already exists for domain ${domain}` - ); + logger.info(`Certificate already exists for domain ${domain}`); return; } diff --git a/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx index 8d5ad7d3..b8459293 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/rules/page.tsx @@ -117,8 +117,8 @@ export default function ResourceRules(props: { const [openAddRuleCountrySelect, setOpenAddRuleCountrySelect] = useState(false); const router = useRouter(); const t = useTranslations(); - const env = useEnvContext(); - const isMaxmindAvailable = env.env.server.maxmind_db_path && env.env.server.maxmind_db_path.length > 0; + const { env } = useEnvContext(); + const isMaxmindAvailable = env.server.maxmind_db_path && env.server.maxmind_db_path.length > 0; const RuleAction = { ACCEPT: t('alwaysAllow'), diff --git a/src/components/ResourceInfoBox.tsx b/src/components/ResourceInfoBox.tsx index 579b2c31..ebb5fac7 100644 --- a/src/components/ResourceInfoBox.tsx +++ b/src/components/ResourceInfoBox.tsx @@ -13,12 +13,14 @@ import { import { useTranslations } from "next-intl"; import { build } from "@server/build"; import CertificateStatus from "@app/components/private/CertificateStatus"; -import { toUnicode } from 'punycode'; +import { toUnicode } from "punycode"; +import { useEnvContext } from "@app/hooks/useEnvContext"; type ResourceInfoBoxType = {}; -export default function ResourceInfoBox({ }: ResourceInfoBoxType) { +export default function ResourceInfoBox({}: ResourceInfoBoxType) { const { resource, authInfo } = useResourceContext(); + const { env } = useEnvContext(); const t = useTranslations(); @@ -28,7 +30,13 @@ export default function ResourceInfoBox({ }: ResourceInfoBoxType) { {/* 4 cols because of the certs */} - + {resource.http ? ( <> @@ -37,9 +45,9 @@ export default function ResourceInfoBox({ }: ResourceInfoBoxType) { {authInfo.password || - authInfo.pincode || - authInfo.sso || - authInfo.whitelist ? ( + authInfo.pincode || + authInfo.sso || + authInfo.whitelist ? (
{t("protected")} @@ -126,25 +134,28 @@ export default function ResourceInfoBox({ }: ResourceInfoBoxType) { {/* */} {/* */} {/* Certificate Status Column */} - {resource.http && resource.domainId && resource.fullDomain && build != "oss" && ( - - - {t("certificateStatus", { - defaultValue: "Certificate" - })} - - - - - - )} + {resource.http && + resource.domainId && + resource.fullDomain && + build != "oss" && ( + + + {t("certificateStatus", { + defaultValue: "Certificate" + })} + + + + + + )} {t("visibility")} diff --git a/src/components/private/AuthPageSettings.tsx b/src/components/private/AuthPageSettings.tsx index 8da85468..b944ab7b 100644 --- a/src/components/private/AuthPageSettings.tsx +++ b/src/components/private/AuthPageSettings.tsx @@ -80,6 +80,7 @@ const AuthPageSettings = forwardRef( const api = createApiClient(useEnvContext()); const router = useRouter(); const t = useTranslations(); + const { env } = useEnvContext(); const subscription = useSubscriptionStatusContext(); const subscribed = subscription?.getTier() === TierId.STANDARD; @@ -435,8 +436,8 @@ const AuthPageSettings = forwardRef(
{/* Certificate Status */} - {(build !== "saas" || - (build === "saas" && subscribed)) && + {( + (env.flags.generateOwnCertificates && subscribed)) && loginPage?.domainId && loginPage?.fullDomain && !hasUnsavedChanges && ( diff --git a/src/lib/pullEnv.ts b/src/lib/pullEnv.ts index dfe22a87..c099a3fb 100644 --- a/src/lib/pullEnv.ts +++ b/src/lib/pullEnv.ts @@ -48,7 +48,11 @@ export function pullEnv(): Env { enableClients: process.env.FLAGS_ENABLE_CLIENTS === "true" ? true : false, hideSupporterKey: - process.env.HIDE_SUPPORTER_KEY === "true" ? true : false + process.env.HIDE_SUPPORTER_KEY === "true" ? true : false, + generateOwnCertificates: + process.env.GENERATE_OWN_CERTIFICATES === "true" + ? true + : false }, branding: { diff --git a/src/lib/types/env.ts b/src/lib/types/env.ts index 2c3c2479..179d390f 100644 --- a/src/lib/types/env.ts +++ b/src/lib/types/env.ts @@ -28,6 +28,7 @@ export type Env = { disableBasicWireguardSites: boolean; enableClients: boolean; hideSupporterKey: boolean; + generateOwnCertificates: boolean; }, branding: { appName?: string;