allow multi level sudomains in domain picker

This commit is contained in:
miloschwartz
2025-07-18 15:48:23 -07:00
parent 7a59e3acf7
commit 2ddb4ec905
7 changed files with 53 additions and 71 deletions

View File

@@ -1162,7 +1162,7 @@
"selectDomainTypeCnameName": "Single Domain (CNAME)", "selectDomainTypeCnameName": "Single Domain (CNAME)",
"selectDomainTypeCnameDescription": "Just this specific domain. Use this for individual subdomains or specific domain entries.", "selectDomainTypeCnameDescription": "Just this specific domain. Use this for individual subdomains or specific domain entries.",
"selectDomainTypeWildcardName": "Wildcard Domain", "selectDomainTypeWildcardName": "Wildcard Domain",
"selectDomainTypeWildcardDescription": "This domain and its first level of subdomains.", "selectDomainTypeWildcardDescription": "This domain and its subdomains.",
"domainDelegation": "Single Domain", "domainDelegation": "Single Domain",
"selectType": "Select a type", "selectType": "Select a type",
"actions": "Actions", "actions": "Actions",

View File

@@ -620,8 +620,6 @@ authenticated.post(
authenticated.delete("/idp/:idpId", verifyUserIsServerAdmin, idp.deleteIdp); authenticated.delete("/idp/:idpId", verifyUserIsServerAdmin, idp.deleteIdp);
authenticated.get("/idp", verifyUserIsServerAdmin, idp.listIdps);
authenticated.get("/idp/:idpId", verifyUserIsServerAdmin, idp.getIdp); authenticated.get("/idp/:idpId", verifyUserIsServerAdmin, idp.getIdp);
authenticated.put( authenticated.put(

View File

@@ -261,14 +261,6 @@ async function createHttpResource(
) )
); );
} }
if (parsedSubdomain.data.includes(".")) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Subdomain cannot contain a dot when using wildcard domains"
)
);
}
fullDomain = `${subdomain}.${domainRes.domains.baseDomain}`; fullDomain = `${subdomain}.${domainRes.domains.baseDomain}`;
} else { } else {
fullDomain = domainRes.domains.baseDomain; fullDomain = domainRes.domains.baseDomain;

View File

@@ -297,14 +297,6 @@ async function updateHttpResource(
) )
); );
} }
if (parsedSubdomain.data.includes(".")) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Subdomain cannot contain a dot when using wildcard domains"
)
);
}
fullDomain = `${updateData.subdomain}.${domainRes.domains.baseDomain}`; fullDomain = `${updateData.subdomain}.${domainRes.domains.baseDomain}`;
} else { } else {
fullDomain = domainRes.domains.baseDomain; fullDomain = domainRes.domains.baseDomain;

View File

@@ -617,7 +617,7 @@ export default function Page() {
idp || null idp || null
); );
}} }}
cols={3} cols={2}
/> />
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@@ -179,7 +179,7 @@ export default function DomainPicker({
}); });
} }
} else if (orgDomain.type === "wildcard") { } else if (orgDomain.type === "wildcard") {
// For wildcard domains, allow the base domain or one level up // For wildcard domains, allow the base domain or multiple levels up
const userInputLower = userInput.toLowerCase(); const userInputLower = userInput.toLowerCase();
const baseDomainLower = orgDomain.baseDomain.toLowerCase(); const baseDomainLower = orgDomain.baseDomain.toLowerCase();
@@ -194,24 +194,22 @@ export default function DomainPicker({
domainId: orgDomain.domainId domainId: orgDomain.domainId
}); });
} }
// Check if user input is one level up (subdomain.baseDomain) // Check if user input ends with the base domain (allows multiple level subdomains)
else if (userInputLower.endsWith(`.${baseDomainLower}`)) { else if (userInputLower.endsWith(`.${baseDomainLower}`)) {
const subdomain = userInputLower.slice( const subdomain = userInputLower.slice(
0, 0,
-(baseDomainLower.length + 1) -(baseDomainLower.length + 1)
); );
// Only allow one level up (no dots in subdomain) // Allow multiple levels (subdomain can contain dots)
if (!subdomain.includes(".")) { options.push({
options.push({ id: `org-${orgDomain.domainId}`,
id: `org-${orgDomain.domainId}`, domain: userInput,
domain: userInput, type: "organization",
type: "organization", verified: orgDomain.verified,
verified: orgDomain.verified, domainType: "wildcard",
domainType: "wildcard", domainId: orgDomain.domainId,
domainId: orgDomain.domainId, subdomain: subdomain
subdomain: subdomain });
});
}
} }
} }
}); });
@@ -320,7 +318,7 @@ export default function DomainPicker({
setUserInput(validInput); setUserInput(validInput);
}} }}
/> />
<p className="text-xs text-muted-foreground"> <p className="text-sm text-muted-foreground">
{build === "saas" {build === "saas"
? t("domainPickerDescriptionSaas") ? t("domainPickerDescriptionSaas")
: t("domainPickerDescription")} : t("domainPickerDescription")}
@@ -328,42 +326,44 @@ export default function DomainPicker({
</div> </div>
{/* Tabs and Sort Toggle */} {/* Tabs and Sort Toggle */}
<div className="flex justify-between items-center"> {build === "saas" && (
<Tabs <div className="flex justify-between items-center">
value={activeTab} <Tabs
onValueChange={(value) => value={activeTab}
setActiveTab( onValueChange={(value) =>
value as "all" | "organization" | "provided" setActiveTab(
) value as "all" | "organization" | "provided"
} )
> }
<TabsList> >
<TabsTrigger value="all"> <TabsList>
{t("domainPickerTabAll")} <TabsTrigger value="all">
</TabsTrigger> {t("domainPickerTabAll")}
<TabsTrigger value="organization">
{t("domainPickerTabOrganization")}
</TabsTrigger>
{build == "saas" && (
<TabsTrigger value="provided">
{t("domainPickerTabProvided")}
</TabsTrigger> </TabsTrigger>
)} <TabsTrigger value="organization">
</TabsList> {t("domainPickerTabOrganization")}
</Tabs> </TabsTrigger>
<Button {build == "saas" && (
variant="outline" <TabsTrigger value="provided">
size="sm" {t("domainPickerTabProvided")}
onClick={() => </TabsTrigger>
setSortOrder(sortOrder === "asc" ? "desc" : "asc") )}
} </TabsList>
> </Tabs>
<ArrowUpDown className="h-4 w-4 mr-2" /> <Button
{sortOrder === "asc" variant="outline"
? t("domainPickerSortAsc") size="sm"
: t("domainPickerSortDesc")} onClick={() =>
</Button> setSortOrder(sortOrder === "asc" ? "desc" : "asc")
</div> }
>
<ArrowUpDown className="h-4 w-4 mr-2" />
{sortOrder === "asc"
? t("domainPickerSortAsc")
: t("domainPickerSortDesc")}
</Button>
</div>
)}
{/* Loading State */} {/* Loading State */}
{isChecking && ( {isChecking && (

View File

@@ -18,7 +18,7 @@ function PopoverTrigger({
function PopoverContent({ function PopoverContent({
className, className,
align = "center", align = "start",
sideOffset = 4, sideOffset = 4,
...props ...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) { }: React.ComponentProps<typeof PopoverPrimitive.Content>) {