import { z } from 'zod'; const schema = z.object({ NODE_ENV: z.enum(['development', 'test', 'production']).default('development'), PORT: z.coerce.number().int().positive().default(3000), HOST: z.string().default('0.0.0.0'), DATABASE_URL: z.string().min(1, 'DATABASE_URL fehlt'), SESSION_SECRET: z.string().min(32, 'SESSION_SECRET muss mindestens 32 Zeichen lang sein'), // Lebensdauer einer Session in Stunden. SESSION_TTL_HOURS: z.coerce.number().int().positive().default(12), // Wird beim ersten Start angelegt, solange noch kein Account existiert. BOOTSTRAP_ADMIN_USER: z.string().min(3).default('admin'), BOOTSTRAP_ADMIN_PASSWORD: z.string().min(12).optional(), // Hinter einem Reverse Proxy nötig, damit Client-IPs im Audit-Log stimmen. TRUST_PROXY: z .string() .default('true') .transform((v) => v === 'true' || v === '1'), // Muss in Produktion true sein (Cookies nur über HTTPS). COOKIE_SECURE: z .string() .optional() .transform((v) => (v === undefined ? undefined : v === 'true' || v === '1')), // Basis-Adresse der GExtension-Instanz. Der Parser ist auf deren Aufbau // zugeschnitten; eine völlig andere Software ließe sich damit nicht lesen. GEXTENSION_BASE_URL: z.string().url().default('https://gextension.senseigaming.de'), // Basis-Adresse des Forums für den Antrags-Abgleich. Der Parser ist auf // WoltLab (Burning Board) zugeschnitten. FORUM_BASE_URL: z.string().url().default('https://senseigaming.de/forum'), // Aufbewahrung der Audit-Einträge in Tagen. 0 schaltet das Aufräumen ab. // Die Guthaben-Historie ist davon nicht betroffen, die bleibt dauerhaft. AUDIT_RETENTION_DAYS: z.coerce.number().int().min(0).default(365), LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'), }); const parsed = schema.safeParse(process.env); if (!parsed.success) { const issues = parsed.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\n'); console.error(`Konfiguration unvollständig:\n${issues}`); process.exit(1); } const raw = parsed.data; export const env = { ...raw, isProduction: raw.NODE_ENV === 'production', // In Produktion ohne expliziten Wert: sichere Cookies erzwingen. cookieSecure: raw.COOKIE_SECURE ?? raw.NODE_ENV === 'production', }; export type Env = typeof env;