Skip to content

Commit 745cf9d

Browse files
authored
Merge pull request #3374 from david-dev-de/feat/middleware-configuration
feat: add configurable middlewares for domains
2 parents 791ca65 + 70c6119 commit 745cf9d

File tree

12 files changed

+8654
-27
lines changed

12 files changed

+8654
-27
lines changed

apps/dokploy/__test__/compose/domain/host-rule-format.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe("Host rule format regression tests", () => {
3333
internalPath: "/",
3434
stripPath: false,
3535
customEntrypoint: null,
36+
middlewares: null,
3637
};
3738

3839
describe("Host rule format validation", () => {

apps/dokploy/__test__/compose/domain/labels.test.ts

Lines changed: 131 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ describe("createDomainLabels", () => {
2222
previewDeploymentId: "",
2323
internalPath: "/",
2424
stripPath: false,
25+
middlewares: null,
2526
};
2627

2728
it("should create basic labels for web entrypoint", async () => {
@@ -172,12 +173,12 @@ describe("createDomainLabels", () => {
172173
"websecure",
173174
);
174175

175-
// Web entrypoint should have both middlewares with redirect first
176+
// Web entrypoint with HTTPS should only have redirect
176177
expect(webLabels).toContain(
177-
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,addprefix-test-app-1",
178+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
178179
);
179180

180-
// Websecure should only have the addprefix middleware
181+
// Websecure should have the addprefix middleware
181182
expect(websecureLabels).toContain(
182183
"traefik.http.routers.test-app-1-websecure.middlewares=addprefix-test-app-1",
183184
);
@@ -209,9 +210,9 @@ describe("createDomainLabels", () => {
209210
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
210211
);
211212

212-
// Should have middlewares in correct order: redirect, stripprefix, addprefix
213+
// Web router with HTTPS should only have redirect
213214
expect(webLabels).toContain(
214-
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,stripprefix-test-app-1,addprefix-test-app-1",
215+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
215216
);
216217
});
217218

@@ -242,6 +243,131 @@ describe("createDomainLabels", () => {
242243
);
243244
});
244245

246+
it("should add single custom middleware to router", async () => {
247+
const customMiddlewareDomain = {
248+
...baseDomain,
249+
middlewares: ["auth@file"],
250+
};
251+
const labels = await createDomainLabels(
252+
appName,
253+
customMiddlewareDomain,
254+
"web",
255+
);
256+
257+
expect(labels).toContain(
258+
"traefik.http.routers.test-app-1-web.middlewares=auth@file",
259+
);
260+
});
261+
262+
it("should add multiple custom middlewares to router", async () => {
263+
const customMiddlewareDomain = {
264+
...baseDomain,
265+
middlewares: ["auth@file", "rate-limit@file"],
266+
};
267+
const labels = await createDomainLabels(
268+
appName,
269+
customMiddlewareDomain,
270+
"web",
271+
);
272+
273+
expect(labels).toContain(
274+
"traefik.http.routers.test-app-1-web.middlewares=auth@file,rate-limit@file",
275+
);
276+
});
277+
278+
it("should only have redirect on web router when HTTPS is enabled with custom middlewares", async () => {
279+
const combinedDomain = {
280+
...baseDomain,
281+
https: true,
282+
middlewares: ["auth@file"],
283+
};
284+
const labels = await createDomainLabels(appName, combinedDomain, "web");
285+
286+
// Web router with HTTPS should only redirect, custom middlewares go on websecure
287+
expect(labels).toContain(
288+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
289+
);
290+
expect(labels).not.toContain("auth@file");
291+
});
292+
293+
it("should combine custom middlewares with stripPath middleware (no HTTPS)", async () => {
294+
const combinedDomain = {
295+
...baseDomain,
296+
path: "/api",
297+
stripPath: true,
298+
middlewares: ["auth@file"],
299+
};
300+
const labels = await createDomainLabels(appName, combinedDomain, "web");
301+
302+
// stripprefix should come before custom middleware
303+
expect(labels).toContain(
304+
"traefik.http.routers.test-app-1-web.middlewares=stripprefix-test-app-1,auth@file",
305+
);
306+
});
307+
308+
it("should only have redirect on web router even with all built-in middlewares and custom middlewares", async () => {
309+
const fullDomain = {
310+
...baseDomain,
311+
https: true,
312+
path: "/api",
313+
stripPath: true,
314+
internalPath: "/hello",
315+
middlewares: ["auth@file", "rate-limit@file"],
316+
};
317+
const webLabels = await createDomainLabels(appName, fullDomain, "web");
318+
319+
// Web router with HTTPS should only redirect
320+
expect(webLabels).toContain(
321+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
322+
);
323+
// Middleware definitions should still be present (Traefik needs them registered)
324+
expect(webLabels).toContain(
325+
"traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api",
326+
);
327+
expect(webLabels).toContain(
328+
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
329+
);
330+
// But they should NOT be attached to the router
331+
expect(webLabels).not.toContain("stripprefix-test-app-1,");
332+
expect(webLabels).not.toContain("auth@file");
333+
expect(webLabels).not.toContain("rate-limit@file");
334+
});
335+
336+
it("should include custom middlewares on websecure entrypoint", async () => {
337+
const customMiddlewareDomain = {
338+
...baseDomain,
339+
https: true,
340+
middlewares: ["auth@file"],
341+
};
342+
const websecureLabels = await createDomainLabels(
343+
appName,
344+
customMiddlewareDomain,
345+
"websecure",
346+
);
347+
348+
// Websecure should have custom middleware but not redirect-to-https
349+
expect(websecureLabels).toContain(
350+
"traefik.http.routers.test-app-1-websecure.middlewares=auth@file",
351+
);
352+
expect(websecureLabels).not.toContain("redirect-to-https");
353+
});
354+
355+
it("should NOT include custom middlewares on web router when HTTPS is enabled (only redirect)", async () => {
356+
const domain = {
357+
...baseDomain,
358+
https: true,
359+
middlewares: ["rate-limit@file", "auth@file"],
360+
};
361+
const webLabels = await createDomainLabels(appName, domain, "web");
362+
363+
// Web router with HTTPS should ONLY have redirect, not custom middlewares
364+
expect(webLabels).toContain(
365+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
366+
);
367+
expect(webLabels).not.toContain("rate-limit@file");
368+
expect(webLabels).not.toContain("auth@file");
369+
});
370+
245371
it("should create basic labels for custom entrypoint", async () => {
246372
const labels = await createDomainLabels(
247373
appName,

apps/dokploy/__test__/traefik/traefik.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ const baseDomain: Domain = {
146146
previewDeploymentId: "",
147147
internalPath: "/",
148148
stripPath: false,
149+
middlewares: null,
149150
};
150151

151152
const baseRedirect: Redirect = {
@@ -265,6 +266,80 @@ test("Websecure entrypoint on https domain with redirect", async () => {
265266
expect(router.middlewares).toContain("redirect-test-1");
266267
});
267268

269+
/** Custom Middlewares */
270+
271+
test("Web entrypoint with single custom middleware", async () => {
272+
const router = await createRouterConfig(
273+
baseApp,
274+
{ ...baseDomain, middlewares: ["auth@file"] },
275+
"web",
276+
);
277+
278+
expect(router.middlewares).toContain("auth@file");
279+
});
280+
281+
test("Web entrypoint with multiple custom middlewares", async () => {
282+
const router = await createRouterConfig(
283+
baseApp,
284+
{ ...baseDomain, middlewares: ["auth@file", "rate-limit@file"] },
285+
"web",
286+
);
287+
288+
expect(router.middlewares).toContain("auth@file");
289+
expect(router.middlewares).toContain("rate-limit@file");
290+
});
291+
292+
test("Web entrypoint on https domain with custom middleware", async () => {
293+
const router = await createRouterConfig(
294+
baseApp,
295+
{ ...baseDomain, https: true, middlewares: ["auth@file"] },
296+
"web",
297+
);
298+
299+
// Should only have HTTPS redirect - custom middleware applies on websecure
300+
expect(router.middlewares).toContain("redirect-to-https");
301+
expect(router.middlewares).not.toContain("auth@file");
302+
});
303+
304+
test("Websecure entrypoint with custom middleware", async () => {
305+
const router = await createRouterConfig(
306+
baseApp,
307+
{ ...baseDomain, https: true, middlewares: ["auth@file"] },
308+
"websecure",
309+
);
310+
311+
// Should have custom middleware but not HTTPS redirect
312+
expect(router.middlewares).not.toContain("redirect-to-https");
313+
expect(router.middlewares).toContain("auth@file");
314+
});
315+
316+
test("Web entrypoint with redirect and custom middleware", async () => {
317+
const router = await createRouterConfig(
318+
{
319+
...baseApp,
320+
appName: "test",
321+
redirects: [{ ...baseRedirect, uniqueConfigKey: 1 }],
322+
},
323+
{ ...baseDomain, middlewares: ["auth@file"] },
324+
"web",
325+
);
326+
327+
// Should have both redirect middleware and custom middleware
328+
expect(router.middlewares).toContain("redirect-test-1");
329+
expect(router.middlewares).toContain("auth@file");
330+
});
331+
332+
test("Web entrypoint with empty middlewares array", async () => {
333+
const router = await createRouterConfig(
334+
baseApp,
335+
{ ...baseDomain, https: false, middlewares: [] },
336+
"web",
337+
);
338+
339+
// Should behave same as no middlewares - no redirect for http
340+
expect(router.middlewares).not.toContain("redirect-to-https");
341+
});
342+
268343
/** Certificates */
269344

270345
test("CertificateType on websecure entrypoint", async () => {

0 commit comments

Comments
 (0)