diff --git a/messages/en-US.json b/messages/en-US.json index 2f7fd3e0..c3e93ba8 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1895,5 +1895,6 @@ "certResolver": "Certificate Resolver", "certResolverDescription": "Select the certificate resolver to use for this resource.", "selectCertResolver": "Select Certificate Resolver", - "enterCustomResolver": "Enter Custom Resolver" + "enterCustomResolver": "Enter Custom Resolver", + "preferWildcardCert": "Prefer Wildcard Certificate" } diff --git a/server/db/pg/schema/schema.ts b/server/db/pg/schema/schema.ts index 33b2ae28..ae5205bb 100644 --- a/server/db/pg/schema/schema.ts +++ b/server/db/pg/schema/schema.ts @@ -20,7 +20,8 @@ export const domains = pgTable("domains", { failed: boolean("failed").notNull().default(false), tries: integer("tries").notNull().default(0), certResolver: varchar("certResolver"), - customCertResolver: varchar("customCertResolver") + customCertResolver: varchar("customCertResolver"), + preferWildcardCert: boolean("preferWildcardCert") }); export const orgs = pgTable("orgs", { diff --git a/server/db/sqlite/schema/schema.ts b/server/db/sqlite/schema/schema.ts index ebfe7bcf..30841a4b 100644 --- a/server/db/sqlite/schema/schema.ts +++ b/server/db/sqlite/schema/schema.ts @@ -13,7 +13,7 @@ export const domains = sqliteTable("domains", { failed: integer("failed", { mode: "boolean" }).notNull().default(false), tries: integer("tries").notNull().default(0), certResolver: text("certResolver"), - customCertResolver: text("customCertResolver") + preferWildcardCert: integer("preferWildcardCert", { mode: "boolean" }) }); export const orgs = sqliteTable("orgs", { diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index 436c76a6..45729a30 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -77,8 +77,7 @@ export async function getTraefikConfig( subnet: sites.subnet, exitNodeId: sites.exitNodeId, // Domain cert resolver fields - domainCertResolver: domains.certResolver, - domainCustomCertResolver: domains.customCertResolver + domainCertResolver: domains.certResolver }) .from(sites) .innerJoin(targets, eq(targets.siteId, sites.siteId)) @@ -167,8 +166,7 @@ export async function getTraefikConfig( rewritePathType: row.rewritePathType, priority: priority, // Store domain cert resolver fields - domainCertResolver: row.domainCertResolver, - domainCustomCertResolver: row.domainCustomCertResolver + domainCertResolver: row.domainCertResolver }); } @@ -247,42 +245,47 @@ export async function getTraefikConfig( wildCard = resource.fullDomain; } - const configDomain = config.getDomain(resource.domainId); - const rawTraefikCfg = config.getRawConfig().traefik || {}; - const globalDefaultResolver = rawTraefikCfg.cert_resolver; + const globalDefaultResolver = + config.getRawConfig().traefik.cert_resolver; + const globalDefaultPreferWildcard = + config.getRawConfig().traefik.prefer_wildcard_cert; - - const domainCertResolver = - resource.domainCertResolver ?? configDomain?.cert_resolver; - const domainCustomResolver = - resource.domainCustomCertResolver; - const preferWildcardCert = - resource.preferWildcardCert ?? configDomain?.prefer_wildcard_cert ?? false; + const domainCertResolver = resource.domainCertResolver; + const preferWildcardCert = resource.preferWildcardCert; let resolverName: string | undefined; - + let preferWildcard: boolean | undefined; // Handle both letsencrypt & custom cases - if (domainCertResolver === "custom") { - resolverName = domainCustomResolver?.trim(); - } else if (domainCertResolver) { - resolverName = domainCertResolver; + if (domainCertResolver) { + resolverName = domainCertResolver.trim(); } else { resolverName = globalDefaultResolver; } - const tls = { - certResolver: resolverName, - ...(preferWildcardCert - ? { - domains: [ - { - main: wildCard - } - ] - } - : {}) - }; + if ( + preferWildcardCert !== undefined && + preferWildcardCert !== null + ) { + preferWildcard = preferWildcardCert; + } else { + preferWildcard = globalDefaultPreferWildcard; + } + let tls = {}; + if (build == "oss") { + tls = { + certResolver: resolverName, + ...(preferWildcard + ? { + domains: [ + { + main: wildCard + } + ] + } + : {}) + }; + } const additionalMiddlewares = config.getRawConfig().traefik.additional_middlewares || []; diff --git a/server/private/lib/traefik/getTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts index 634bc818..c0f934cd 100644 --- a/server/private/lib/traefik/getTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -108,7 +108,6 @@ export async function getTraefikConfig( // Certificate certificateStatus: certificates.status, domainCertResolver: domains.certResolver, - domainCustomCertResolver: domains.customCertResolver }) .from(sites) .innerJoin(targets, eq(targets.siteId, sites.siteId)) @@ -206,7 +205,6 @@ export async function getTraefikConfig( rewritePathType: row.rewritePathType, priority: priority, // may be null, we fallback later domainCertResolver: row.domainCertResolver, - domainCustomCertResolver: row.domainCustomCertResolver }); } @@ -306,29 +304,6 @@ export async function getTraefikConfig( wildCard = resource.fullDomain; } - const configDomain = config.getDomain(resource.domainId); - const rawTraefikCfg = config.getRawConfig().traefik || {}; - const globalDefaultResolver = rawTraefikCfg.cert_resolver; - - - const domainCertResolver = - resource.domainCertResolver ?? configDomain?.cert_resolver; - const domainCustomResolver = - resource.domainCustomCertResolver; - const preferWildcardCert = - resource.preferWildcardCert ?? configDomain?.prefer_wildcard_cert ?? false; - - let resolverName: string | undefined; - - // Handle both letsencrypt & custom cases - if (domainCertResolver === "custom") { - resolverName = domainCustomResolver?.trim(); - } else if (domainCertResolver) { - resolverName = domainCertResolver; - } else { - resolverName = globalDefaultResolver; - } - let tls = {}; if (!privateConfig.getRawPrivateConfig().flags.use_pangolin_dns) { const domainParts = fullDomain.split("."); diff --git a/server/routers/domain/createOrgDomain.ts b/server/routers/domain/createOrgDomain.ts index 54cad172..f9a9dcbd 100644 --- a/server/routers/domain/createOrgDomain.ts +++ b/server/routers/domain/createOrgDomain.ts @@ -25,8 +25,8 @@ const bodySchema = z .object({ type: z.enum(["ns", "cname", "wildcard"]), baseDomain: subdomainSchema, - certResolver: z.enum(["letsencrypt", "custom"]).optional(), // optional, only for wildcard - customCertResolver: z.string().optional() // required if certResolver === "custom" + certResolver: z.string().optional().nullable(), + preferWildcardCert: z.boolean().optional().nullable() // optional, only for wildcard }) .strict(); @@ -38,7 +38,7 @@ export type CreateDomainResponse = { aRecords?: { baseDomain: string; value: string }[]; txtRecords?: { baseDomain: string; value: string }[]; certResolver?: string | null; - customCertResolver?: string | null; + preferWildcardCert?: boolean; }; // Helper to check if a domain is a subdomain or equal to another domain @@ -76,7 +76,7 @@ export async function createOrgDomain( } const { orgId } = parsedParams.data; - const { type, baseDomain, certResolver, customCertResolver } = parsedBody.data; + const { type, baseDomain, certResolver, preferWildcardCert } = parsedBody.data; if (build == "oss") { if (type !== "wildcard") { @@ -261,7 +261,7 @@ export async function createOrgDomain( type, verified: type === "wildcard" ? true : false, certResolver: certResolver || null, - customCertResolver: customCertResolver || null + preferWildcardCert: preferWildcardCert || false }) .returning(); @@ -334,7 +334,7 @@ export async function createOrgDomain( nsRecords, aRecords, certResolver: returned.certResolver, - customCertResolver: returned.customCertResolver + preferWildcardCert: returned.preferWildcardCert }, success: true, error: false, diff --git a/server/routers/domain/listDomains.ts b/server/routers/domain/listDomains.ts index 85bc3caa..55ea99cb 100644 --- a/server/routers/domain/listDomains.ts +++ b/server/routers/domain/listDomains.ts @@ -44,7 +44,7 @@ async function queryDomains(orgId: string, limit: number, offset: number) { tries: domains.tries, configManaged: domains.configManaged, certResolver: domains.certResolver, - customCertResolver: domains.customCertResolver, + preferWildcardCert: domains.preferWildcardCert }) .from(orgDomains) .where(eq(orgDomains.orgId, orgId)) diff --git a/server/setup/copyInConfig.ts b/server/setup/copyInConfig.ts index b8c00192..a8627d5e 100644 --- a/server/setup/copyInConfig.ts +++ b/server/setup/copyInConfig.ts @@ -37,7 +37,9 @@ async function copyInDomains() { const configDomains = Object.entries(rawDomains).map( ([key, value]) => ({ domainId: key, - baseDomain: value.base_domain.toLowerCase() + baseDomain: value.base_domain.toLowerCase(), + certResolver: value.cert_resolver || null, + preferWildcardCert: value.prefer_wildcard_cert || null }) ); @@ -59,11 +61,11 @@ async function copyInDomains() { } } - for (const { domainId, baseDomain } of configDomains) { + for (const { domainId, baseDomain, certResolver, preferWildcardCert } of configDomains) { if (existingDomainKeys.has(domainId)) { await trx .update(domains) - .set({ baseDomain, verified: true, type: "wildcard" }) + .set({ baseDomain, verified: true, type: "wildcard", certResolver, preferWildcardCert }) .where(eq(domains.domainId, domainId)) .execute(); } else { @@ -74,7 +76,9 @@ async function copyInDomains() { baseDomain, configManaged: true, type: "wildcard", - verified: true + verified: true, + certResolver, + preferWildcardCert }) .execute(); } diff --git a/src/components/CreateDomainForm.tsx b/src/components/CreateDomainForm.tsx index 24b1d466..7e7fcc66 100644 --- a/src/components/CreateDomainForm.tsx +++ b/src/components/CreateDomainForm.tsx @@ -11,6 +11,7 @@ import { FormDescription } from "@app/components/ui/form"; import { Input } from "@app/components/ui/input"; +import { Checkbox, CheckboxWithLabel } from "@app/components/ui/checkbox"; import { useToast } from "@app/hooks/useToast"; import { zodResolver } from "@hookform/resolvers/zod"; import { useState, useMemo } from "react"; @@ -98,8 +99,8 @@ const formSchema = z.object({ .refine((val) => isValidDomainFormat(val), "Invalid domain format") .transform((val) => toPunycode(val)), type: z.enum(["ns", "cname", "wildcard"]), - certResolver: z.string().optional(), - customCertResolver: z.string().optional() + certResolver: z.string().nullable().optional(), + preferWildcardCert: z.boolean().optional() }); type FormValues = z.infer; @@ -112,7 +113,7 @@ type CreateDomainFormProps = { // Example cert resolver options (replace with real API/fetch if needed) const certResolverOptions = [ - { id: "letsencrypt", title: "Let's Encrypt" }, + { id: "default", title: "Default" }, { id: "custom", title: "Custom Resolver" } ]; @@ -135,8 +136,8 @@ export default function CreateDomainForm({ defaultValues: { baseDomain: "", type: build == "oss" || !env.flags.usePangolinDns ? "wildcard" : "ns", - certResolver: "", - customCertResolver: "" + certResolver: null, + preferWildcardCert: false } }); @@ -281,8 +282,20 @@ export default function CreateDomainForm({ {t("certResolver")} - {field.value === "custom" && ( - - - form.setValue("customCertResolver", e.target.value) - } + {field.value !== null && field.value !== "default" && ( +
+ + field.onChange(e.target.value)} + /> + + ( + + + + + {/*
+ + {t("preferWildcardCert")} + +
*/} +
+ )} /> - +
)} )} /> - )}