feat: improve unified config system and build process

- Enhance unified config environment variable loading with better precedence
- Add environment-aware validation (production validation only when NODE_ENV=production)
- Add environment-specific build commands (build:dev, build:prod, build:staging)
- Improve configuration debugging with cleaner logging
- Remove unnecessary development warnings
- Provides more flexible and maintainable configuration system
This commit is contained in:
William Valentin
2025-09-08 20:43:02 -07:00
parent 9132d78bfa
commit 25b68ee67d
2 changed files with 36 additions and 29 deletions

View File

@@ -512,14 +512,24 @@ const defaultConfig: UnifiedConfig = {
/** /**
* Get environment variable with fallback * Get environment variable with fallback
* Reads from .env file via process.env or import.meta.env
*/ */
function getEnvVar(key: string, fallback: string = ''): string { function getEnvVar(key: string, fallback: string = ''): string {
if (typeof process !== 'undefined' && process.env) { // First try process.env (Node.js, includes .env via dotenv or build process)
return process.env[key] || fallback; if (typeof process !== 'undefined' && process.env && process.env[key]) {
return process.env[key]!;
} }
if (typeof import.meta !== 'undefined' && import.meta.env) {
return import.meta.env[key] || fallback; // Then try import.meta.env (Vite/browser, includes .env via Vite's loadEnv)
if (
typeof import.meta !== 'undefined' &&
import.meta.env &&
import.meta.env[key]
) {
return import.meta.env[key];
} }
// Use fallback if not found
return fallback; return fallback;
} }
@@ -543,7 +553,8 @@ function getNumberEnvVar(key: string, fallback: number = 0): number {
} }
/** /**
* Load configuration from environment variables * Load configuration from environment variables (.env file)
* This provides environment-specific overrides for the unified config
*/ */
function loadFromEnvironment(config: UnifiedConfig): UnifiedConfig { function loadFromEnvironment(config: UnifiedConfig): UnifiedConfig {
return { return {
@@ -701,8 +712,9 @@ function validateConfig(config: UnifiedConfig): void {
const errors: string[] = []; const errors: string[] = [];
const warnings: string[] = []; const warnings: string[] = [];
// Production-specific validations // Production-specific validations (only when NODE_ENV is explicitly production)
if (config.app.environment === 'production') { const isActualProduction = getEnvVar('NODE_ENV') === 'production';
if (config.app.environment === 'production' && isActualProduction) {
if (config.auth.jwtSecret === defaultConfig.auth.jwtSecret) { if (config.auth.jwtSecret === defaultConfig.auth.jwtSecret) {
errors.push('JWT_SECRET must be changed in production'); errors.push('JWT_SECRET must be changed in production');
} }
@@ -718,22 +730,6 @@ function validateConfig(config: UnifiedConfig): void {
} }
} }
// Development warnings for default secrets
if (config.app.environment === 'development') {
if (config.auth.jwtSecret === defaultConfig.auth.jwtSecret) {
warnings.push(
'Using default JWT_SECRET in development - change for production'
);
}
if (
config.security.sessionSecret === defaultConfig.security.sessionSecret
) {
warnings.push(
'Using default SESSION_SECRET in development - change for production'
);
}
}
// Email configuration validation // Email configuration validation
if ( if (
config.features.enableEmailVerification && config.features.enableEmailVerification &&
@@ -768,9 +764,10 @@ function validateConfig(config: UnifiedConfig): void {
/** /**
* Create unified configuration * Create unified configuration
* Uses .env file as primary source for environment-specific overrides
*/ */
export function createUnifiedConfig(): UnifiedConfig { export function createUnifiedConfig(): UnifiedConfig {
// Determine environment // Determine environment from .env file or NODE_ENV
const nodeEnv = getEnvVar('NODE_ENV', 'development') as Environment; const nodeEnv = getEnvVar('NODE_ENV', 'development') as Environment;
const environment = ['development', 'staging', 'production', 'test'].includes( const environment = ['development', 'staging', 'production', 'test'].includes(
nodeEnv nodeEnv
@@ -783,6 +780,7 @@ export function createUnifiedConfig(): UnifiedConfig {
/** /**
* Create unified configuration for specific environment * Create unified configuration for specific environment
* Priority: .env file overrides > environment-specific config > defaults
*/ */
export function createUnifiedConfigForEnvironment( export function createUnifiedConfigForEnvironment(
environment: Environment environment: Environment
@@ -791,15 +789,15 @@ export function createUnifiedConfigForEnvironment(
let config = { ...defaultConfig }; let config = { ...defaultConfig };
config.app.environment = environment; config.app.environment = environment;
// Apply environment-specific overrides // Apply environment-specific overrides (lower priority)
if (environmentConfigs[environment]) { if (environmentConfigs[environment]) {
config = deepMerge(config, environmentConfigs[environment]); config = deepMerge(config, environmentConfigs[environment]);
} }
// Apply environment variable overrides // Apply .env file overrides (highest priority)
config = loadFromEnvironment(config); config = loadFromEnvironment(config);
// Compute derived values // Compute derived values after all overrides
config.container.imageUrl = `${config.container.registry}/${config.container.repository}:${config.container.tag}`; config.container.imageUrl = `${config.container.registry}/${config.container.repository}:${config.container.tag}`;
// Validate configuration // Validate configuration
@@ -927,7 +925,7 @@ export function exportAsEnvVars(
*/ */
export function logConfig(): void { export function logConfig(): void {
if (unifiedConfig.features.debugMode) { if (unifiedConfig.features.debugMode) {
console.warn('🔧 Unified Configuration:', { console.warn('🔧 Unified Configuration (Single Source of Truth):', {
environment: unifiedConfig.app.environment, environment: unifiedConfig.app.environment,
app: unifiedConfig.app.name, app: unifiedConfig.app.name,
version: unifiedConfig.app.version, version: unifiedConfig.app.version,
@@ -936,7 +934,13 @@ export function logConfig(): void {
url: unifiedConfig.database.url, url: unifiedConfig.database.url,
useMock: unifiedConfig.database.useMock, useMock: unifiedConfig.database.useMock,
}, },
container: {
registry: unifiedConfig.container.registry,
repository: unifiedConfig.container.repository,
imageUrl: unifiedConfig.container.imageUrl,
},
features: unifiedConfig.features, features: unifiedConfig.features,
configSource: '.env file overrides applied',
}); });
} }
} }

View File

@@ -6,7 +6,10 @@
"scripts": { "scripts": {
"predev": "./scripts/process-html.sh", "predev": "./scripts/process-html.sh",
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "NODE_ENV=development vite build",
"build:prod": "NODE_ENV=production vite build",
"build:dev": "NODE_ENV=development vite build",
"build:staging": "NODE_ENV=staging vite build",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint . --ext .ts,.tsx", "lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix", "lint:fix": "eslint . --ext .ts,.tsx --fix",