const path = require('path'); const dotenv = require('dotenv'); dotenv.config({ path: path.resolve(process.cwd(), '.env') }); const requireEnv = (name) => { const value = process.env[name]; if (!value) { throw new Error(`Missing required environment variable: ${name}`); } return value; }; const parseDurationMs = (value, fallbackMs) => { if (!value) return fallbackMs; const match = String(value).trim().match(/^(\d+)(ms|s|m|h|d)?$/i); if (!match) return fallbackMs; const amount = Number(match[1]); const unit = (match[2] || 'ms').toLowerCase(); const multipliers = { ms: 1, s: 1000, m: 60 * 1000, h: 60 * 60 * 1000, d: 24 * 60 * 60 * 1000, }; return amount * multipliers[unit]; }; if (process.env.NODE_ENV === 'production') { [ 'JWT_SECRET', 'CORS_ORIGIN', 'FRONTEND_URL', 'DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASSWORD', 'STORAGE_PATH', 'MAIL_HOST', 'MAIL_PORT', 'MAIL_USER', 'MAIL_PASS', 'MAIL_FROM', ].forEach(requireEnv); if (process.env.JWT_SECRET === 'change_me') { throw new Error('JWT_SECRET cannot use the default insecure value in production.'); } if ((process.env.FRONTEND_URL || '').includes('localhost')) { throw new Error('FRONTEND_URL must not point to localhost in production.'); } } const env = { port: Number(process.env.PORT) || 4000, nodeEnv: process.env.NODE_ENV || 'development', corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:5173', db: { host: process.env.DB_HOST || '127.0.0.1', port: Number(process.env.DB_PORT) || 3306, name: process.env.DB_NAME || 'signdb', user: process.env.DB_USER || 'SignLinked', password: process.env.DB_PASSWORD || '', }, jwt: { secret: process.env.JWT_SECRET || 'change_me', accessExpires: process.env.JWT_ACCESS_EXPIRES || '15m', refreshExpires: process.env.JWT_REFRESH_EXPIRES || '15m', refreshExpiresMs: parseDurationMs(process.env.JWT_REFRESH_EXPIRES || '15m', 15 * 60 * 1000), biometricExpires: process.env.JWT_BIOMETRIC_EXPIRES || '180d', biometricExpiresMs: parseDurationMs(process.env.JWT_BIOMETRIC_EXPIRES || '180d', 180 * 24 * 60 * 60 * 1000), }, storagePath: process.env.STORAGE_PATH || '/tmp/linkedsign_storage', p12: { defaultPath: process.env.P12_DEFAULT_PATH || null, defaultPassword: process.env.P12_DEFAULT_PASSWORD || '', }, mail: { host: process.env.MAIL_HOST || 'smtp.gmail.com', port: Number(process.env.MAIL_PORT) || 587, secure: process.env.MAIL_SECURE === 'true', rejectUnauthorized: process.env.MAIL_TLS_REJECT_UNAUTHORIZED !== 'false', user: process.env.MAIL_USER || '', pass: process.env.MAIL_PASS || '', from: process.env.MAIL_FROM || 'noreply@linkedsign.local', }, legal: { controllerName: process.env.LEGAL_CONTROLLER_NAME || 'LinkedCorp', platformName: process.env.LEGAL_PLATFORM_NAME || 'LinkedSign', contactEmail: process.env.LEGAL_CONTACT_EMAIL || process.env.MAIL_FROM || 'sign@linkedcorp.net', contactPhone: process.env.LEGAL_CONTACT_PHONE || 'No informado en este entorno', contactAddress: process.env.LEGAL_CONTACT_ADDRESS || 'No informado en este entorno', website: process.env.LEGAL_WEBSITE || process.env.FRONTEND_URL || 'http://localhost:5173', rightsEmail: process.env.LEGAL_RIGHTS_EMAIL || process.env.MAIL_FROM || 'sign@linkedcorp.net', }, frontendUrl: process.env.FRONTEND_URL || 'http://localhost:5173', refreshCookieName: process.env.REFRESH_COOKIE_NAME || 'linkedsign_refresh', androidApkPath: process.env.ANDROID_APK_PATH || path.resolve(process.cwd(), '..', 'android', 'apk', 'LinkedSign-debug.apk'), }; module.exports = env;