mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-28 02:25:23 +02:00
Merge pull request #3374 from david-dev-de/feat/middleware-configuration
feat: add configurable middlewares for domains
This commit is contained in:
@@ -33,6 +33,7 @@ describe("Host rule format regression tests", () => {
|
||||
internalPath: "/",
|
||||
stripPath: false,
|
||||
customEntrypoint: null,
|
||||
middlewares: null,
|
||||
};
|
||||
|
||||
describe("Host rule format validation", () => {
|
||||
|
||||
@@ -22,6 +22,7 @@ describe("createDomainLabels", () => {
|
||||
previewDeploymentId: "",
|
||||
internalPath: "/",
|
||||
stripPath: false,
|
||||
middlewares: null,
|
||||
};
|
||||
|
||||
it("should create basic labels for web entrypoint", async () => {
|
||||
@@ -172,12 +173,12 @@ describe("createDomainLabels", () => {
|
||||
"websecure",
|
||||
);
|
||||
|
||||
// Web entrypoint should have both middlewares with redirect first
|
||||
// Web entrypoint with HTTPS should only have redirect
|
||||
expect(webLabels).toContain(
|
||||
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,addprefix-test-app-1",
|
||||
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
|
||||
);
|
||||
|
||||
// Websecure should only have the addprefix middleware
|
||||
// Websecure should have the addprefix middleware
|
||||
expect(websecureLabels).toContain(
|
||||
"traefik.http.routers.test-app-1-websecure.middlewares=addprefix-test-app-1",
|
||||
);
|
||||
@@ -209,9 +210,9 @@ describe("createDomainLabels", () => {
|
||||
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
|
||||
);
|
||||
|
||||
// Should have middlewares in correct order: redirect, stripprefix, addprefix
|
||||
// Web router with HTTPS should only have redirect
|
||||
expect(webLabels).toContain(
|
||||
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,stripprefix-test-app-1,addprefix-test-app-1",
|
||||
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -242,6 +243,131 @@ describe("createDomainLabels", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should add single custom middleware to router", async () => {
|
||||
const customMiddlewareDomain = {
|
||||
...baseDomain,
|
||||
middlewares: ["auth@file"],
|
||||
};
|
||||
const labels = await createDomainLabels(
|
||||
appName,
|
||||
customMiddlewareDomain,
|
||||
"web",
|
||||
);
|
||||
|
||||
expect(labels).toContain(
|
||||
"traefik.http.routers.test-app-1-web.middlewares=auth@file",
|
||||
);
|
||||
});
|
||||
|
||||
it("should add multiple custom middlewares to router", async () => {
|
||||
const customMiddlewareDomain = {
|
||||
...baseDomain,
|
||||
middlewares: ["auth@file", "rate-limit@file"],
|
||||
};
|
||||
const labels = await createDomainLabels(
|
||||
appName,
|
||||
customMiddlewareDomain,
|
||||
"web",
|
||||
);
|
||||
|
||||
expect(labels).toContain(
|
||||
"traefik.http.routers.test-app-1-web.middlewares=auth@file,rate-limit@file",
|
||||
);
|
||||
});
|
||||
|
||||
it("should only have redirect on web router when HTTPS is enabled with custom middlewares", async () => {
|
||||
const combinedDomain = {
|
||||
...baseDomain,
|
||||
https: true,
|
||||
middlewares: ["auth@file"],
|
||||
};
|
||||
const labels = await createDomainLabels(appName, combinedDomain, "web");
|
||||
|
||||
// Web router with HTTPS should only redirect, custom middlewares go on websecure
|
||||
expect(labels).toContain(
|
||||
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
|
||||
);
|
||||
expect(labels).not.toContain("auth@file");
|
||||
});
|
||||
|
||||
it("should combine custom middlewares with stripPath middleware (no HTTPS)", async () => {
|
||||
const combinedDomain = {
|
||||
...baseDomain,
|
||||
path: "/api",
|
||||
stripPath: true,
|
||||
middlewares: ["auth@file"],
|
||||
};
|
||||
const labels = await createDomainLabels(appName, combinedDomain, "web");
|
||||
|
||||
// stripprefix should come before custom middleware
|
||||
expect(labels).toContain(
|
||||
"traefik.http.routers.test-app-1-web.middlewares=stripprefix-test-app-1,auth@file",
|
||||
);
|
||||
});
|
||||
|
||||
it("should only have redirect on web router even with all built-in middlewares and custom middlewares", async () => {
|
||||
const fullDomain = {
|
||||
...baseDomain,
|
||||
https: true,
|
||||
path: "/api",
|
||||
stripPath: true,
|
||||
internalPath: "/hello",
|
||||
middlewares: ["auth@file", "rate-limit@file"],
|
||||
};
|
||||
const webLabels = await createDomainLabels(appName, fullDomain, "web");
|
||||
|
||||
// Web router with HTTPS should only redirect
|
||||
expect(webLabels).toContain(
|
||||
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
|
||||
);
|
||||
// Middleware definitions should still be present (Traefik needs them registered)
|
||||
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",
|
||||
);
|
||||
// But they should NOT be attached to the router
|
||||
expect(webLabels).not.toContain("stripprefix-test-app-1,");
|
||||
expect(webLabels).not.toContain("auth@file");
|
||||
expect(webLabels).not.toContain("rate-limit@file");
|
||||
});
|
||||
|
||||
it("should include custom middlewares on websecure entrypoint", async () => {
|
||||
const customMiddlewareDomain = {
|
||||
...baseDomain,
|
||||
https: true,
|
||||
middlewares: ["auth@file"],
|
||||
};
|
||||
const websecureLabels = await createDomainLabels(
|
||||
appName,
|
||||
customMiddlewareDomain,
|
||||
"websecure",
|
||||
);
|
||||
|
||||
// Websecure should have custom middleware but not redirect-to-https
|
||||
expect(websecureLabels).toContain(
|
||||
"traefik.http.routers.test-app-1-websecure.middlewares=auth@file",
|
||||
);
|
||||
expect(websecureLabels).not.toContain("redirect-to-https");
|
||||
});
|
||||
|
||||
it("should NOT include custom middlewares on web router when HTTPS is enabled (only redirect)", async () => {
|
||||
const domain = {
|
||||
...baseDomain,
|
||||
https: true,
|
||||
middlewares: ["rate-limit@file", "auth@file"],
|
||||
};
|
||||
const webLabels = await createDomainLabels(appName, domain, "web");
|
||||
|
||||
// Web router with HTTPS should ONLY have redirect, not custom middlewares
|
||||
expect(webLabels).toContain(
|
||||
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
|
||||
);
|
||||
expect(webLabels).not.toContain("rate-limit@file");
|
||||
expect(webLabels).not.toContain("auth@file");
|
||||
});
|
||||
|
||||
it("should create basic labels for custom entrypoint", async () => {
|
||||
const labels = await createDomainLabels(
|
||||
appName,
|
||||
|
||||
@@ -146,6 +146,7 @@ const baseDomain: Domain = {
|
||||
previewDeploymentId: "",
|
||||
internalPath: "/",
|
||||
stripPath: false,
|
||||
middlewares: null,
|
||||
};
|
||||
|
||||
const baseRedirect: Redirect = {
|
||||
@@ -265,6 +266,80 @@ test("Websecure entrypoint on https domain with redirect", async () => {
|
||||
expect(router.middlewares).toContain("redirect-test-1");
|
||||
});
|
||||
|
||||
/** Custom Middlewares */
|
||||
|
||||
test("Web entrypoint with single custom middleware", async () => {
|
||||
const router = await createRouterConfig(
|
||||
baseApp,
|
||||
{ ...baseDomain, middlewares: ["auth@file"] },
|
||||
"web",
|
||||
);
|
||||
|
||||
expect(router.middlewares).toContain("auth@file");
|
||||
});
|
||||
|
||||
test("Web entrypoint with multiple custom middlewares", async () => {
|
||||
const router = await createRouterConfig(
|
||||
baseApp,
|
||||
{ ...baseDomain, middlewares: ["auth@file", "rate-limit@file"] },
|
||||
"web",
|
||||
);
|
||||
|
||||
expect(router.middlewares).toContain("auth@file");
|
||||
expect(router.middlewares).toContain("rate-limit@file");
|
||||
});
|
||||
|
||||
test("Web entrypoint on https domain with custom middleware", async () => {
|
||||
const router = await createRouterConfig(
|
||||
baseApp,
|
||||
{ ...baseDomain, https: true, middlewares: ["auth@file"] },
|
||||
"web",
|
||||
);
|
||||
|
||||
// Should only have HTTPS redirect - custom middleware applies on websecure
|
||||
expect(router.middlewares).toContain("redirect-to-https");
|
||||
expect(router.middlewares).not.toContain("auth@file");
|
||||
});
|
||||
|
||||
test("Websecure entrypoint with custom middleware", async () => {
|
||||
const router = await createRouterConfig(
|
||||
baseApp,
|
||||
{ ...baseDomain, https: true, middlewares: ["auth@file"] },
|
||||
"websecure",
|
||||
);
|
||||
|
||||
// Should have custom middleware but not HTTPS redirect
|
||||
expect(router.middlewares).not.toContain("redirect-to-https");
|
||||
expect(router.middlewares).toContain("auth@file");
|
||||
});
|
||||
|
||||
test("Web entrypoint with redirect and custom middleware", async () => {
|
||||
const router = await createRouterConfig(
|
||||
{
|
||||
...baseApp,
|
||||
appName: "test",
|
||||
redirects: [{ ...baseRedirect, uniqueConfigKey: 1 }],
|
||||
},
|
||||
{ ...baseDomain, middlewares: ["auth@file"] },
|
||||
"web",
|
||||
);
|
||||
|
||||
// Should have both redirect middleware and custom middleware
|
||||
expect(router.middlewares).toContain("redirect-test-1");
|
||||
expect(router.middlewares).toContain("auth@file");
|
||||
});
|
||||
|
||||
test("Web entrypoint with empty middlewares array", async () => {
|
||||
const router = await createRouterConfig(
|
||||
baseApp,
|
||||
{ ...baseDomain, https: false, middlewares: [] },
|
||||
"web",
|
||||
);
|
||||
|
||||
// Should behave same as no middlewares - no redirect for http
|
||||
expect(router.middlewares).not.toContain("redirect-to-https");
|
||||
});
|
||||
|
||||
/** Certificates */
|
||||
|
||||
test("CertificateType on websecure entrypoint", async () => {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema";
|
||||
import { DatabaseZap, Dices, RefreshCw } from "lucide-react";
|
||||
import { DatabaseZap, Dices, RefreshCw, X } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import z from "zod";
|
||||
import { AlertBlock } from "@/components/shared/alert-block";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
@@ -68,6 +69,7 @@ export const domain = z
|
||||
customCertResolver: z.string().optional(),
|
||||
serviceName: z.string().optional(),
|
||||
domainType: z.enum(["application", "compose", "preview"]).optional(),
|
||||
middlewares: z.array(z.string()).optional(),
|
||||
})
|
||||
.superRefine((input, ctx) => {
|
||||
if (input.https && !input.certificateType) {
|
||||
@@ -213,6 +215,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
|
||||
customCertResolver: undefined,
|
||||
serviceName: undefined,
|
||||
domainType: type,
|
||||
middlewares: [],
|
||||
},
|
||||
mode: "onChange",
|
||||
});
|
||||
@@ -239,6 +242,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
|
||||
customCertResolver: data?.customCertResolver || undefined,
|
||||
serviceName: data?.serviceName || undefined,
|
||||
domainType: data?.domainType || type,
|
||||
middlewares: data?.middlewares || [],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -255,6 +259,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
|
||||
certificateType: undefined,
|
||||
customCertResolver: undefined,
|
||||
domainType: type,
|
||||
middlewares: [],
|
||||
});
|
||||
}
|
||||
}, [form, data, isPending, domainId]);
|
||||
@@ -285,6 +290,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
|
||||
composeId: id,
|
||||
}),
|
||||
...data,
|
||||
customEntrypoint: data.useCustomEntrypoint ? data.customEntrypoint : null,
|
||||
})
|
||||
.then(async () => {
|
||||
toast.success(dictionary.success);
|
||||
@@ -669,7 +675,12 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange(checked);
|
||||
if (!checked) {
|
||||
form.setValue("customEntrypoint", undefined);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
@@ -786,6 +797,88 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="middlewares"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex items-center gap-2">
|
||||
<FormLabel>Middlewares</FormLabel>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
||||
?
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-[300px]">
|
||||
<p>
|
||||
Add Traefik middleware references. Middlewares
|
||||
must be defined in your Traefik configuration.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 mb-2">
|
||||
{field.value?.map((name, index) => (
|
||||
<Badge key={index} variant="secondary">
|
||||
{name}
|
||||
<X
|
||||
className="ml-1 size-3 cursor-pointer"
|
||||
onClick={() => {
|
||||
const newMiddlewares = [...(field.value || [])];
|
||||
newMiddlewares.splice(index, 1);
|
||||
form.setValue("middlewares", newMiddlewares);
|
||||
}}
|
||||
/>
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<FormControl>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
placeholder="e.g., rate-limit@file, auth@file"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
const input = e.currentTarget;
|
||||
const value = input.value.trim();
|
||||
if (value && !field.value?.includes(value)) {
|
||||
form.setValue("middlewares", [
|
||||
...(field.value || []),
|
||||
value,
|
||||
]);
|
||||
input.value = "";
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
const input = document.querySelector(
|
||||
'input[placeholder="e.g., rate-limit@file, auth@file"]',
|
||||
) as HTMLInputElement;
|
||||
const value = input.value.trim();
|
||||
if (value && !field.value?.includes(value)) {
|
||||
form.setValue("middlewares", [
|
||||
...(field.value || []),
|
||||
value,
|
||||
]);
|
||||
input.value = "";
|
||||
}
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -560,6 +560,22 @@ export const ShowDomains = ({ id, type }: Props) => {
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
{item.middlewares?.map((middleware, index) => (
|
||||
<TooltipProvider key={`${middleware}-${index}`}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant="secondary">
|
||||
<InfoIcon className="size-3 mr-1" />
|
||||
Middleware: {middleware}
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Traefik middleware reference</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
))}
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
|
||||
1
apps/dokploy/drizzle/0161_solid_newton_destine.sql
Normal file
1
apps/dokploy/drizzle/0161_solid_newton_destine.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "domain" ADD COLUMN "middlewares" text[] DEFAULT ARRAY[]::text[];
|
||||
8285
apps/dokploy/drizzle/meta/0161_snapshot.json
Normal file
8285
apps/dokploy/drizzle/meta/0161_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1128,6 +1128,13 @@
|
||||
"when": 1775316657754,
|
||||
"tag": "0160_burly_odin",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 161,
|
||||
"version": "7",
|
||||
"when": 1775317611429,
|
||||
"tag": "0161_solid_newton_destine",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import {
|
||||
type AnyPgColumn,
|
||||
boolean,
|
||||
@@ -54,6 +54,7 @@ export const domains = pgTable("domain", {
|
||||
certificateType: certificateType("certificateType").notNull().default("none"),
|
||||
internalPath: text("internalPath").default("/"),
|
||||
stripPath: boolean("stripPath").notNull().default(false),
|
||||
middlewares: text("middlewares").array().default(sql`ARRAY[]::text[]`),
|
||||
});
|
||||
|
||||
export const domainsRelations = relations(domains, ({ one }) => ({
|
||||
@@ -92,6 +93,7 @@ export const apiCreateDomain = createSchema.pick({
|
||||
previewDeploymentId: true,
|
||||
internalPath: true,
|
||||
stripPath: true,
|
||||
middlewares: true,
|
||||
});
|
||||
|
||||
export const apiFindDomain = z.object({
|
||||
@@ -123,5 +125,6 @@ export const apiUpdateDomain = createSchema
|
||||
domainType: true,
|
||||
internalPath: true,
|
||||
stripPath: true,
|
||||
middlewares: true,
|
||||
})
|
||||
.merge(createSchema.pick({ domainId: true }).required());
|
||||
|
||||
@@ -20,6 +20,7 @@ export const domain = z
|
||||
https: z.boolean().optional(),
|
||||
certificateType: z.enum(["letsencrypt", "none", "custom"]).optional(),
|
||||
customCertResolver: z.string(),
|
||||
middlewares: z.array(z.string()).optional(),
|
||||
})
|
||||
.superRefine((input, ctx) => {
|
||||
if (input.https && !input.certificateType) {
|
||||
@@ -83,6 +84,7 @@ export const domainCompose = z
|
||||
certificateType: z.enum(["letsencrypt", "none", "custom"]).optional(),
|
||||
customCertResolver: z.string(),
|
||||
serviceName: z.string().min(1, { message: "Service name is required" }),
|
||||
middlewares: z.array(z.string()).optional(),
|
||||
})
|
||||
.superRefine((input, ctx) => {
|
||||
if (input.https && !input.certificateType) {
|
||||
|
||||
@@ -279,34 +279,45 @@ export const createDomainLabels = (
|
||||
|
||||
// Collect middlewares for this router
|
||||
const middlewares: string[] = [];
|
||||
const isRedirectRouter = entrypoint === "web" && https && !customEntrypoint;
|
||||
|
||||
// Add HTTPS redirect for web entrypoint (must be first)
|
||||
if (entrypoint === "web" && https) {
|
||||
// Web router with HTTPS only needs redirect — all other middlewares
|
||||
// run on the websecure router where the request actually lands.
|
||||
if (isRedirectRouter) {
|
||||
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)
|
||||
// Define middleware on web (or custom) entrypoint so Traefik registers it
|
||||
if (entrypoint === "web" || customEntrypoint) {
|
||||
labels.push(
|
||||
`traefik.http.middlewares.${middlewareName}.stripprefix.prefixes=${path}`,
|
||||
);
|
||||
}
|
||||
middlewares.push(middlewareName);
|
||||
if (!isRedirectRouter) {
|
||||
middlewares.push(middlewareName);
|
||||
}
|
||||
}
|
||||
|
||||
// Add internalPath middleware if needed
|
||||
if (internalPath && internalPath !== "/" && internalPath.startsWith("/")) {
|
||||
const middlewareName = `addprefix-${appName}-${uniqueConfigKey}`;
|
||||
// Only define middleware once (on web entrypoint)
|
||||
// Define middleware on web (or custom) entrypoint so Traefik registers it
|
||||
if (entrypoint === "web" || customEntrypoint) {
|
||||
labels.push(
|
||||
`traefik.http.middlewares.${middlewareName}.addprefix.prefix=${internalPath}`,
|
||||
);
|
||||
}
|
||||
middlewares.push(middlewareName);
|
||||
if (!isRedirectRouter) {
|
||||
middlewares.push(middlewareName);
|
||||
}
|
||||
}
|
||||
|
||||
// Add custom middlewares (skip for redirect-only router)
|
||||
if (!isRedirectRouter && domain.middlewares?.length) {
|
||||
middlewares.push(...domain.middlewares);
|
||||
}
|
||||
|
||||
// Apply middlewares to router if any exist
|
||||
|
||||
@@ -143,22 +143,24 @@ export const createRouterConfig = async (
|
||||
entryPoints: [entryPoint],
|
||||
};
|
||||
|
||||
// Add path rewriting middleware if needed
|
||||
if (internalPath && internalPath !== "/" && internalPath !== path) {
|
||||
const pathMiddleware = `addprefix-${appName}-${uniqueConfigKey}`;
|
||||
routerConfig.middlewares?.push(pathMiddleware);
|
||||
}
|
||||
const isRedirectRouter = entryPoint === "web" && https && !customEntrypoint;
|
||||
|
||||
if (stripPath && path && path !== "/") {
|
||||
const stripMiddleware = `stripprefix-${appName}-${uniqueConfigKey}`;
|
||||
routerConfig.middlewares?.push(stripMiddleware);
|
||||
}
|
||||
// Web router with HTTPS only needs redirect — all other middlewares
|
||||
// run on the websecure router where the request actually lands.
|
||||
if (isRedirectRouter) {
|
||||
routerConfig.middlewares?.push("redirect-to-https");
|
||||
} else {
|
||||
// Add path rewriting middleware if needed
|
||||
if (internalPath && internalPath !== "/" && internalPath !== path) {
|
||||
const pathMiddleware = `addprefix-${appName}-${uniqueConfigKey}`;
|
||||
routerConfig.middlewares?.push(pathMiddleware);
|
||||
}
|
||||
|
||||
if (entryPoint === "web" && https) {
|
||||
routerConfig.middlewares = ["redirect-to-https"];
|
||||
}
|
||||
if (stripPath && path && path !== "/") {
|
||||
const stripMiddleware = `stripprefix-${appName}-${uniqueConfigKey}`;
|
||||
routerConfig.middlewares?.push(stripMiddleware);
|
||||
}
|
||||
|
||||
if ((entryPoint === "websecure" && https) || !https) {
|
||||
// redirects - skip for preview deployments as wildcard subdomains
|
||||
// should not inherit parent redirect rules (e.g., www-redirect)
|
||||
if (domain.domainType !== "preview") {
|
||||
@@ -179,6 +181,11 @@ export const createRouterConfig = async (
|
||||
}
|
||||
routerConfig.middlewares?.push(middlewareName);
|
||||
}
|
||||
|
||||
// custom middlewares from domain
|
||||
if (domain.middlewares && domain.middlewares.length > 0) {
|
||||
routerConfig.middlewares?.push(...domain.middlewares);
|
||||
}
|
||||
}
|
||||
|
||||
if (entryPoint === "websecure" || (customEntrypoint && https)) {
|
||||
|
||||
Reference in New Issue
Block a user