diff --git a/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts b/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts index b504ed0f5..bca5a14e1 100644 --- a/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts +++ b/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts @@ -1,5 +1,7 @@ import { + getPublicWhitelabelingConfig, getWebServerSettings, + hasValidLicense, IS_CLOUD, updateWebServerSettings, } from "@dokploy/server"; @@ -13,10 +15,13 @@ import { } from "../../trpc"; export const whitelabelingRouter = createTRPCRouter({ - get: protectedProcedure.query(async () => { + get: protectedProcedure.query(async ({ ctx }) => { if (IS_CLOUD) { return null; } + if (!(await hasValidLicense(ctx.session.activeOrganizationId))) { + return null; + } const settings = await getWebServerSettings(); return settings?.whitelabelingConfig ?? null; }), @@ -82,25 +87,5 @@ export const whitelabelingRouter = createTRPCRouter({ // Public endpoint only for unauthenticated pages (login, register, error) // Returns only the fields needed for public pages - getPublic: publicProcedure.query(async () => { - if (IS_CLOUD) { - return null; - } - const settings = await getWebServerSettings(); - const config = settings?.whitelabelingConfig; - if (!config) return null; - - return { - appName: config.appName, - appDescription: config.appDescription, - logoUrl: config.logoUrl, - loginLogoUrl: config.loginLogoUrl, - faviconUrl: config.faviconUrl, - customCss: config.customCss, - metaTitle: config.metaTitle, - errorPageTitle: config.errorPageTitle, - errorPageDescription: config.errorPageDescription, - footerText: config.footerText, - }; - }), + getPublic: publicProcedure.query(() => getPublicWhitelabelingConfig()), }); diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index b8a2fcec7..165e1667e 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -38,6 +38,7 @@ export * from "./services/project"; export * from "./services/proprietary/forward-auth"; export * from "./services/proprietary/license-key"; export * from "./services/proprietary/sso"; +export * from "./services/proprietary/whitelabeling"; export * from "./services/redirect"; export * from "./services/redis"; export * from "./services/registry"; diff --git a/packages/server/src/services/proprietary/whitelabeling.ts b/packages/server/src/services/proprietary/whitelabeling.ts new file mode 100644 index 000000000..c1d6e39db --- /dev/null +++ b/packages/server/src/services/proprietary/whitelabeling.ts @@ -0,0 +1,58 @@ +import { IS_CLOUD } from "@dokploy/server/constants"; +import { db } from "@dokploy/server/db"; +import { hasValidLicense } from "@dokploy/server/services/proprietary/license-key"; +import { getWebServerSettings } from "@dokploy/server/services/web-server-settings"; + +export interface PublicWhitelabelingConfig { + appName: string | null; + appDescription: string | null; + logoUrl: string | null; + loginLogoUrl: string | null; + faviconUrl: string | null; + customCss: string | null; + metaTitle: string | null; + errorPageTitle: string | null; + errorPageDescription: string | null; + footerText: string | null; +} + +// Self-hosted is single-tenant; gate on the oldest organization's license, +// mirroring resolveLocalConcurrency in server/queues/concurrency.ts. Used only +// for unauthenticated requests (no active organization in session). +const hasAnyValidLicense = async (): Promise => { + const org = await db.query.organization.findFirst({ + columns: { id: true }, + orderBy: (organization, { asc }) => [asc(organization.createdAt)], + }); + return org ? await hasValidLicense(org.id) : false; +}; + +/** + * Public whitelabeling config for unauthenticated contexts (login page, SSR + * document shell). No active organization/session is available here. + */ +export const getPublicWhitelabelingConfig = + async (): Promise => { + if (IS_CLOUD) { + return null; + } + if (!(await hasAnyValidLicense())) { + return null; + } + const settings = await getWebServerSettings(); + const config = settings?.whitelabelingConfig; + if (!config) return null; + + return { + appName: config.appName, + appDescription: config.appDescription, + logoUrl: config.logoUrl, + loginLogoUrl: config.loginLogoUrl, + faviconUrl: config.faviconUrl, + customCss: config.customCss, + metaTitle: config.metaTitle, + errorPageTitle: config.errorPageTitle, + errorPageDescription: config.errorPageDescription, + footerText: config.footerText, + }; + };