diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts index 8bc9fbcc..c5f45810 100644 --- a/apps/dokploy/__test__/compose/domain/labels.test.ts +++ b/apps/dokploy/__test__/compose/domain/labels.test.ts @@ -9,6 +9,7 @@ describe("createDomainLabels", () => { port: 8080, https: false, uniqueConfigKey: 1, + customCertResolver: null, certificateType: "none", applicationId: "", composeId: "", diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.test.ts index 4e6f20d3..4ca78b36 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.test.ts @@ -37,6 +37,7 @@ const baseApp: ApplicationNested = { isPreviewDeploymentsActive: false, previewBuildArgs: null, previewCertificateType: "none", + previewCustomCertResolver: null, previewEnv: null, previewHttps: false, previewPath: "/", diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 955103de..df20e163 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -23,6 +23,7 @@ const baseApp: ApplicationNested = { previewPath: "/", previewPort: 3000, previewLimit: 0, + previewCustomCertResolver: null, previewWildcard: "", project: { env: "", @@ -103,6 +104,7 @@ const baseDomain: Domain = { port: null, serviceName: "", composeId: "", + customCertResolver: null, domainType: "application", uniqueConfigKey: 1, previewDeploymentId: "", diff --git a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx index 61168943..f91218ce 100644 --- a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx @@ -85,8 +85,20 @@ export const AddDomain = ({ const form = useForm({ resolver: zodResolver(domain), + defaultValues: { + host: "", + path: undefined, + port: undefined, + https: false, + certificateType: undefined, + customCertResolver: undefined, + }, + mode: "onChange", }); + const certificateType = form.watch("certificateType"); + const https = form.watch("https"); + useEffect(() => { if (data) { form.reset({ @@ -94,13 +106,29 @@ export const AddDomain = ({ /* Convert null to undefined */ path: data?.path || undefined, port: data?.port || undefined, + certificateType: data?.certificateType || undefined, + customCertResolver: data?.customCertResolver || undefined, }); } if (!domainId) { - form.reset({}); + form.reset({ + host: "", + path: undefined, + port: undefined, + https: false, + certificateType: undefined, + customCertResolver: undefined, + }); } - }, [form, form.reset, data, isLoading]); + }, [form, data, isLoading, domainId]); + + // Separate effect for handling custom cert resolver validation + useEffect(() => { + if (certificateType === "custom") { + form.trigger("customCertResolver"); + } + }, [certificateType, form]); const dictionary = { success: domainId ? "Domain Updated" : "Domain Created", @@ -256,34 +284,73 @@ export const AddDomain = ({ )} /> - {form.getValues().https && ( - ( - - Certificate Provider - { + field.onChange(value); + if (value !== "custom") { + form.setValue( + "customCertResolver", + undefined, + ); + } + }} + value={field.value} + > + + + + + + + None + + Let's Encrypt + + Custom + + + + + ); + }} + /> - - None - - Let's Encrypt - - - - - + {certificateType === "custom" && ( + { + return ( + + Custom Certificate Resolver + + { + field.onChange(e); + form.trigger("customCertResolver"); + }} + /> + + + + ); + }} + /> )} - /> + )} diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx index 64b7c3c6..78cd55d7 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx @@ -94,6 +94,7 @@ export const AddPreviewDomain = ({ /* Convert null to undefined */ path: data?.path || undefined, port: data?.port || undefined, + customCertResolver: data?.customCertResolver || undefined, }); } diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index 9d53f31d..bfc6ad2e 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -35,16 +35,30 @@ import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -const schema = z.object({ - env: z.string(), - buildArgs: z.string(), - wildcardDomain: z.string(), - port: z.number(), - previewLimit: z.number(), - previewHttps: z.boolean(), - previewPath: z.string(), - previewCertificateType: z.enum(["letsencrypt", "none"]), -}); +const schema = z + .object({ + env: z.string(), + buildArgs: z.string(), + wildcardDomain: z.string(), + port: z.number(), + previewLimit: z.number(), + previewHttps: z.boolean(), + previewPath: z.string(), + previewCertificateType: z.enum(["letsencrypt", "none", "custom"]), + previewCustomCertResolver: z.string().optional(), + }) + .superRefine((input, ctx) => { + if ( + input.previewCertificateType === "custom" && + !input.previewCustomCertResolver + ) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["previewCustomCertResolver"], + message: "Required", + }); + } + }); type Schema = z.infer; @@ -90,6 +104,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { previewHttps: data.previewHttps || false, previewPath: data.previewPath || "/", previewCertificateType: data.previewCertificateType || "none", + previewCustomCertResolver: data.previewCustomCertResolver || "", }); } }, [data]); @@ -105,6 +120,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { previewHttps: formData.previewHttps, previewPath: formData.previewPath, previewCertificateType: formData.previewCertificateType, + previewCustomCertResolver: formData.previewCustomCertResolver, }) .then(() => { toast.success("Preview Deployments settings updated"); @@ -184,10 +200,6 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { render={({ field }) => ( Preview Limit - {/* - Set the limit of preview deployments that can be - created for this app. - */} @@ -238,6 +250,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { Let's Encrypt + Custom @@ -245,6 +258,25 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { )} /> )} + + {form.watch("previewCertificateType") === "custom" && ( + ( + + Certificate Provider + + + + + + )} + /> + )}
diff --git a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx index e18d40d7..9b412c83 100644 --- a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx @@ -104,6 +104,15 @@ export const AddDomainCompose = ({ const form = useForm({ resolver: zodResolver(domainCompose), + defaultValues: { + host: "", + path: undefined, + port: undefined, + https: false, + certificateType: undefined, + customCertResolver: undefined, + serviceName: "", + }, }); const https = form.watch("https"); @@ -116,11 +125,21 @@ export const AddDomainCompose = ({ path: data?.path || undefined, port: data?.port || undefined, serviceName: data?.serviceName || undefined, + certificateType: data?.certificateType || undefined, + customCertResolver: data?.customCertResolver || undefined, }); } if (!domainId) { - form.reset({}); + form.reset({ + host: "", + path: undefined, + port: undefined, + https: false, + certificateType: undefined, + customCertResolver: undefined, + serviceName: "", + }); } }, [form, form.reset, data, isLoading]); @@ -393,33 +412,55 @@ export const AddDomainCompose = ({ /> {https && ( - ( - - Certificate Provider - + + + + + - - None - - Let's Encrypt - - - - - + + None + + Let's Encrypt + + Custom + + + + + )} + /> + + {form.getValues().certificateType === "custom" && ( + ( + + Custom Certificate Resolver + + + + + + )} + /> )} - /> + )}
diff --git a/apps/dokploy/components/dashboard/settings/web-domain.tsx b/apps/dokploy/components/dashboard/settings/web-domain.tsx index 3b3f70ba..a579df39 100644 --- a/apps/dokploy/components/dashboard/settings/web-domain.tsx +++ b/apps/dokploy/components/dashboard/settings/web-domain.tsx @@ -35,7 +35,7 @@ const addServerDomain = z .object({ domain: z.string().min(1, { message: "URL is required" }), letsEncryptEmail: z.string(), - certificateType: z.enum(["letsencrypt", "none"]), + certificateType: z.enum(["letsencrypt", "none", "custom"]), }) .superRefine((data, ctx) => { if (data.certificateType === "letsencrypt" && !data.letsEncryptEmail) { @@ -193,6 +193,7 @@ export const WebDomain = () => { ); }} /> +