diff --git a/server/index.ts b/server/index.ts index a92968a6..8b4b3728 100644 --- a/server/index.ts +++ b/server/index.ts @@ -19,11 +19,16 @@ import { setHostMeta } from "@server/lib/hostMeta"; import { initTelemetryClient } from "./lib/telemetry.js"; import { TraefikConfigManager } from "./lib/traefik/TraefikConfigManager.js"; import { initCleanup } from "#dynamic/cleanup"; +import license from "#dynamic/license/license"; async function startServers() { await setHostMeta(); await config.initServer(); + + license.setServerSecret(config.getRawConfig().server.secret!); + await license.check(); + await runSetupFunctions(); initTelemetryClient(); diff --git a/server/lib/config.ts b/server/lib/config.ts index 22c30338..6cd3413e 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts @@ -3,11 +3,9 @@ import { __DIRNAME, APP_VERSION } from "@server/lib/consts"; import { db } from "@server/db"; import { SupporterKey, supporterKey } from "@server/db"; import { eq } from "drizzle-orm"; -import { license } from "#dynamic/license/license"; import { configSchema, readConfigFile } from "./readConfigFile"; import { fromError } from "zod-validation-error"; import { build } from "@server/build"; -import logger from "@server/logger"; export class Config { private rawConfig!: z.infer; @@ -103,16 +101,10 @@ export class Config { throw new Error("Config not loaded. Call load() first."); } - license.setServerSecret(this.rawConfig.server.secret!); - await this.checkKeyStatus(); } private async checkKeyStatus() { - if (build === "enterprise") { - await license.check(); - } - if (build == "oss") { this.checkSupporterKey(); } diff --git a/server/private/lib/config.ts b/server/private/lib/config.ts index eed642ed..d310d271 100644 --- a/server/private/lib/config.ts +++ b/server/private/lib/config.ts @@ -19,8 +19,6 @@ import { privateConfigSchema, readPrivateConfigFile } from "#private/lib/readConfigFile"; -import { build } from "@server/build"; -import license from "#private/license/license"; export class PrivateConfig { private rawPrivateConfig!: z.infer; @@ -47,99 +45,81 @@ export class PrivateConfig { this.rawPrivateConfig = parsedPrivateConfig; - this.loadEnv(); - } - - private async loadEnv() { - let isValidLicense = false; - if (build === "enterprise") { - const licenseData = await license.check(); - if (licenseData.isLicenseValid) { - isValidLicense = true; - } - } else if (build === "saas") { - isValidLicense = true; + if (this.rawPrivateConfig.branding?.colors) { + process.env.BRANDING_COLORS = JSON.stringify( + this.rawPrivateConfig.branding?.colors + ); } - if (isValidLicense) { - if (this.rawPrivateConfig.branding?.colors) { - process.env.BRANDING_COLORS = JSON.stringify( - this.rawPrivateConfig.branding?.colors - ); - } + if (this.rawPrivateConfig.branding?.logo?.light_path) { + process.env.BRANDING_LOGO_LIGHT_PATH = + this.rawPrivateConfig.branding?.logo?.light_path; + } + if (this.rawPrivateConfig.branding?.logo?.dark_path) { + process.env.BRANDING_LOGO_DARK_PATH = + this.rawPrivateConfig.branding?.logo?.dark_path || undefined; + } - if (this.rawPrivateConfig.branding?.logo?.light_path) { - process.env.BRANDING_LOGO_LIGHT_PATH = - this.rawPrivateConfig.branding?.logo?.light_path; - } - if (this.rawPrivateConfig.branding?.logo?.dark_path) { - process.env.BRANDING_LOGO_DARK_PATH = - this.rawPrivateConfig.branding?.logo?.dark_path || - undefined; - } + process.env.BRANDING_LOGO_AUTH_WIDTH = this.rawPrivateConfig.branding + ?.logo?.auth_page?.width + ? this.rawPrivateConfig.branding?.logo?.auth_page?.width.toString() + : undefined; + process.env.BRANDING_LOGO_AUTH_HEIGHT = this.rawPrivateConfig.branding + ?.logo?.auth_page?.height + ? this.rawPrivateConfig.branding?.logo?.auth_page?.height.toString() + : undefined; - process.env.BRANDING_LOGO_AUTH_WIDTH = this.rawPrivateConfig - .branding?.logo?.auth_page?.width - ? this.rawPrivateConfig.branding?.logo?.auth_page?.width.toString() - : undefined; - process.env.BRANDING_LOGO_AUTH_HEIGHT = this.rawPrivateConfig - .branding?.logo?.auth_page?.height - ? this.rawPrivateConfig.branding?.logo?.auth_page?.height.toString() - : undefined; + process.env.BRANDING_LOGO_NAVBAR_WIDTH = this.rawPrivateConfig.branding + ?.logo?.navbar?.width + ? this.rawPrivateConfig.branding?.logo?.navbar?.width.toString() + : undefined; + process.env.BRANDING_LOGO_NAVBAR_HEIGHT = this.rawPrivateConfig.branding + ?.logo?.navbar?.height + ? this.rawPrivateConfig.branding?.logo?.navbar?.height.toString() + : undefined; - process.env.BRANDING_LOGO_NAVBAR_WIDTH = this.rawPrivateConfig - .branding?.logo?.navbar?.width - ? this.rawPrivateConfig.branding?.logo?.navbar?.width.toString() - : undefined; - process.env.BRANDING_LOGO_NAVBAR_HEIGHT = this.rawPrivateConfig - .branding?.logo?.navbar?.height - ? this.rawPrivateConfig.branding?.logo?.navbar?.height.toString() - : undefined; + process.env.BRANDING_FAVICON_PATH = + this.rawPrivateConfig.branding?.favicon_path; - process.env.BRANDING_FAVICON_PATH = - this.rawPrivateConfig.branding?.favicon_path; + process.env.BRANDING_APP_NAME = + this.rawPrivateConfig.branding?.app_name || "Pangolin"; - process.env.BRANDING_APP_NAME = - this.rawPrivateConfig.branding?.app_name || "Pangolin"; + if (this.rawPrivateConfig.branding?.footer) { + process.env.BRANDING_FOOTER = JSON.stringify( + this.rawPrivateConfig.branding?.footer + ); + } - if (this.rawPrivateConfig.branding?.footer) { - process.env.BRANDING_FOOTER = JSON.stringify( - this.rawPrivateConfig.branding?.footer - ); - } + process.env.LOGIN_PAGE_TITLE_TEXT = + this.rawPrivateConfig.branding?.login_page?.title_text || ""; + process.env.LOGIN_PAGE_SUBTITLE_TEXT = + this.rawPrivateConfig.branding?.login_page?.subtitle_text || ""; - process.env.LOGIN_PAGE_TITLE_TEXT = - this.rawPrivateConfig.branding?.login_page?.title_text || ""; - process.env.LOGIN_PAGE_SUBTITLE_TEXT = - this.rawPrivateConfig.branding?.login_page?.subtitle_text || ""; + process.env.SIGNUP_PAGE_TITLE_TEXT = + this.rawPrivateConfig.branding?.signup_page?.title_text || ""; + process.env.SIGNUP_PAGE_SUBTITLE_TEXT = + this.rawPrivateConfig.branding?.signup_page?.subtitle_text || ""; - process.env.SIGNUP_PAGE_TITLE_TEXT = - this.rawPrivateConfig.branding?.signup_page?.title_text || ""; - process.env.SIGNUP_PAGE_SUBTITLE_TEXT = - this.rawPrivateConfig.branding?.signup_page?.subtitle_text || - ""; + process.env.RESOURCE_AUTH_PAGE_HIDE_POWERED_BY = + this.rawPrivateConfig.branding?.resource_auth_page + ?.hide_powered_by === true + ? "true" + : "false"; + process.env.RESOURCE_AUTH_PAGE_SHOW_LOGO = + this.rawPrivateConfig.branding?.resource_auth_page?.show_logo === + true + ? "true" + : "false"; + process.env.RESOURCE_AUTH_PAGE_TITLE_TEXT = + this.rawPrivateConfig.branding?.resource_auth_page?.title_text || + ""; + process.env.RESOURCE_AUTH_PAGE_SUBTITLE_TEXT = + this.rawPrivateConfig.branding?.resource_auth_page?.subtitle_text || + ""; - process.env.RESOURCE_AUTH_PAGE_HIDE_POWERED_BY = - this.rawPrivateConfig.branding?.resource_auth_page - ?.hide_powered_by === true - ? "true" - : "false"; - process.env.RESOURCE_AUTH_PAGE_SHOW_LOGO = - this.rawPrivateConfig.branding?.resource_auth_page - ?.show_logo === true - ? "true" - : "false"; - process.env.RESOURCE_AUTH_PAGE_TITLE_TEXT = - this.rawPrivateConfig.branding?.resource_auth_page - ?.title_text || ""; - process.env.RESOURCE_AUTH_PAGE_SUBTITLE_TEXT = - this.rawPrivateConfig.branding?.resource_auth_page - ?.subtitle_text || ""; - - if (this.rawPrivateConfig.branding?.background_image_path) { - process.env.BACKGROUND_IMAGE_PATH = - this.rawPrivateConfig.branding?.background_image_path; - } + if (this.rawPrivateConfig.branding?.background_image_path) { + process.env.BACKGROUND_IMAGE_PATH = + this.rawPrivateConfig.branding?.background_image_path; } if (this.rawPrivateConfig.server.reo_client_id) { diff --git a/server/private/lib/readConfigFile.ts b/server/private/lib/readConfigFile.ts index a983f39c..71613f5f 100644 --- a/server/private/lib/readConfigFile.ts +++ b/server/private/lib/readConfigFile.ts @@ -174,9 +174,9 @@ export function readPrivateConfigFile() { // test if the config file is there if (!fs.existsSync(privateConfigFilePath1)) { - console.warn( - `Private configuration file not found at ${privateConfigFilePath1}. Using default configuration.` - ); + // console.warn( + // `Private configuration file not found at ${privateConfigFilePath1}. Using default configuration.` + // ); // load the default values of the zod schema and return those return privateConfigSchema.parse({}); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index fe1fb31b..5cd083b8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -67,6 +67,12 @@ export default async function RootLayout({ ) )(); licenseStatus = licenseStatusRes.data.data; + } else if (build === "saas") { + licenseStatus = { + isHostLicensed: true, + isLicenseValid: true, + hostId: "saas" + }; } else { licenseStatus = { isHostLicensed: false, diff --git a/src/components/BrandingLogo.tsx b/src/components/BrandingLogo.tsx index 4a5330c8..540b8e0e 100644 --- a/src/components/BrandingLogo.tsx +++ b/src/components/BrandingLogo.tsx @@ -1,6 +1,7 @@ "use client"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useTheme } from "next-themes"; import Image from "next/image"; import { useEffect, useState } from "react"; @@ -13,6 +14,7 @@ type BrandingLogoProps = { export default function BrandingLogo(props: BrandingLogoProps) { const { env } = useEnvContext(); const { theme } = useTheme(); + const { isUnlocked } = useLicenseStatusContext(); const [path, setPath] = useState(""); // Default logo path useEffect(() => { @@ -27,12 +29,16 @@ export default function BrandingLogo(props: BrandingLogoProps) { } if (lightOrDark === "light") { - return ( - env.branding.logo?.lightPath || "/logo/word_mark_black.png" - ); + if (isUnlocked() && env.branding.logo?.lightPath) { + return env.branding.logo.lightPath; + } + return "/logo/word_mark_black.png"; } - return env.branding.logo?.darkPath || "/logo/word_mark_white.png"; + if (isUnlocked() && env.branding.logo?.darkPath) { + return env.branding.logo.darkPath; + } + return "/logo/word_mark_white.png"; } const path = getPath(); diff --git a/src/components/DashboardLoginForm.tsx b/src/components/DashboardLoginForm.tsx index 7be37db5..6d3fc332 100644 --- a/src/components/DashboardLoginForm.tsx +++ b/src/components/DashboardLoginForm.tsx @@ -16,6 +16,7 @@ import Image from "next/image"; import { cleanRedirect } from "@app/lib/cleanRedirect"; import BrandingLogo from "@app/components/BrandingLogo"; import { useTranslations } from "next-intl"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; type DashboardLoginFormProps = { redirect?: string; @@ -29,18 +30,22 @@ export default function DashboardLoginForm({ const router = useRouter(); const { env } = useEnvContext(); const t = useTranslations(); + const { isUnlocked } = useLicenseStatusContext(); function getSubtitle() { return t("loginStart"); } + const logoWidth = isUnlocked() ? env.branding.logo?.authPage?.width || 175 : 175; + const logoHeight = isUnlocked() ? env.branding.logo?.authPage?.height || 58 : 58; + return (
diff --git a/src/components/LayoutHeader.tsx b/src/components/LayoutHeader.tsx index 36db7628..390e24ac 100644 --- a/src/components/LayoutHeader.tsx +++ b/src/components/LayoutHeader.tsx @@ -1,15 +1,13 @@ "use client"; import React, { useEffect, useState } from "react"; -import Image from "next/image"; import Link from "next/link"; import ProfileIcon from "@app/components/ProfileIcon"; import ThemeSwitcher from "@app/components/ThemeSwitcher"; import { useTheme } from "next-themes"; import BrandingLogo from "./BrandingLogo"; import { useEnvContext } from "@app/hooks/useEnvContext"; -import { Badge } from "./ui/badge"; -import { build } from "@server/build"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; interface LayoutHeaderProps { showTopBar: boolean; @@ -19,6 +17,14 @@ export function LayoutHeader({ showTopBar }: LayoutHeaderProps) { const { theme } = useTheme(); const [path, setPath] = useState(""); const { env } = useEnvContext(); + const { isUnlocked } = useLicenseStatusContext(); + + const logoWidth = isUnlocked() + ? env.branding.logo?.navbar?.width || 98 + : 98; + const logoHeight = isUnlocked() + ? env.branding.logo?.navbar?.height || 32 + : 32; useEffect(() => { function getPath() { @@ -50,12 +56,8 @@ export function LayoutHeader({ showTopBar }: LayoutHeaderProps) {
{/* {build === "saas" && ( diff --git a/src/components/LayoutSidebar.tsx b/src/components/LayoutSidebar.tsx index 2e4ec4ce..fb1902a8 100644 --- a/src/components/LayoutSidebar.tsx +++ b/src/components/LayoutSidebar.tsx @@ -67,6 +67,9 @@ export function LayoutSidebar({ }, [isSidebarCollapsed]); function loadFooterLinks(): { text: string; href?: string }[] | undefined { + if (!isUnlocked()) { + return undefined; + } if (env.branding.footer) { try { return JSON.parse(env.branding.footer); diff --git a/src/components/ResourceAuthPortal.tsx b/src/components/ResourceAuthPortal.tsx index 65498ae0..6668a787 100644 --- a/src/components/ResourceAuthPortal.tsx +++ b/src/components/ResourceAuthPortal.tsx @@ -48,6 +48,7 @@ import BrandingLogo from "@app/components/BrandingLogo"; import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; import { useTranslations } from "next-intl"; import { build } from "@server/build"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; const pinSchema = z.object({ pin: z @@ -92,6 +93,7 @@ type ResourceAuthPortalProps = { export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { const router = useRouter(); const t = useTranslations(); + const { isUnlocked } = useLicenseStatusContext(); const getNumMethods = () => { let colLength = 0; @@ -308,14 +310,22 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { } function getTitle() { - if (build !== "oss" && env.branding.resourceAuthPage?.titleText) { + if ( + isUnlocked() && + build !== "oss" && + env.branding.resourceAuthPage?.titleText + ) { return env.branding.resourceAuthPage.titleText; } return t("authenticationRequired"); } function getSubtitle(resourceName: string) { - if (build !== "oss" && env.branding.resourceAuthPage?.subtitleText) { + if ( + isUnlocked() && + build !== "oss" && + env.branding.resourceAuthPage?.subtitleText + ) { return env.branding.resourceAuthPage.subtitleText .split("{{resourceName}}") .join(resourceName); @@ -325,11 +335,14 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { : t("authenticationRequest", { name: resourceName }); } + const logoWidth = isUnlocked() ? env.branding.logo?.authPage?.width || 100 : 100; + const logoHeight = isUnlocked() ? env.branding.logo?.authPage?.height || 100 : 100; + return (
{!accessDenied ? (
- {build === "enterprise" ? ( + {isUnlocked() && build === "enterprise" ? ( !env.branding.resourceAuthPage?.hidePoweredBy && (
@@ -362,18 +375,13 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) { )} - {build !== "oss" && + {isUnlocked() && + build !== "oss" && env.branding?.resourceAuthPage?.showLogo && (
)} diff --git a/src/components/SignupForm.tsx b/src/components/SignupForm.tsx index 8e13d770..fb81846c 100644 --- a/src/components/SignupForm.tsx +++ b/src/components/SignupForm.tsx @@ -18,9 +18,7 @@ import { import { Card, CardContent, - CardDescription, CardHeader, - CardTitle } from "@/components/ui/card"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Progress } from "@/components/ui/progress"; @@ -31,13 +29,13 @@ import { AxiosResponse } from "axios"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; -import Image from "next/image"; import { cleanRedirect } from "@app/lib/cleanRedirect"; import { useTranslations } from "next-intl"; import BrandingLogo from "@app/components/BrandingLogo"; import { build } from "@server/build"; import { Check, X } from "lucide-react"; import { cn } from "@app/lib/cn"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; // Password strength calculation const calculatePasswordStrength = (password: string) => { @@ -111,6 +109,7 @@ export default function SignupForm({ const { env } = useEnvContext(); const api = createApiClient({ env }); const t = useTranslations(); + const { isUnlocked } = useLicenseStatusContext(); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -192,14 +191,18 @@ export default function SignupForm({ } }; + const logoWidth = isUnlocked() + ? env.branding.logo?.authPage?.width || 175 + : 175; + const logoHeight = isUnlocked() + ? env.branding.logo?.authPage?.height || 58 + : 58; + return (
- +

{getSubtitle()}

diff --git a/src/components/private/SplashImage.tsx b/src/components/private/SplashImage.tsx index c6ddc466..ad9d5069 100644 --- a/src/components/private/SplashImage.tsx +++ b/src/components/private/SplashImage.tsx @@ -3,6 +3,7 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { usePathname } from "next/navigation"; import Image from "next/image"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; type SplashImageProps = { children: React.ReactNode; @@ -11,8 +12,12 @@ type SplashImageProps = { export default function SplashImage({ children }: SplashImageProps) { const pathname = usePathname(); const { env } = useEnvContext(); + const { isUnlocked } = useLicenseStatusContext(); function showBackgroundImage() { + if (!isUnlocked()) { + return false; + } if (!env.branding.background_image_path) { return false; } diff --git a/src/providers/LicenseStatusProvider.tsx b/src/providers/LicenseStatusProvider.tsx index 1196128d..9b6064af 100644 --- a/src/providers/LicenseStatusProvider.tsx +++ b/src/providers/LicenseStatusProvider.tsx @@ -1,6 +1,7 @@ "use client"; import LicenseStatusContext from "@app/contexts/licenseStatusContext"; +import { build } from "@server/build"; import { LicenseStatus } from "@server/license/license"; import { useState } from "react";