diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts index 172bff2af..9a75e0a84 100644 --- a/apps/dokploy/__test__/compose/domain/labels.test.ts +++ b/apps/dokploy/__test__/compose/domain/labels.test.ts @@ -108,4 +108,136 @@ describe("createDomainLabels", () => { "traefik.http.services.test-app-1-web.loadbalancer.server.port=3000", ); }); + + it("should add stripPath middleware when stripPath is enabled", async () => { + const stripPathDomain = { + ...baseDomain, + path: "/api", + stripPath: true, + }; + const labels = await createDomainLabels(appName, stripPathDomain, "web"); + + expect(labels).toContain( + "traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api", + ); + expect(labels).toContain( + "traefik.http.routers.test-app-1-web.middlewares=stripprefix-test-app-1", + ); + }); + + it("should add internalPath middleware when internalPath is set", async () => { + const internalPathDomain = { + ...baseDomain, + internalPath: "/hello", + }; + const webLabels = await createDomainLabels( + appName, + internalPathDomain, + "web", + ); + const websecureLabels = await createDomainLabels( + appName, + internalPathDomain, + "websecure", + ); + + // Middleware definition should only appear in web entrypoint + expect(webLabels).toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + expect(websecureLabels).not.toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + + // Both routers should reference the middleware + expect(webLabels).toContain( + "traefik.http.routers.test-app-1-web.middlewares=addprefix-test-app-1", + ); + expect(websecureLabels).toContain( + "traefik.http.routers.test-app-1-websecure.middlewares=addprefix-test-app-1", + ); + }); + + it("should combine HTTPS redirect with internalPath middleware in correct order", async () => { + const combinedDomain = { + ...baseDomain, + https: true, + internalPath: "/hello", + }; + const webLabels = await createDomainLabels(appName, combinedDomain, "web"); + const websecureLabels = await createDomainLabels( + appName, + combinedDomain, + "websecure", + ); + + // Web entrypoint should have both middlewares with redirect first + expect(webLabels).toContain( + "traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,addprefix-test-app-1", + ); + + // Websecure should only have the addprefix middleware + expect(websecureLabels).toContain( + "traefik.http.routers.test-app-1-websecure.middlewares=addprefix-test-app-1", + ); + + // Middleware definition should only appear once (in web) + expect(webLabels).toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + expect(websecureLabels).not.toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + }); + + it("should combine all middlewares in correct order", async () => { + const fullDomain = { + ...baseDomain, + https: true, + path: "/api", + stripPath: true, + internalPath: "/hello", + }; + const webLabels = await createDomainLabels(appName, fullDomain, "web"); + + // Should have all middleware definitions (only in web) + expect(webLabels).toContain( + "traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api", + ); + expect(webLabels).toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + + // Should have middlewares in correct order: redirect, stripprefix, addprefix + expect(webLabels).toContain( + "traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,stripprefix-test-app-1,addprefix-test-app-1", + ); + }); + + it("should not add middleware definitions for websecure entrypoint", async () => { + const internalPathDomain = { + ...baseDomain, + path: "/api", + stripPath: true, + internalPath: "/hello", + }; + const websecureLabels = await createDomainLabels( + appName, + internalPathDomain, + "websecure", + ); + + // Should not contain any middleware definitions + expect(websecureLabels).not.toContain( + "traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api", + ); + expect(websecureLabels).not.toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + + // But should reference the middlewares + expect(websecureLabels).toContain( + "traefik.http.routers.test-app-1-websecure.middlewares=stripprefix-test-app-1,addprefix-test-app-1", + ); + }); }); diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts index c12083345..0ce138d70 100644 --- a/packages/server/src/utils/docker/domain.ts +++ b/packages/server/src/utils/docker/domain.ts @@ -313,40 +313,46 @@ export const createDomainLabels = ( `traefik.http.routers.${routerName}.service=${routerName}`, ]; - // Validate stripPath - it should only be used when path is defined and not "/" - if (stripPath) { - if (!path || path === "/") { - console.warn( - `stripPath is enabled but path is not defined or is "/" for domain ${host}`, - ); - } else { - const middlewareName = `stripprefix-${appName}-${uniqueConfigKey}`; + // Collect middlewares for this router + const middlewares: string[] = []; + + // Add HTTPS redirect for web entrypoint (must be first) + if (entrypoint === "web" && https) { + middlewares.push("redirect-to-https@file"); + } + + // Add stripPath middleware if needed + if (stripPath && path && path !== "/") { + const middlewareName = `stripprefix-${appName}-${uniqueConfigKey}`; + // Only define middleware once (on web entrypoint) + if (entrypoint === "web") { labels.push( `traefik.http.middlewares.${middlewareName}.stripprefix.prefixes=${path}`, ); } + middlewares.push(middlewareName); } - // Validate internalPath - ensure it's a valid path format - if (internalPath && internalPath !== "/") { - if (!internalPath.startsWith("/")) { - console.warn( - `internalPath "${internalPath}" should start with "/" and not be empty for domain ${host}`, - ); - } else { - const middlewareName = `addprefix-${appName}-${uniqueConfigKey}`; + // Add internalPath middleware if needed + if (internalPath && internalPath !== "/" && internalPath.startsWith("/")) { + const middlewareName = `addprefix-${appName}-${uniqueConfigKey}`; + // Only define middleware once (on web entrypoint) + if (entrypoint === "web") { labels.push( `traefik.http.middlewares.${middlewareName}.addprefix.prefix=${internalPath}`, ); } + middlewares.push(middlewareName); } - if (entrypoint === "web" && https) { + // Apply middlewares to router if any exist + if (middlewares.length > 0) { labels.push( - `traefik.http.routers.${routerName}.middlewares=redirect-to-https@file`, + `traefik.http.routers.${routerName}.middlewares=${middlewares.join(",")}`, ); } + // Add TLS configuration for websecure if (entrypoint === "websecure") { if (certificateType === "letsencrypt") { labels.push(