From 064d42d2dc72e9b7fe2c0e1969b8e8a3f0a2eaaf Mon Sep 17 00:00:00 2001 From: Andreassenemyr Date: Thu, 12 Dec 2024 20:08:44 +0100 Subject: [PATCH 1/2] feat: add wildcard domain support - Introduced a new optional field `wildcardDomain` in the admin schema and related forms. - Updated the database schema to include the `wildcardDomain` column. - Enhanced the domain generation logic to utilize the `wildcardDomain` when provided. - Added localization strings for the new `wildcardDomain` input in the settings. This change allows users to specify a wildcard domain for their server configurations. --- .../dashboard/settings/web-domain.tsx | 28 +++++++++++++++++++ .../drizzle/0045_add_custom_domain.sql | 1 + apps/dokploy/public/locales/en/settings.json | 2 ++ apps/dokploy/templates/templates.ts | 16 +++++------ apps/dokploy/templates/utils/index.ts | 6 ++++ packages/server/src/db/schema/admin.ts | 4 +++ packages/server/src/services/domain.ts | 5 ++++ packages/server/src/templates/utils/index.ts | 6 ++++ 8 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 apps/dokploy/drizzle/0045_add_custom_domain.sql diff --git a/apps/dokploy/components/dashboard/settings/web-domain.tsx b/apps/dokploy/components/dashboard/settings/web-domain.tsx index 00f54904d..04987c0cf 100644 --- a/apps/dokploy/components/dashboard/settings/web-domain.tsx +++ b/apps/dokploy/components/dashboard/settings/web-domain.tsx @@ -35,6 +35,7 @@ const addServerDomain = z domain: z.string().min(1, { message: "URL is required" }), letsEncryptEmail: z.string(), certificateType: z.enum(["letsencrypt", "none"]), + wildcardDomain: z.string().optional(), }) .superRefine((data, ctx) => { if (data.certificateType === "letsencrypt" && !data.letsEncryptEmail) { @@ -60,6 +61,7 @@ export const WebDomain = () => { domain: "", certificateType: "none", letsEncryptEmail: "", + wildcardDomain: "", }, resolver: zodResolver(addServerDomain), }); @@ -69,6 +71,7 @@ export const WebDomain = () => { domain: user?.host || "", certificateType: user?.certificateType, letsEncryptEmail: user?.letsEncryptEmail || "", + wildcardDomain: user?.wildcardDomain || "", }); } }, [form, form.reset, user]); @@ -78,6 +81,7 @@ export const WebDomain = () => { host: data.domain, letsEncryptEmail: data.letsEncryptEmail, certificateType: data.certificateType, + wildcardDomain: data.wildcardDomain, }) .then(async () => { await refetch(); @@ -126,6 +130,30 @@ export const WebDomain = () => { }} /> + { + return ( + + + {t("settings.server.domain.form.wildcardDomain")} + + + + + + + ); + }} + /> + import("./discourse/index").then((m) => m.generate), - }, - { + }, + { id: "immich", name: "Immich", version: "v1.121.0", @@ -896,8 +896,8 @@ export const templates: TemplateData[] = [ }, tags: ["photos", "videos", "backup", "media"], load: () => import("./immich/index").then((m) => m.generate), - }, - { + }, + { id: "twenty", name: "Twenty CRM", version: "latest", @@ -911,8 +911,8 @@ export const templates: TemplateData[] = [ }, tags: ["crm", "sales", "business"], load: () => import("./twenty/index").then((m) => m.generate), - }, - { + }, + { id: "yourls", name: "YOURLS", version: "1.9.2", @@ -926,8 +926,8 @@ export const templates: TemplateData[] = [ }, tags: ["url-shortener", "php"], load: () => import("./yourls/index").then((m) => m.generate), - }, - { + }, + { id: "ryot", name: "Ryot", version: "v7.10", diff --git a/apps/dokploy/templates/utils/index.ts b/apps/dokploy/templates/utils/index.ts index b5369b916..1e3ada901 100644 --- a/apps/dokploy/templates/utils/index.ts +++ b/apps/dokploy/templates/utils/index.ts @@ -10,6 +10,7 @@ import type { TemplatesKeys } from "../types/templates-data.type"; export interface Schema { serverIp: string; projectName: string; + wildcardDomain?: string | null; } export type DomainSchema = Pick; @@ -26,10 +27,15 @@ export interface Template { export const generateRandomDomain = ({ serverIp, projectName, + wildcardDomain, }: Schema): string => { const hash = randomBytes(3).toString("hex"); const slugIp = serverIp.replaceAll(".", "-"); + if (wildcardDomain) { + return `${projectName}.${wildcardDomain}`; + } + return `${projectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.traefik.me`; }; diff --git a/packages/server/src/db/schema/admin.ts b/packages/server/src/db/schema/admin.ts index 222fb16c8..27ce7030d 100644 --- a/packages/server/src/db/schema/admin.ts +++ b/packages/server/src/db/schema/admin.ts @@ -18,6 +18,7 @@ export const admins = pgTable("admin", { serverIp: text("serverIp"), certificateType: certificateType("certificateType").notNull().default("none"), host: text("host"), + wildcardDomain: text("wildcardDomain"), letsEncryptEmail: text("letsEncryptEmail"), sshPrivateKey: text("sshPrivateKey"), enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), @@ -51,6 +52,7 @@ const createSchema = createInsertSchema(admins, { certificateType: z.enum(["letsencrypt", "none"]).default("none"), serverIp: z.string().optional(), letsEncryptEmail: z.string().optional(), + wildcardDomain: z.string().optional(), }); export const apiUpdateAdmin = createSchema.partial(); @@ -66,10 +68,12 @@ export const apiAssignDomain = createSchema host: true, certificateType: true, letsEncryptEmail: true, + wildcardDomain: true, }) .required() .partial({ letsEncryptEmail: true, + wildcardDomain: true, }); export const apiUpdateDockerCleanup = createSchema diff --git a/packages/server/src/services/domain.ts b/packages/server/src/services/domain.ts index 28dd3ba24..f434fc6f0 100644 --- a/packages/server/src/services/domain.ts +++ b/packages/server/src/services/domain.ts @@ -45,22 +45,27 @@ export const generateTraefikMeDomain = async ( ) => { if (serverId) { const server = await findServerById(serverId); + const admin = await findAdminById(adminId); return generateRandomDomain({ serverIp: server.ipAddress, projectName: appName, + wildcardDomain: admin?.wildcardDomain, }); } if (process.env.NODE_ENV === "development") { + const admin = await findAdminById(adminId); return generateRandomDomain({ serverIp: "", projectName: appName, + wildcardDomain: admin?.wildcardDomain, }); } const admin = await findAdminById(adminId); return generateRandomDomain({ serverIp: admin?.serverIp || "", projectName: appName, + wildcardDomain: admin?.wildcardDomain, }); }; diff --git a/packages/server/src/templates/utils/index.ts b/packages/server/src/templates/utils/index.ts index b8eb7d497..84f641fd3 100644 --- a/packages/server/src/templates/utils/index.ts +++ b/packages/server/src/templates/utils/index.ts @@ -6,6 +6,7 @@ import type { Domain } from "@dokploy/server/services/domain"; export interface Schema { serverIp: string; projectName: string; + wildcardDomain?: string | null; } export type DomainSchema = Pick; @@ -22,10 +23,15 @@ export interface Template { export const generateRandomDomain = ({ serverIp, projectName, + wildcardDomain, }: Schema): string => { const hash = randomBytes(3).toString("hex"); const slugIp = serverIp.replaceAll(".", "-"); + if (wildcardDomain) { + return `${projectName}.${wildcardDomain}`; + } + return `${projectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.traefik.me`; }; From daf0a990780508172e5919730ee7025a0d0069ea Mon Sep 17 00:00:00 2001 From: Andreassenemyr Date: Thu, 12 Dec 2024 20:15:35 +0100 Subject: [PATCH 2/2] fix: add wildcardDomain to admin configuration --- .../dokploy/__test__/traefik/server/update-server-config.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index c966748a7..2619aa468 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -20,6 +20,7 @@ const baseAdmin: Admin = { serverIp: null, certificateType: "none", host: null, + wildcardDomain: null, letsEncryptEmail: null, sshPrivateKey: null, enableDockerCleanup: false,