import { randomBytes } from 'node:crypto'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import dotenv from 'dotenv'; const here = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(here, '../../..'); dotenv.config({ path: path.join(repoRoot, '.env'), quiet: true }); function required(name: string, fallback?: string): string { const value = process.env[name] ?? fallback; if (!value) { throw new Error( `Variable d'environnement manquante : ${name}. Copie .env.example vers .env et complète-la.`, ); } return value; } function int(name: string, fallback: number): number { const raw = process.env[name]; if (!raw) return fallback; const parsed = Number.parseInt(raw, 10); return Number.isFinite(parsed) ? parsed : fallback; } const isDev = process.env.NODE_ENV !== 'production'; export const config = { port: int('PORT', 8080), host: process.env.HOST ?? '0.0.0.0', isDev, adminPassword: required('ADMIN_PASSWORD', isDev ? 'admin' : undefined), sessionSecret: required( 'SESSION_SECRET', isDev ? randomBytes(32).toString('hex') : undefined, ), sessionTtlMs: int('SESSION_TTL_MS', 12 * 60 * 60 * 1000), /** Vide = enrôlement automatique désactivé. */ enrollmentToken: process.env.ENROLLMENT_TOKEN ?? '', dbPath: path.resolve(repoRoot, process.env.DB_PATH ?? './data/stream-control.sqlite'), statusIntervalMs: int('STATUS_INTERVAL_MS', 2000), agentTimeoutMs: int('AGENT_TIMEOUT_MS', 15_000), commandTimeoutMs: int('COMMAND_TIMEOUT_MS', 15_000), /** Nombre d'entrées de journal conservées en base. */ logRetention: int('LOG_RETENTION', 2000), webDist: path.resolve(repoRoot, 'packages/web/dist'), } as const;