diff --git a/.env.example b/.env.example index 60c77c5d..31ee2b4b 100644 --- a/.env.example +++ b/.env.example @@ -76,8 +76,16 @@ JMAP_SERVER_URL=https://your-jmap-server.com # Login Page Customization (all optional) # ============================================================================= +# Custom logo images for the login page (PNG, SVG, etc.) +# Can be absolute URLs or paths relative to the public/ directory. +# Light mode logo (shown on light backgrounds), defaults to Bulwark logo. +LOGIN_LOGO_LIGHT_URL=/branding/Bulwark_Logo_Color.svg +# +# Dark mode logo (shown on dark backgrounds), defaults to Bulwark white logo. +LOGIN_LOGO_DARK_URL=/branding/Bulwark_Logo_Color.svg + # Company or organization name displayed above the version on the login page -# LOGIN_COMPANY_NAME=My Company +LOGIN_COMPANY_NAME=Bulwark Webmail # URL for the imprint/legal notice link on the login page # LOGIN_IMPRINT_URL=https://example.com/imprint @@ -86,7 +94,7 @@ JMAP_SERVER_URL=https://your-jmap-server.com # LOGIN_PRIVACY_POLICY_URL=https://example.com/privacy # URL for the company website link on the login page -# LOGIN_WEBSITE_URL=https://example.com +LOGIN_WEBSITE_URL=https://bulwarkmail.org # ============================================================================= # Legacy Build-time Variables (still supported as fallback) diff --git a/app/[locale]/login/page.tsx b/app/[locale]/login/page.tsx index 99433236..75fab314 100644 --- a/app/[locale]/login/page.tsx +++ b/app/[locale]/login/page.tsx @@ -28,8 +28,9 @@ export default function LoginPage() { const t = useTranslations("login"); const params = useParams(); const { login, isLoading, error, clearError, isAuthenticated } = useAuthStore(); - const { theme, setTheme, initializeTheme } = useThemeStore(); - const { appName, jmapServerUrl: serverUrl, oauthEnabled, oauthOnly, oauthClientId, oauthIssuerUrl, rememberMeEnabled, devMode, loginCompanyName, loginImprintUrl, loginPrivacyPolicyUrl, loginWebsiteUrl, isLoading: configLoading, error: configError } = useConfig(); + const { theme, setTheme, initializeTheme } = useThemeStore((s) => ({ theme: s.theme, setTheme: s.setTheme, initializeTheme: s.initializeTheme })); + const { appName, jmapServerUrl: serverUrl, oauthEnabled, oauthOnly, oauthClientId, oauthIssuerUrl, rememberMeEnabled, devMode, loginLogoLightUrl, loginLogoDarkUrl, loginCompanyName, loginImprintUrl, loginPrivacyPolicyUrl, loginWebsiteUrl, isLoading: configLoading, error: configError } = useConfig(); + const resolvedTheme = useThemeStore((s) => s.resolvedTheme); const [formData, setFormData] = useState({ username: "", @@ -416,8 +417,12 @@ export default function LoginPage() {
{/* Header section with logo */}
-
- +
+ {appName}

