Plugin & Theme System: - Add plugin type definitions, permissions (30+), and validation constants - Add IndexedDB storage layer for plugin code, theme CSS, and previews - Add theme CSS sanitization, injection, and safety validation - Add HookBus event system with 130+ hooks across 20 domains - Add plugin ZIP extraction and manifest validation with JS security checks - Add sandboxed PluginAPI factory with scoped storage, logging, and permission gating - Add plugin loader with blob URL dynamic import and auto-disable circuit breaker - Add 3 built-in themes (Nord, Catppuccin, Solarized) - Add Zustand plugin store with install/uninstall/enable/disable lifecycle - Add PluginSlot, PluginSlotRenderer, and PluginErrorBoundary components - Add plugins and themes settings UI panels - Integrate plugin slots into email viewer, composer, navigation rail, sidebar, and context menu - Extend theme store with custom theme installation and activation Admin Dashboard: - Add admin authentication with scrypt password hashing and AES-256-GCM sessions - Add rate-limited login (5 attempts/15min per IP) - Add config manager with admin override > env var > default priority - Add settings policy system with feature gates and per-setting restrictions - Add audit logging with rotation - Add admin API routes (login, logout, config, policy, audit, password change) - Add admin UI pages (login, dashboard, config, policy, audit) - Add policy store for client-side feature gate enforcement - Wire admin password initialization into server instrumentation Tests: - Add 139 tests across 10 test files covering all plugin/theme modules
55 lines
2.7 KiB
TypeScript
55 lines
2.7 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { logger } from '@/lib/logger';
|
|
import { configManager } from '@/lib/admin/config-manager';
|
|
|
|
/**
|
|
* Runtime configuration endpoint
|
|
*
|
|
* This endpoint serves configuration values that can be set at runtime
|
|
* via environment variables or admin dashboard overrides, enabling
|
|
* post-build configuration for Docker deployments.
|
|
*
|
|
* Priority order:
|
|
* 1. Admin dashboard overrides (data/admin/config.json)
|
|
* 2. Runtime env vars (APP_NAME, JMAP_SERVER_URL)
|
|
* 3. Build-time env vars (NEXT_PUBLIC_APP_NAME, NEXT_PUBLIC_JMAP_SERVER_URL)
|
|
* 4. Default values
|
|
*/
|
|
export async function GET() {
|
|
logger.debug('Config requested');
|
|
await configManager.ensureLoaded();
|
|
|
|
const appName = configManager.get<string>('appName') || process.env.NEXT_PUBLIC_APP_NAME || 'Webmail';
|
|
const jmapServerUrl = configManager.get<string>('jmapServerUrl') || process.env.NEXT_PUBLIC_JMAP_SERVER_URL || '';
|
|
const oauthEnabled = configManager.get<boolean>('oauthEnabled', false);
|
|
const oauthOnly = oauthEnabled && configManager.get<boolean>('oauthOnly', false);
|
|
const stalwartFeaturesEnabled = configManager.get<boolean>('stalwartFeaturesEnabled', true);
|
|
const allowedFrameAncestors = configManager.get<string>('allowedFrameAncestors', '');
|
|
|
|
return NextResponse.json({
|
|
appName,
|
|
jmapServerUrl,
|
|
oauthEnabled,
|
|
oauthOnly,
|
|
oauthClientId: configManager.get<string>('oauthClientId', ''),
|
|
oauthIssuerUrl: configManager.get<string>('oauthIssuerUrl', ''),
|
|
rememberMeEnabled: !!process.env.SESSION_SECRET,
|
|
settingsSyncEnabled: configManager.get<boolean>('settingsSyncEnabled', false) && !!process.env.SESSION_SECRET,
|
|
stalwartFeaturesEnabled,
|
|
devMode: configManager.get<boolean>('devMode', false),
|
|
faviconUrl: configManager.get<string>('faviconUrl', '/branding/Bulwark_Favicon.svg'),
|
|
appLogoLightUrl: configManager.get<string>('appLogoLightUrl', ''),
|
|
appLogoDarkUrl: configManager.get<string>('appLogoDarkUrl', ''),
|
|
loginLogoLightUrl: configManager.get<string>('loginLogoLightUrl', '/branding/Bulwark_Logo_Color.svg'),
|
|
loginLogoDarkUrl: configManager.get<string>('loginLogoDarkUrl', '/branding/Bulwark_Logo_White.svg'),
|
|
loginCompanyName: configManager.get<string>('loginCompanyName', ''),
|
|
loginImprintUrl: configManager.get<string>('loginImprintUrl', ''),
|
|
loginPrivacyPolicyUrl: configManager.get<string>('loginPrivacyPolicyUrl', ''),
|
|
loginWebsiteUrl: configManager.get<string>('loginWebsiteUrl', ''),
|
|
demoMode: configManager.get<boolean>('demoMode', false),
|
|
autoSsoEnabled: configManager.get<boolean>('autoSsoEnabled', false),
|
|
embeddedMode: !!allowedFrameAncestors && allowedFrameAncestors !== "'none'",
|
|
parentOrigin: configManager.get<string>('parentOrigin', ''),
|
|
});
|
|
}
|