diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts index c5f45810f..172bff2af 100644 --- a/apps/dokploy/__test__/compose/domain/labels.test.ts +++ b/apps/dokploy/__test__/compose/domain/labels.test.ts @@ -19,6 +19,8 @@ describe("createDomainLabels", () => { path: "/", createdAt: "", previewDeploymentId: "", + internalPath: "/", + stripPath: false, }; it("should create basic labels for web entrypoint", async () => { diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 5cd033af5..f2d0f0a50 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -119,6 +119,8 @@ const baseDomain: Domain = { domainType: "application", uniqueConfigKey: 1, previewDeploymentId: "", + internalPath: "/", + stripPath: false, }; const baseRedirect: Redirect = { diff --git a/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx b/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx index c145afcfc..c8522f5f5 100644 --- a/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx @@ -49,6 +49,8 @@ export const domain = z .object({ host: z.string().min(1, { message: "Add a hostname" }), path: z.string().min(1).optional(), + internalPath: z.string().optional(), + stripPath: z.boolean().optional(), port: z .number() .min(1, { message: "Port must be at least 1" }) @@ -84,6 +86,29 @@ export const domain = z message: "Required", }); } + + // Validate stripPath requires a valid path + if (input.stripPath && (!input.path || input.path === "/")) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["stripPath"], + message: + "Strip path can only be enabled when a path other than '/' is specified", + }); + } + + // Validate internalPath starts with / + if ( + input.internalPath && + input.internalPath !== "/" && + !input.internalPath.startsWith("/") + ) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["internalPath"], + message: "Internal path must start with '/'", + }); + } }); type Domain = z.infer; @@ -162,6 +187,8 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => { defaultValues: { host: "", path: undefined, + internalPath: undefined, + stripPath: false, port: undefined, https: false, certificateType: undefined, @@ -182,6 +209,8 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => { ...data, /* Convert null to undefined */ path: data?.path || undefined, + internalPath: data?.internalPath || undefined, + stripPath: data?.stripPath || false, port: data?.port || undefined, certificateType: data?.certificateType || undefined, customCertResolver: data?.customCertResolver || undefined, @@ -194,6 +223,8 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => { form.reset({ host: "", path: undefined, + internalPath: undefined, + stripPath: false, port: undefined, https: false, certificateType: undefined, @@ -469,6 +500,49 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => { }} /> + { + return ( + + Internal Path + + The path where your application expects to receive + requests internally (defaults to "/") + + + + + + + ); + }} + /> + + ( + +
+ Strip Path + + Remove the external path from the request before + forwarding to the application + + +
+ + + +
+ )} + /> +