{appName} diff --git a/app/api/config/route.ts b/app/api/config/route.ts index c5bde963..92cb4b1d 100644 --- a/app/api/config/route.ts +++ b/app/api/config/route.ts @@ -26,6 +26,8 @@ export async function GET() { settingsSyncEnabled: process.env.SETTINGS_SYNC_ENABLED === 'true' && !!process.env.SESSION_SECRET, stalwartFeaturesEnabled: process.env.STALWART_FEATURES !== 'false', devMode: process.env.DEV_MOCK_JMAP === 'true', + loginLogoLightUrl: process.env.LOGIN_LOGO_LIGHT_URL || '/branding/Bulwark_Logo_Color.svg', + loginLogoDarkUrl: process.env.LOGIN_LOGO_DARK_URL || '/branding/Bulwark_Logo_White.svg', loginCompanyName: process.env.LOGIN_COMPANY_NAME || '', loginImprintUrl: process.env.LOGIN_IMPRINT_URL || '', loginPrivacyPolicyUrl: process.env.LOGIN_PRIVACY_POLICY_URL || '', diff --git a/hooks/use-config.ts b/hooks/use-config.ts index 31fc5394..1e3bf653 100644 --- a/hooks/use-config.ts +++ b/hooks/use-config.ts @@ -13,6 +13,8 @@ interface ConfigData { settingsSyncEnabled: boolean; stalwartFeaturesEnabled: boolean; devMode: boolean; + loginLogoLightUrl: string; + loginLogoDarkUrl: string; loginCompanyName: string; loginImprintUrl: string; loginPrivacyPolicyUrl: string; @@ -77,6 +79,8 @@ export function useConfig(): AppConfig { settingsSyncEnabled: configCache?.settingsSyncEnabled || false, stalwartFeaturesEnabled: configCache?.stalwartFeaturesEnabled ?? true, devMode: configCache?.devMode || false, + loginLogoLightUrl: configCache?.loginLogoLightUrl || '/branding/Bulwark_Logo_Color.svg', + loginLogoDarkUrl: configCache?.loginLogoDarkUrl || '/branding/Bulwark_Logo_White.svg', loginCompanyName: configCache?.loginCompanyName || '', loginImprintUrl: configCache?.loginImprintUrl || '', loginPrivacyPolicyUrl: configCache?.loginPrivacyPolicyUrl || '', @@ -99,6 +103,8 @@ export function useConfig(): AppConfig { settingsSyncEnabled: configCache.settingsSyncEnabled, stalwartFeaturesEnabled: configCache.stalwartFeaturesEnabled, devMode: configCache.devMode, + loginLogoLightUrl: configCache.loginLogoLightUrl, + loginLogoDarkUrl: configCache.loginLogoDarkUrl, loginCompanyName: configCache.loginCompanyName, loginImprintUrl: configCache.loginImprintUrl, loginPrivacyPolicyUrl: configCache.loginPrivacyPolicyUrl, @@ -122,6 +128,8 @@ export function useConfig(): AppConfig { settingsSyncEnabled: data.settingsSyncEnabled, stalwartFeaturesEnabled: data.stalwartFeaturesEnabled, devMode: data.devMode, + loginLogoLightUrl: data.loginLogoLightUrl, + loginLogoDarkUrl: data.loginLogoDarkUrl, loginCompanyName: data.loginCompanyName, loginImprintUrl: data.loginImprintUrl, loginPrivacyPolicyUrl: data.loginPrivacyPolicyUrl, diff --git a/public/branding/Bulwark Favicon.png b/public/branding/Bulwark_Favicon.png similarity index 100% rename from public/branding/Bulwark Favicon.png rename to public/branding/Bulwark_Favicon.png diff --git a/public/branding/Bulwark Favicon.svg b/public/branding/Bulwark_Favicon.svg similarity index 100% rename from public/branding/Bulwark Favicon.svg rename to public/branding/Bulwark_Favicon.svg diff --git a/public/branding/Bulwark Logo Color.png b/public/branding/Bulwark_Logo_Color.png similarity index 100% rename from public/branding/Bulwark Logo Color.png rename to public/branding/Bulwark_Logo_Color.png diff --git a/public/branding/Bulwark Logo Color.svg b/public/branding/Bulwark_Logo_Color.svg similarity index 100% rename from public/branding/Bulwark Logo Color.svg rename to public/branding/Bulwark_Logo_Color.svg diff --git a/public/branding/Bulwark Logo Dark.png b/public/branding/Bulwark_Logo_Dark.png similarity index 100% rename from public/branding/Bulwark Logo Dark.png rename to public/branding/Bulwark_Logo_Dark.png diff --git a/public/branding/Bulwark Logo Dark.svg b/public/branding/Bulwark_Logo_Dark.svg similarity index 100% rename from public/branding/Bulwark Logo Dark.svg rename to public/branding/Bulwark_Logo_Dark.svg diff --git a/public/branding/Bulwark Logo White.png b/public/branding/Bulwark_Logo_White.png similarity index 100% rename from public/branding/Bulwark Logo White.png rename to public/branding/Bulwark_Logo_White.png diff --git a/public/branding/Bulwark Logo White.svg b/public/branding/Bulwark_Logo_White.svg similarity index 100% rename from public/branding/Bulwark Logo White.svg rename to public/branding/Bulwark_Logo_White.svg diff --git a/public/branding/Bulwark Logo with Lettering Dark Color.svg b/public/branding/Bulwark_Logo_with_Lettering_Dark_Color.svg similarity index 100% rename from public/branding/Bulwark Logo with Lettering Dark Color.svg rename to public/branding/Bulwark_Logo_with_Lettering_Dark_Color.svg diff --git a/public/branding/Bulwark Logo with Lettering Dark and Color.png b/public/branding/Bulwark_Logo_with_Lettering_Dark_and_Color.png similarity index 100% rename from public/branding/Bulwark Logo with Lettering Dark and Color.png rename to public/branding/Bulwark_Logo_with_Lettering_Dark_and_Color.png diff --git a/public/branding/Bulwark Logo with Lettering White and Color.png b/public/branding/Bulwark_Logo_with_Lettering_White_and_Color.png similarity index 100% rename from public/branding/Bulwark Logo with Lettering White and Color.png rename to public/branding/Bulwark_Logo_with_Lettering_White_and_Color.png diff --git a/public/branding/Bulwark Logo with Lettering White and Color.svg b/public/branding/Bulwark_Logo_with_Lettering_White_and_Color.svg similarity index 100% rename from public/branding/Bulwark Logo with Lettering White and Color.svg rename to public/branding/Bulwark_Logo_with_Lettering_White_and_Color.svg diff --git a/setup.sh b/setup.sh index 891c7a13..952e51eb 100644 --- a/setup.sh +++ b/setup.sh @@ -65,6 +65,8 @@ CFG_SETTINGS_DATA_DIR="./data/settings" CFG_LOG_FORMAT="text" CFG_LOG_LEVEL="info" CFG_LOGIN_COMPANY_NAME="" +CFG_LOGIN_LOGO_LIGHT_URL="" +CFG_LOGIN_LOGO_DARK_URL="" CFG_LOGIN_IMPRINT_URL="" CFG_LOGIN_PRIVACY_POLICY_URL="" CFG_LOGIN_WEBSITE_URL="" @@ -768,13 +770,19 @@ screen_login_customization() { echo -e " ${DIM}Shown on the login page footer. Example: Acme Corp${RESET}" prompt_value "Company / organization name" "$CFG_LOGIN_COMPANY_NAME" "CFG_LOGIN_COMPANY_NAME" echo "" + echo -e " ${DIM}Custom logo URLs for the login page (PNG, SVG, etc.)${RESET}" + echo -e " ${DIM}Leave blank to use the default Bulwark logo.${RESET}" + prompt_value "Logo URL (light mode)" "$CFG_LOGIN_LOGO_LIGHT_URL" "CFG_LOGIN_LOGO_LIGHT_URL" + echo "" + prompt_value "Logo URL (dark mode)" "$CFG_LOGIN_LOGO_DARK_URL" "CFG_LOGIN_LOGO_DARK_URL" + echo "" prompt_value "Website URL" "$CFG_LOGIN_WEBSITE_URL" "CFG_LOGIN_WEBSITE_URL" echo "" prompt_value "Imprint / legal notice URL" "$CFG_LOGIN_IMPRINT_URL" "CFG_LOGIN_IMPRINT_URL" echo "" prompt_value "Privacy policy URL" "$CFG_LOGIN_PRIVACY_POLICY_URL" "CFG_LOGIN_PRIVACY_POLICY_URL" - if [[ -z "$CFG_LOGIN_COMPANY_NAME" && -z "$CFG_LOGIN_WEBSITE_URL" && -z "$CFG_LOGIN_IMPRINT_URL" && -z "$CFG_LOGIN_PRIVACY_POLICY_URL" ]]; then + if [[ -z "$CFG_LOGIN_COMPANY_NAME" && -z "$CFG_LOGIN_LOGO_LIGHT_URL" && -z "$CFG_LOGIN_LOGO_DARK_URL" && -z "$CFG_LOGIN_WEBSITE_URL" && -z "$CFG_LOGIN_IMPRINT_URL" && -z "$CFG_LOGIN_PRIVACY_POLICY_URL" ]]; then echo "" note "No branding configured. The login page will show defaults." fi @@ -884,9 +892,13 @@ screen_summary() { # Login page echo -e " ${CYAN}${BOLD}BRANDING${RESET}" - if [[ -n "$CFG_LOGIN_COMPANY_NAME" || -n "$CFG_LOGIN_WEBSITE_URL" || -n "$CFG_LOGIN_IMPRINT_URL" || -n "$CFG_LOGIN_PRIVACY_POLICY_URL" ]]; then + if [[ -n "$CFG_LOGIN_COMPANY_NAME" || -n "$CFG_LOGIN_LOGO_LIGHT_URL" || -n "$CFG_LOGIN_LOGO_DARK_URL" || -n "$CFG_LOGIN_WEBSITE_URL" || -n "$CFG_LOGIN_IMPRINT_URL" || -n "$CFG_LOGIN_PRIVACY_POLICY_URL" ]]; then [[ -n "$CFG_LOGIN_COMPANY_NAME" ]] && \ echo -e " Company Name .......... ${BOLD}${CFG_LOGIN_COMPANY_NAME}${RESET}" + [[ -n "$CFG_LOGIN_LOGO_LIGHT_URL" ]] && \ + echo -e " Logo (light mode) ..... ${BOLD}${CFG_LOGIN_LOGO_LIGHT_URL}${RESET}" + [[ -n "$CFG_LOGIN_LOGO_DARK_URL" ]] && \ + echo -e " Logo (dark mode) ...... ${BOLD}${CFG_LOGIN_LOGO_DARK_URL}${RESET}" [[ -n "$CFG_LOGIN_WEBSITE_URL" ]] && \ echo -e " Website URL ........... ${BOLD}${CFG_LOGIN_WEBSITE_URL}${RESET}" [[ -n "$CFG_LOGIN_IMPRINT_URL" ]] && \ @@ -995,6 +1007,8 @@ LOG_LEVEL=${CFG_LOG_LEVEL} ENVEOF [[ -n "$CFG_LOGIN_COMPANY_NAME" ]] && echo "LOGIN_COMPANY_NAME=${CFG_LOGIN_COMPANY_NAME}" >> "$ENV_FILE" + [[ -n "$CFG_LOGIN_LOGO_LIGHT_URL" ]] && echo "LOGIN_LOGO_LIGHT_URL=${CFG_LOGIN_LOGO_LIGHT_URL}" >> "$ENV_FILE" + [[ -n "$CFG_LOGIN_LOGO_DARK_URL" ]] && echo "LOGIN_LOGO_DARK_URL=${CFG_LOGIN_LOGO_DARK_URL}" >> "$ENV_FILE" [[ -n "$CFG_LOGIN_IMPRINT_URL" ]] && echo "LOGIN_IMPRINT_URL=${CFG_LOGIN_IMPRINT_URL}" >> "$ENV_FILE" [[ -n "$CFG_LOGIN_PRIVACY_POLICY_URL" ]] && echo "LOGIN_PRIVACY_POLICY_URL=${CFG_LOGIN_PRIVACY_POLICY_URL}" >> "$ENV_FILE" [[ -n "$CFG_LOGIN_WEBSITE_URL" ]] && echo "LOGIN_WEBSITE_URL=${CFG_LOGIN_WEBSITE_URL}" >> "$ENV_FILE" @@ -1204,6 +1218,8 @@ load_existing_config() { get_env_val "LOG_FORMAT"; [[ -n "$val" ]] && CFG_LOG_FORMAT="$val" get_env_val "LOG_LEVEL"; [[ -n "$val" ]] && CFG_LOG_LEVEL="$val" get_env_val "LOGIN_COMPANY_NAME"; [[ -n "$val" ]] && CFG_LOGIN_COMPANY_NAME="$val" + get_env_val "LOGIN_LOGO_LIGHT_URL"; [[ -n "$val" ]] && CFG_LOGIN_LOGO_LIGHT_URL="$val" + get_env_val "LOGIN_LOGO_DARK_URL"; [[ -n "$val" ]] && CFG_LOGIN_LOGO_DARK_URL="$val" get_env_val "LOGIN_IMPRINT_URL"; [[ -n "$val" ]] && CFG_LOGIN_IMPRINT_URL="$val" get_env_val "LOGIN_PRIVACY_POLICY_URL"; [[ -n "$val" ]] && CFG_LOGIN_PRIVACY_POLICY_URL="$val" get_env_val "LOGIN_WEBSITE_URL"; [[ -n "$val" ]] && CFG_LOGIN_WEBSITE_URL="$val"