From 7f227932daba514da69953b4c86b7fb50c2fe9c1 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 14 Jul 2025 12:24:38 -0700 Subject: [PATCH] Domain picker support wildcard --- server/db/pg/schema.ts | 2 +- server/db/sqlite/schema.ts | 2 +- server/setup/copyInConfig.ts | 2 +- src/components/DomainPicker.tsx | 53 ++++++++++++++++++++++++++++++--- 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index c7cea3fb..962aac70 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -13,7 +13,7 @@ export const domains = pgTable("domains", { domainId: varchar("domainId").primaryKey(), baseDomain: varchar("baseDomain").notNull(), configManaged: boolean("configManaged").notNull().default(false), - type: varchar("type"), // "ns", "cname", "a" + type: varchar("type"), // "ns", "cname", "wildcard" verified: boolean("verified").notNull().default(false), failed: boolean("failed").notNull().default(false), tries: integer("tries").notNull().default(0) diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 1dffafb9..20347cf7 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -7,7 +7,7 @@ export const domains = sqliteTable("domains", { configManaged: integer("configManaged", { mode: "boolean" }) .notNull() .default(false), - type: text("type"), // "ns", "cname", "a" + type: text("type"), // "ns", "cname", "wildcard" verified: integer("verified", { mode: "boolean" }).notNull().default(false), failed: integer("failed", { mode: "boolean" }).notNull().default(false), tries: integer("tries").notNull().default(0) diff --git a/server/setup/copyInConfig.ts b/server/setup/copyInConfig.ts index 9490b66c..772ceec8 100644 --- a/server/setup/copyInConfig.ts +++ b/server/setup/copyInConfig.ts @@ -69,7 +69,7 @@ async function copyInDomains() { } else { await trx .insert(domains) - .values({ domainId, baseDomain, configManaged: true, type: "a" }) + .values({ domainId, baseDomain, configManaged: true, type: "wildcard" }) .execute(); } } diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index 2f33b659..d46db5e9 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -26,7 +26,7 @@ type OrganizationDomain = { domainId: string; baseDomain: string; verified: boolean; - type: "ns" | "cname"; + type: "ns" | "cname" | "wildcard"; }; type AvailableOption = { @@ -40,7 +40,7 @@ type DomainOption = { domain: string; type: "organization" | "provided"; verified?: boolean; - domainType?: "ns" | "cname"; + domainType?: "ns" | "cname" | "wildcard"; domainId?: string; domainNamespaceId?: string; subdomain?: string; @@ -95,11 +95,11 @@ export default function DomainPicker({ const domains = response.data.data.domains .filter( (domain) => - domain.type === "ns" || domain.type === "cname" + domain.type === "ns" || domain.type === "cname" || domain.type === "wildcard" ) .map((domain) => ({ ...domain, - type: domain.type as "ns" | "cname" + type: domain.type as "ns" | "cname" | "wildcard" })); setOrganizationDomains(domains); } @@ -175,6 +175,41 @@ export default function DomainPicker({ domainId: orgDomain.domainId }); } + } else if (orgDomain.type === "wildcard") { + // For wildcard domains, allow the base domain or one level up + const userInputLower = userInput.toLowerCase(); + const baseDomainLower = orgDomain.baseDomain.toLowerCase(); + + // Check if user input is exactly the base domain + if (userInputLower === baseDomainLower) { + options.push({ + id: `org-${orgDomain.domainId}`, + domain: orgDomain.baseDomain, + type: "organization", + verified: orgDomain.verified, + domainType: "wildcard", + domainId: orgDomain.domainId + }); + } + // Check if user input is one level up (subdomain.baseDomain) + else if (userInputLower.endsWith(`.${baseDomainLower}`)) { + const subdomain = userInputLower.slice( + 0, + -(baseDomainLower.length + 1) + ); + // Only allow one level up (no dots in subdomain) + if (!subdomain.includes('.')) { + options.push({ + id: `org-${orgDomain.domainId}`, + domain: userInput, + type: "organization", + verified: orgDomain.verified, + domainType: "wildcard", + domainId: orgDomain.domainId, + subdomain: subdomain + }); + } + } } }); @@ -236,6 +271,16 @@ export default function DomainPicker({ fullDomain: option.domain, baseDomain: option.domain }); + } else if (option.domainType === "wildcard") { + onDomainChange?.({ + domainId: option.domainId!, + type: "organization", + subdomain: option.subdomain || undefined, + fullDomain: option.domain, + baseDomain: option.subdomain + ? option.domain.split('.').slice(1).join('.') + : option.domain + }); } } else if (option.type === "provided") { // Extract subdomain from full domain