diff --git a/apps/dokploy/components/dashboard/impersonation/impersonation-bar.tsx b/apps/dokploy/components/dashboard/impersonation/impersonation-bar.tsx index 02f7f59f1..1f0c6924c 100644 --- a/apps/dokploy/components/dashboard/impersonation/impersonation-bar.tsx +++ b/apps/dokploy/components/dashboard/impersonation/impersonation-bar.tsx @@ -45,12 +45,12 @@ import { import { authClient } from "@/lib/auth-client"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; type User = typeof authClient.$Infer.Session.user; export const ImpersonationBar = () => { - const { config: whitelabeling } = useWhitelabelingPublic(); + const { config: whitelabeling } = useWhitelabeling(); const [users, setUsers] = useState([]); const [selectedUser, setSelectedUser] = useState(null); const [isImpersonating, setIsImpersonating] = useState(false); diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index bab57f797..d3f5bbc71 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -23,8 +23,8 @@ import { Loader2, LogIn, type LucideIcon, - Palette, Package, + Palette, PieChart, Rocket, Server, @@ -895,10 +895,10 @@ export default function Page({ children }: Props) { const pathname = usePathname(); const { data: auth } = api.user.get.useQuery(); const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); - const { data: whitelabeling } = api.whitelabeling.getPublic.useQuery( - undefined, - { staleTime: 5 * 60 * 1000, refetchOnWindowFocus: false }, - ); + const { data: whitelabeling } = api.whitelabeling.get.useQuery(undefined, { + staleTime: 5 * 60 * 1000, + refetchOnWindowFocus: false, + }); const includesProjects = pathname?.includes("/dashboard/project"); const { data: isCloud } = api.settings.isCloud.useQuery(); diff --git a/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-settings.tsx b/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-settings.tsx index ba54c2d8f..f754a7352 100644 --- a/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-settings.tsx +++ b/apps/dokploy/components/proprietary/whitelabeling/whitelabeling-settings.tsx @@ -37,6 +37,8 @@ const formSchema = z.object({ faviconUrl: z.string(), customCss: z.string(), loginLogoUrl: z.string(), + supportUrl: z.string(), + docsUrl: z.string(), errorPageTitle: z.string(), errorPageDescription: z.string(), metaTitle: z.string(), @@ -173,6 +175,8 @@ export function WhitelabelingSettings() { faviconUrl: "", customCss: "", loginLogoUrl: "", + supportUrl: "", + docsUrl: "", errorPageTitle: "", errorPageDescription: "", metaTitle: "", @@ -190,6 +194,8 @@ export function WhitelabelingSettings() { faviconUrl: data.faviconUrl ?? "", customCss: data.customCss ?? "", loginLogoUrl: data.loginLogoUrl ?? "", + supportUrl: data.supportUrl ?? "", + docsUrl: data.docsUrl ?? "", errorPageTitle: data.errorPageTitle ?? "", errorPageDescription: data.errorPageDescription ?? "", metaTitle: data.metaTitle ?? "", @@ -219,8 +225,8 @@ export function WhitelabelingSettings() { primaryColor: null, customCss: values.customCss || null, loginLogoUrl: values.loginLogoUrl || null, - supportUrl: null, - docsUrl: null, + supportUrl: values.supportUrl || null, + docsUrl: values.docsUrl || null, errorPageTitle: values.errorPageTitle || null, errorPageDescription: values.errorPageDescription || null, metaTitle: values.metaTitle || null, @@ -231,6 +237,7 @@ export function WhitelabelingSettings() { toast.success("Whitelabeling settings updated"); await refetch(); await utils.whitelabeling.getPublic.invalidate(); + await utils.whitelabeling.get.invalidate(); }) .catch((error) => { toast.error( @@ -245,6 +252,7 @@ export function WhitelabelingSettings() { toast.success("Whitelabeling settings reset to defaults"); await refetch(); await utils.whitelabeling.getPublic.invalidate(); + await utils.whitelabeling.get.invalidate(); }) .catch((error) => { toast.error(error?.message || "Failed to reset whitelabeling settings"); @@ -423,9 +431,9 @@ export function WhitelabelingSettings() { {/* Metadata & Links Section */} - Metadata + Metadata & Links - Customize the page title and footer text. + Customize the page title, footer text, and sidebar links. @@ -462,6 +470,46 @@ export function WhitelabelingSettings() { )} /> + + ( + + Support URL + + + + + Custom URL for the "Support" link in the sidebar. + + + + )} + /> + + ( + + Documentation URL + + + + + Custom URL for the "Documentation" link in the sidebar. + + + + )} + /> diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx index ed1a9892f..41c44d2a8 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx @@ -98,7 +98,7 @@ import { import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; export type Services = { serverId?: string | null; @@ -371,7 +371,7 @@ const EnvironmentPage = ( { projectId: selectedTargetProject }, { enabled: !!selectedTargetProject }, ); - const { config: whitelabeling } = useWhitelabelingPublic(); + const { config: whitelabeling } = useWhitelabeling(); const appName = whitelabeling?.appName || "Dokploy"; const emptyServices = diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx index d41ccbc1a..544ef28ae 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx @@ -56,7 +56,7 @@ import { import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; type TabState = | "projects" @@ -96,7 +96,7 @@ const Service = ( const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.project?.projectId || "", }); - const { config: whitelabeling } = useWhitelabelingPublic(); + const { config: whitelabeling } = useWhitelabeling(); const appName = whitelabeling?.appName || "Dokploy"; const environmentDropdownItems = environments?.map((env) => ({ diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx index b3dd6926e..c168959ec 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx @@ -52,7 +52,7 @@ import { import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; type TabState = | "projects" @@ -85,7 +85,7 @@ const Service = ( const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); - const { config: whitelabeling } = useWhitelabelingPublic(); + const { config: whitelabeling } = useWhitelabeling(); const appName = whitelabeling?.appName || "Dokploy"; const environmentDropdownItems = environments?.map((env) => ({ diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx index dae865c26..a5ff414e9 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx @@ -45,7 +45,7 @@ import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; @@ -66,7 +66,7 @@ const Mariadb = ( const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); - const { config: whitelabeling } = useWhitelabelingPublic(); + const { config: whitelabeling } = useWhitelabeling(); const appName = whitelabeling?.appName || "Dokploy"; const environmentDropdownItems = environments?.map((env) => ({ diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx index 8b3a971d1..456789332 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx @@ -45,7 +45,7 @@ import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; @@ -65,7 +65,7 @@ const Mongo = ( const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); - const { config: whitelabeling } = useWhitelabelingPublic(); + const { config: whitelabeling } = useWhitelabeling(); const appName = whitelabeling?.appName || "Dokploy"; const environmentDropdownItems = environments?.map((env) => ({ diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx index 530af2ff5..76af699a5 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx @@ -45,7 +45,7 @@ import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; @@ -64,7 +64,7 @@ const MySql = ( const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); - const { config: whitelabeling } = useWhitelabelingPublic(); + const { config: whitelabeling } = useWhitelabeling(); const appName = whitelabeling?.appName || "Dokploy"; const environmentDropdownItems = environments?.map((env) => ({ diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx index 380a0234d..d97ada5b0 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx @@ -45,7 +45,7 @@ import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; @@ -64,7 +64,7 @@ const Postgresql = ( const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); - const { config: whitelabeling } = useWhitelabelingPublic(); + const { config: whitelabeling } = useWhitelabeling(); const appName = whitelabeling?.appName || "Dokploy"; const environmentDropdownItems = environments?.map((env) => ({ diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx index 93aa30ea6..95058d4a6 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx @@ -44,7 +44,7 @@ import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { useWhitelabelingPublic } from "@/utils/hooks/use-whitelabeling"; +import { useWhitelabeling } from "@/utils/hooks/use-whitelabeling"; type TabState = "projects" | "monitoring" | "settings" | "advanced"; @@ -64,7 +64,7 @@ const Redis = ( const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); - const { config: whitelabeling } = useWhitelabelingPublic(); + const { config: whitelabeling } = useWhitelabeling(); const appName = whitelabeling?.appName || "Dokploy"; const environmentDropdownItems = environments?.map((env) => ({ diff --git a/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts b/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts index 6133f752f..2f641902b 100644 --- a/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts +++ b/apps/dokploy/server/api/routers/proprietary/whitelabeling.ts @@ -8,11 +8,12 @@ import { apiUpdateWhitelabeling } from "@/server/db/schema"; import { createTRPCRouter, enterpriseProcedure, + protectedProcedure, publicProcedure, } from "../../trpc"; export const whitelabelingRouter = createTRPCRouter({ - get: enterpriseProcedure.query(async () => { + get: protectedProcedure.query(async () => { if (IS_CLOUD) { return null; } @@ -80,13 +81,28 @@ export const whitelabelingRouter = createTRPCRouter({ return { success: true }; }), - // Public endpoint so the whitelabeling config can be applied globally - // (including on the login page before auth) + // 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(); - return settings?.whitelabelingConfig ?? null; + const config = settings?.whitelabelingConfig; + if (!config) return null; + + return { + appName: config.appName, + appDescription: config.appDescription, + logoUrl: config.logoUrl, + loginLogoUrl: config.loginLogoUrl, + faviconUrl: config.faviconUrl, + primaryColor: config.primaryColor, + customCss: config.customCss, + metaTitle: config.metaTitle, + errorPageTitle: config.errorPageTitle, + errorPageDescription: config.errorPageDescription, + footerText: config.footerText, + }; }), }); diff --git a/apps/dokploy/utils/hooks/use-whitelabeling.ts b/apps/dokploy/utils/hooks/use-whitelabeling.ts index 784817e27..0970f04f9 100644 --- a/apps/dokploy/utils/hooks/use-whitelabeling.ts +++ b/apps/dokploy/utils/hooks/use-whitelabeling.ts @@ -1,8 +1,20 @@ import { api } from "@/utils/api"; +/** + * Hook to access whitelabeling config for authenticated pages (dashboard, services, etc.). + * Requires the user to be logged in. + */ +export function useWhitelabeling() { + const { data, ...rest } = api.whitelabeling.get.useQuery(undefined, { + staleTime: 5 * 60 * 1000, + refetchOnWindowFocus: false, + }); + return { config: data ?? null, ...rest }; +} + /** * Hook to access the public whitelabeling config. - * Can be used on any page (including login) since it uses the public endpoint. + * Only for unauthenticated pages (login, register, error, invitation, password reset). */ export function useWhitelabelingPublic() { const { data, ...rest } = api.whitelabeling.getPublic.useQuery(undefined, {