template for Domain Settings

This commit is contained in:
Pallavi Kumari
2025-10-16 23:37:55 +05:30
parent 43f907ebec
commit a9b9161c40
2 changed files with 238 additions and 36 deletions

View File

@@ -1898,6 +1898,7 @@
"enterCustomResolver": "Enter Custom Resolver",
"preferWildcardCert": "Prefer Wildcard Certificate",
"unverified": "Unverified",
"domainSetting": "DomainSetting",
"domainSettingDescription": "Configure settings for your domain"
"domainSetting": "Domain Settings",
"domainSettingDescription": "Configure settings for your domain",
"preferWildcardCertDescription": "Attempt to generate a wildcard certificate (require a properly configured certificate resolver)."
}

View File

@@ -11,49 +11,250 @@ import {
import { useTranslations } from "next-intl";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useDomainContext } from "@app/hooks/useDomainContext";
import { SettingsContainer, SettingsSection, SettingsSectionBody, SettingsSectionDescription, SettingsSectionFooter, SettingsSectionForm, SettingsSectionHeader, SettingsSectionTitle } from "./Settings";
import { Button } from "./ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
FormDescription
} from "@app/components/ui/form";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
import { Input } from "./ui/input";
import { CheckboxWithLabel } from "./ui/checkbox";
import { useForm } from "react-hook-form";
import z from "zod";
import { toASCII } from "punycode";
import { zodResolver } from "@hookform/resolvers/zod";
import { build } from "@server/build";
import { Switch } from "./ui/switch";
type DomainInfoCardProps = {};
// Helper functions for Unicode domain handling
function toPunycode(domain: string): string {
try {
const parts = toASCII(domain);
return parts;
} catch (error) {
return domain.toLowerCase();
}
}
function isValidDomainFormat(domain: string): boolean {
const unicodeRegex = /^(?!:\/\/)([^\s.]+\.)*[^\s.]+$/;
if (!unicodeRegex.test(domain)) {
return false;
}
const parts = domain.split('.');
for (const part of parts) {
if (part.length === 0 || part.startsWith('-') || part.endsWith('-')) {
return false;
}
if (part.length > 63) {
return false;
}
}
if (domain.length > 253) {
return false;
}
return true;
}
const formSchema = z.object({
baseDomain: z
.string()
.min(1, "Domain is required")
.refine((val) => isValidDomainFormat(val), "Invalid domain format")
.transform((val) => toPunycode(val)),
type: z.enum(["ns", "cname", "wildcard"]),
certResolver: z.string().nullable().optional(),
preferWildcardCert: z.boolean().optional()
});
type FormValues = z.infer<typeof formSchema>;
const certResolverOptions = [
{ id: "default", title: "Default" },
{ id: "custom", title: "Custom Resolver" }
];
export default function DomainInfoCard({ }: DomainInfoCardProps) {
const { domain, updateDomain } = useDomainContext();
const t = useTranslations();
const { env } = useEnvContext();
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
baseDomain: "",
type: build == "oss" || !env.flags.usePangolinDns ? "wildcard" : "ns",
certResolver: null,
preferWildcardCert: false
}
});
return (
<Alert>
<AlertDescription>
<InfoSections cols={env.flags.enableClients ? 3 : 2}>
<InfoSection>
<InfoSectionTitle>
{t("type")}
</InfoSectionTitle>
<InfoSectionContent>
<span>
{domain.type}
</span>
</InfoSectionContent>
</InfoSection>
<InfoSection>
<InfoSectionTitle>
{t("status")}
</InfoSectionTitle>
<InfoSectionContent>
{domain.verified ? (
<div className="text-green-500 flex items-center space-x-2">
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
<span>{t("verified")}</span>
</div>
) : (
<div className="text-neutral-500 flex items-center space-x-2">
<div className="w-2 h-2 bg-gray-500 rounded-full"></div>
<span>{t("unverified")}</span>
</div>
)}
</InfoSectionContent>
</InfoSection>
</InfoSections>
</AlertDescription>
</Alert>
<>
<Alert>
<AlertDescription>
<InfoSections cols={env.flags.enableClients ? 3 : 2}>
<InfoSection>
<InfoSectionTitle>
{t("type")}
</InfoSectionTitle>
<InfoSectionContent>
<span>
{domain.type}
</span>
</InfoSectionContent>
</InfoSection>
<InfoSection>
<InfoSectionTitle>
{t("status")}
</InfoSectionTitle>
<InfoSectionContent>
{domain.verified ? (
<div className="text-green-500 flex items-center space-x-2">
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
<span>{t("verified")}</span>
</div>
) : (
<div className="text-neutral-500 flex items-center space-x-2">
<div className="w-2 h-2 bg-gray-500 rounded-full"></div>
<span>{t("unverified")}</span>
</div>
)}
</InfoSectionContent>
</InfoSection>
</InfoSections>
</AlertDescription>
</Alert>
{/* Domain Settings */}
{/* Add condition later to only show when domain is wildcard */}
<SettingsContainer>
<SettingsSection>
<SettingsSectionHeader>
<SettingsSectionTitle>
{t("domainSetting")}
</SettingsSectionTitle>
</SettingsSectionHeader>
<SettingsSectionBody>
<SettingsSectionForm>
<Form {...form}>
<form
//onSubmit={form.handleSubmit(onSubmit)}
className="space-y-4"
id="create-domain-form"
>
<FormField
control={form.control}
name="certResolver"
render={({ field }) => (
<FormItem>
<FormLabel>{t("certResolver")}</FormLabel>
<FormControl>
<Select
value={
field.value === null ? "default" :
(field.value === "" || (field.value && field.value !== "default")) ? "custom" :
"default"
}
onValueChange={(val) => {
if (val === "default") {
field.onChange(null);
} else if (val === "custom") {
field.onChange("");
} else {
field.onChange(val);
}
}}
>
<SelectTrigger>
<SelectValue placeholder={t("selectCertResolver")} />
</SelectTrigger>
<SelectContent>
{certResolverOptions.map((opt) => (
<SelectItem key={opt.id} value={opt.id}>
{opt.title}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
<FormMessage />
{field.value !== null && field.value !== "default" && (
<div className="space-y-2 mt-2">
<FormControl>
<Input
placeholder={t("enterCustomResolver")}
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
/>
</FormControl>
<FormField
control={form.control}
name="preferWildcardCert"
render={({ field: switchField }) => (
<FormItem className="items-center space-y-2 mt-4">
<FormControl>
<div className="flex items-center space-x-2">
<Switch
defaultChecked={switchField.value}
onCheckedChange={switchField.onChange}
/>
<FormLabel>{t("preferWildcardCert")}</FormLabel>
</div>
</FormControl>
<FormDescription>
{t("preferWildcardCertDescription")}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
</FormItem>
)}
/>
</form>
</Form>
</SettingsSectionForm>
</SettingsSectionBody>
<SettingsSectionFooter>
<Button
type="submit"
// onClick={() => {
// console.log(form.getValues());
// }}
// loading={saveLoading}
// disabled={saveLoading}
form="general-settings-form"
>
{t("saveSettings")}
</Button>
</SettingsSectionFooter>
</SettingsSection>
</SettingsContainer >
</>
);
}