Files
dokploy/packages/server/src/db/schema/domain.ts
Jhonatan Caldeira bb904bb011 feat: add internal path routing and path stripping for domains
- Add internalPath and stripPath fields to domain schema
- Implement UI controls for configuring internal path routing
- Create Traefik middleware for path manipulation (addPrefix/stripPrefix)
- Support different external and internal paths for applications
- Enable path stripping for cleaner URL forwarding

This allows applications to be accessed via external paths while maintaining
different internal routing structures, useful for microservices and legacy
applications that expect specific path prefixes.
2025-06-20 16:36:27 -03:00

123 lines
3.1 KiB
TypeScript

import { relations } from "drizzle-orm";
import {
type AnyPgColumn,
boolean,
integer,
pgEnum,
pgTable,
serial,
text,
} from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { domain } from "../validations/domain";
import { applications } from "./application";
import { compose } from "./compose";
import { previewDeployments } from "./preview-deployments";
import { certificateType } from "./shared";
export const domainType = pgEnum("domainType", [
"compose",
"application",
"preview",
]);
export const domains = pgTable("domain", {
domainId: text("domainId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
host: text("host").notNull(),
https: boolean("https").notNull().default(false),
port: integer("port").default(3000),
path: text("path").default("/"),
serviceName: text("serviceName"),
domainType: domainType("domainType").default("application"),
uniqueConfigKey: serial("uniqueConfigKey"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
composeId: text("composeId").references(() => compose.composeId, {
onDelete: "cascade",
}),
customCertResolver: text("customCertResolver"),
applicationId: text("applicationId").references(
() => applications.applicationId,
{ onDelete: "cascade" },
),
previewDeploymentId: text("previewDeploymentId").references(
(): AnyPgColumn => previewDeployments.previewDeploymentId,
{ onDelete: "cascade" },
),
certificateType: certificateType("certificateType").notNull().default("none"),
internalPath: text("internalPath").default("/"),
stripPath: boolean("stripPath").notNull().default(false),
});
export const domainsRelations = relations(domains, ({ one }) => ({
application: one(applications, {
fields: [domains.applicationId],
references: [applications.applicationId],
}),
compose: one(compose, {
fields: [domains.composeId],
references: [compose.composeId],
}),
previewDeployment: one(previewDeployments, {
fields: [domains.previewDeploymentId],
references: [previewDeployments.previewDeploymentId],
}),
}));
const createSchema = createInsertSchema(domains, domain._def.schema.shape);
export const apiCreateDomain = createSchema.pick({
host: true,
path: true,
port: true,
https: true,
applicationId: true,
certificateType: true,
customCertResolver: true,
composeId: true,
serviceName: true,
domainType: true,
previewDeploymentId: true,
internalPath: true,
stripPath: true,
});
export const apiFindDomain = createSchema
.pick({
domainId: true,
})
.required();
export const apiFindDomainByApplication = createSchema.pick({
applicationId: true,
});
export const apiCreateTraefikMeDomain = createSchema.pick({}).extend({
appName: z.string().min(1),
});
export const apiFindDomainByCompose = createSchema.pick({
composeId: true,
});
export const apiUpdateDomain = createSchema
.pick({
host: true,
path: true,
port: true,
https: true,
certificateType: true,
customCertResolver: true,
serviceName: true,
domainType: true,
internalPath: true,
stripPath: true,
})
.merge(createSchema.pick({ domainId: true }).required());