Merge branch 'canary' into feat/scaleway-secret-manager

This commit is contained in:
Mauricio Siu
2026-08-11 20:02:08 -06:00
18 changed files with 9638 additions and 53 deletions
+2
View File
@@ -56,6 +56,7 @@ export const domains = pgTable("domain", {
stripPath: boolean("stripPath").notNull().default(false),
middlewares: text("middlewares").array().default(sql`ARRAY[]::text[]`),
forwardAuthEnabled: boolean("forwardAuthEnabled").notNull().default(false),
enabled: boolean("enabled").notNull().default(true),
});
export const domainsRelations = relations(domains, ({ one }) => ({
@@ -129,5 +130,6 @@ export const apiUpdateDomain = createSchema
stripPath: true,
middlewares: true,
forwardAuthEnabled: true,
enabled: true,
})
.merge(createSchema.pick({ domainId: true }).required());
+1 -1
View File
@@ -20,7 +20,7 @@ export const TRAEFIK_PORT =
Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80;
export const TRAEFIK_HTTP3_PORT =
Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443;
export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.6.7";
export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.6.25";
export interface TraefikOptions {
env?: string[];
+64 -22
View File
@@ -170,6 +170,32 @@ export const applyComposeFilePatch = async (
}
};
const removeDomainLabels = (
labels: DefinitionsService["labels"],
appName: string,
uniqueConfigKey: number,
) => {
const prefixes = [
`traefik.http.routers.${appName}-${uniqueConfigKey}-`,
`traefik.http.services.${appName}-${uniqueConfigKey}-`,
`traefik.http.middlewares.stripprefix-${appName}-${uniqueConfigKey}.`,
`traefik.http.middlewares.addprefix-${appName}-${uniqueConfigKey}.`,
];
const belongsToDomain = (label: string) =>
prefixes.some((prefix) => label.startsWith(prefix));
if (Array.isArray(labels)) {
return labels.filter((label) => !belongsToDomain(label));
}
if (labels) {
return Object.fromEntries(
Object.entries(labels).filter(([label]) => !belongsToDomain(label)),
);
}
return labels;
};
export const addDomainToCompose = async (
compose: Compose,
domains: Domain[],
@@ -203,6 +229,24 @@ export const addDomainToCompose = async (
}
for (const domain of domains) {
for (const service of Object.values(result.services ?? {})) {
if (compose.composeType === "docker-compose") {
service.labels = removeDomainLabels(
service.labels,
appName,
domain.uniqueConfigKey,
);
} else if (service.deploy) {
service.deploy.labels = removeDomainLabels(
service.deploy.labels,
appName,
domain.uniqueConfigKey,
);
}
}
}
for (const domain of domains.filter((d) => d.enabled)) {
const { serviceName, https } = domain;
if (!serviceName) {
throw new Error(`Domain "${domain.host}" is missing a service name`);
@@ -242,33 +286,31 @@ export const addDomainToCompose = async (
labels = result.services[serviceName].deploy.labels;
}
const networkLabel =
compose.composeType === "docker-compose"
? "traefik.docker.network"
: "traefik.swarm.network";
const networkName = compose.isolatedDeployment
? compose.suffix || compose.appName
: "dokploy-network";
if (Array.isArray(labels)) {
if (!labels.includes("traefik.enable=true")) {
labels.unshift("traefik.enable=true");
}
labels.unshift(...httpLabels);
if (!compose.isolatedDeployment) {
if (compose.composeType === "docker-compose") {
if (!labels.includes("traefik.docker.network=dokploy-network")) {
labels.unshift("traefik.docker.network=dokploy-network");
}
} else {
// Stack Case
if (!labels.includes("traefik.swarm.network=dokploy-network")) {
labels.unshift("traefik.swarm.network=dokploy-network");
}
}
} else {
const isolatedNetwork = compose.suffix || compose.appName;
if (compose.composeType === "docker-compose") {
if (!labels.includes(`traefik.docker.network=${isolatedNetwork}`)) {
labels.unshift(`traefik.docker.network=${isolatedNetwork}`);
}
} else {
if (!labels.includes(`traefik.swarm.network=${isolatedNetwork}`)) {
labels.unshift(`traefik.swarm.network=${isolatedNetwork}`);
}
}
const networkLabelEntry = `${networkLabel}=${networkName}`;
if (!labels.includes(networkLabelEntry)) {
labels.unshift(networkLabelEntry);
}
} else if (labels) {
labels["traefik.enable"] = "true";
labels[networkLabel] = networkName;
for (const label of httpLabels) {
const separatorIndex = label.indexOf("=");
labels[label.slice(0, separatorIndex)] = label.slice(
separatorIndex + 1,
);
}
}
@@ -19,6 +19,15 @@ import { createPathMiddlewares, removePathMiddlewares } from "./middleware";
export const manageDomain = async (app: ApplicationNested, domain: Domain) => {
const { appName } = app;
// A disabled domain keeps its configuration in the database but must never
// expose a traefik router. Guarding here covers every caller (create, update,
// forward-auth, toggle) so a disabled domain can't be revived from any path.
if (!domain.enabled) {
await removeDomain(app, domain.uniqueConfigKey);
return;
}
let config: FileConfig;
if (app.serverId) {