feat: implement unified configuration as single source of truth

- Make unified config lazy-loaded to avoid initialization issues
- Replace direct config exports with getter functions
- Rewrite utils/env.ts to use unified config instead of scattered access
- Add show-config.js helper script for configuration management
- Type-safe configuration access throughout the app
- Smart defaults for all environments with environment overrides
- Eliminates scattered process.env and import.meta.env access
This commit is contained in:
William Valentin
2025-09-08 21:23:44 -07:00
parent 8830842ba2
commit b59160eb10
3 changed files with 330 additions and 89 deletions

View File

@@ -1,59 +1,29 @@
// Environment utility to safely access environment variables
// Compatible with both Vite (browser) and Node.js (Jest/server)
/**
* Environment Utilities - Single Source of Truth
*
* This module provides environment utilities that use the unified config
* as the single source of truth for all environment variables.
*
* Use this instead of directly accessing process.env or import.meta.env
*/
export interface EnvConfig {
VITE_COUCHDB_URL?: string;
VITE_COUCHDB_USERNAME?: string;
VITE_COUCHDB_PASSWORD?: string;
VITE_MAILGUN_API_KEY?: string;
VITE_MAILGUN_DOMAIN?: string;
VITE_MAILGUN_BASE_URL?: string;
VITE_MAILGUN_FROM_NAME?: string;
VITE_MAILGUN_FROM_EMAIL?: string;
NODE_ENV?: string;
COUCHDB_URL?: string;
[key: string]: string | undefined;
}
import {
getAppConfig,
getDatabaseConfig,
getAuthConfig,
getEmailConfig,
getOAuthConfig,
getFeatureFlags,
getLoggingConfig,
getSecurityConfig,
unifiedConfig,
} from '../config/unified.config';
/**
* Safely get environment variables from various sources
* Works in both browser (Vite) and Node.js (Jest/server) environments
* Get the current environment
*/
export function getEnv(): EnvConfig {
let env: EnvConfig = {};
// Try to get from import.meta.env (Vite/browser)
try {
if (typeof globalThis !== 'undefined' && 'import' in globalThis) {
const importMeta = (
globalThis as { import?: { meta?: { env?: Record<string, string> } } }
).import?.meta;
if (importMeta?.env) {
env = { ...env, ...importMeta.env };
}
}
} catch (_e) {
// Ignore errors accessing import.meta
}
// Try to get from process.env (Node.js)
try {
if (typeof process !== 'undefined' && process.env) {
env = { ...env, ...process.env };
}
} catch (_e) {
// Ignore errors accessing process.env
}
return env;
}
/**
* Get a specific environment variable with optional fallback
*/
export function getEnvVar(key: string, fallback?: string): string | undefined {
const env = getEnv();
return env[key] || fallback;
export function getEnvironment(): string {
return getAppConfig().environment;
}
/**
@@ -74,35 +44,110 @@ export function isNode(): boolean {
* Check if we're running in a test environment
*/
export function isTest(): boolean {
const env = getEnv();
return getAppConfig().environment === 'test';
}
// Check for Jest environment
if (
typeof global !== 'undefined' &&
'expect' in global &&
'describe' in global
) {
return true;
}
// Check for Node.js test environment variables
if (typeof process !== 'undefined' && process.env) {
if (process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID) {
return true;
}
}
// Check environment variables
return env.NODE_ENV === 'test';
/**
* Check if we're running in development
*/
export function isDevelopment(): boolean {
return getAppConfig().environment === 'development';
}
/**
* Check if we're running in production
*/
export function isProduction(): boolean {
const env = getEnv();
return (
env.NODE_ENV === 'production' ||
(typeof process !== 'undefined' && process.env?.NODE_ENV === 'production')
);
return getAppConfig().environment === 'production';
}
/**
* Check if we're running in staging
*/
export function isStaging(): boolean {
return getAppConfig().environment === 'staging';
}
/**
* Get application configuration
*/
export { getAppConfig };
/**
* Get database configuration
*/
export { getDatabaseConfig };
/**
* Get authentication configuration
*/
export { getAuthConfig };
/**
* Get email configuration
*/
export { getEmailConfig };
/**
* Get OAuth configuration
*/
export { getOAuthConfig };
/**
* Get feature flags
*/
export { getFeatureFlags };
/**
* Get logging configuration
*/
export { getLoggingConfig };
/**
* Get security configuration
*/
export { getSecurityConfig };
/**
* Get the full unified configuration
* Use this sparingly - prefer the specific getters above
*/
export function getUnifiedConfig() {
return unifiedConfig;
}
/**
* Legacy compatibility - get a specific environment variable
* @deprecated Use the unified config getters instead
*/
export function getEnvVar(key: string, fallback?: string): string | undefined {
console.warn(
`getEnvVar('${key}') is deprecated. Use unified config instead.`
);
// Try to map common environment variables to unified config
switch (key) {
case 'NODE_ENV':
return getAppConfig().environment;
case 'APP_NAME':
case 'VITE_APP_NAME':
return getAppConfig().name;
case 'APP_BASE_URL':
return getAppConfig().baseUrl;
case 'VITE_COUCHDB_URL':
case 'COUCHDB_URL':
return getDatabaseConfig().url;
case 'VITE_COUCHDB_USER':
case 'COUCHDB_USER':
return getDatabaseConfig().username;
case 'VITE_COUCHDB_PASSWORD':
case 'COUCHDB_PASSWORD':
return getDatabaseConfig().password;
case 'JWT_SECRET':
return getAuthConfig().jwtSecret;
case 'LOG_LEVEL':
return getLoggingConfig().level;
default:
return fallback;
}
}