import { z } from 'zod' /** * Umgebungsvariablen werden beim Start einmal geprueft. Fehlt etwas, soll der * Container sofort und mit klarer Meldung sterben statt spaeter beim ersten * Request. Die Werte stehen im .env-Reiter von Dockge, Vorlage ist .env.example. */ const schema = z.object({ NODE_ENV: z.enum(['development', 'test', 'production']).default('development'), DATABASE_URL: z.string().min(1), DIRECT_DATABASE_URL: z.string().min(1).optional(), APP_URL: z.url(), AUTH_SECRET: z.string().min(32), DISCORD_CLIENT_ID: z.string().min(1), DISCORD_CLIENT_SECRET: z.string().min(1), DISCORD_BOT_TOKEN: z.string().min(1), DISCORD_GUILD_ID: z.string().min(1), /// Notfallzugang. Diese Discord-IDs haben immer Stufe 5 und alle Rechte, /// unabhaengig vom Rollen-Mapping. Siehe docs/03-auth-und-rechte.md. BOOTSTRAP_ADMIN_IDS: z .string() .default('') .transform((v) => v.split(',').map((s) => s.trim()).filter(Boolean)), FILES_DIR: z.string().default('/data/files'), /** `host:port` oder Pfad zu einem Unix-Socket. Siehe lib/files/clamav.ts. */ CLAMAV_ADDRESS: z.string().default('clamav:3310'), PDF_SERVICE_URL: z.url().optional(), TZ: z.string().default('Europe/Berlin'), }) export type Env = z.infer let cached: Env | null = null export function getEnv(): Env { if (cached) return cached const parsed = schema.safeParse(process.env) if (!parsed.success) { const missing = parsed.error.issues .map((i) => `${i.path.join('.')}: ${i.message}`) .join('\n ') throw new Error(`Konfiguration unvollständig:\n ${missing}`) } cached = parsed.data return cached } /** Nur fuer Tests, damit eine geaenderte Umgebung neu gelesen wird. */ export function resetEnvCache(): void { cached = null }