import dotenv from 'dotenv'; import { z } from 'zod'; dotenv.config({ quiet: true }); const optionalText = z.preprocess( (value) => (value === '' || value === undefined ? undefined : value), z.string().optional(), ); const optionalUrl = z.preprocess( (value) => (value === '' || value === undefined ? undefined : value), z.string().url().optional(), ); const optionalPort = z.preprocess( (value) => (value === '' || value === undefined ? undefined : value), z.coerce.number().int().positive().optional(), ); const envSchema = z.object({ NODE_ENV: z.enum(['development', 'test', 'production']).default('development'), PORT: z.coerce.number().int().positive().default(4000), CORS_ORIGIN: z.string().min(1).default('http://localhost:5173'), DB_HOST: optionalText, DB_PORT: optionalPort, DB_USER: optionalText, DB_PASSWORD: optionalText, DB_NAME: optionalText, DB_SSL: z.preprocess( (value) => (value === '' || value === undefined ? undefined : value), z.enum(['true', 'false']).optional(), ), S3_BUCKET: optionalText, S3_REGION: optionalText, S3_ACCESS_KEY_ID: optionalText, S3_SECRET_ACCESS_KEY: optionalText, SENDGRID_API_KEY: optionalText, SENDGRID_SANDBOX_MODE: z.preprocess((value) => { if (value === undefined || value === '') { return false; } if (value === 'true' || value === true) { return true; } if (value === 'false' || value === false) { return false; } return value; }, z.boolean()), SENDGRID_WEBHOOK_VERIFICATION_KEY: optionalText, ADMIN_NOTIFY_EMAILS: optionalText, MAIL_FROM: optionalText, APP_BASE_URL: optionalUrl, JWT_SECRET: optionalText, TOKEN_PEPPER: optionalText, RECAPTCHA_SITE_KEY: optionalText, RECAPTCHA_SECRET_KEY: optionalText, CAPTCHA_ENABLED: z.preprocess((value) => { if (value === undefined || value === '') { return false; } if (value === 'true' || value === true) { return true; } if (value === 'false' || value === false) { return false; } return value; }, z.boolean()), ADMIN_EMAIL: optionalText, ADMIN_PASSWORD: optionalText, DATA_ENCRYPTION_KEY: optionalText, WEASYPRINT_BIN: optionalText, PDF_REGENERATE: z.preprocess( (value) => (value === '' || value === undefined ? undefined : value), z.enum(['never', 'changed', 'always']).default('never'), ), TRUST_PROXY: z.preprocess((value) => { if (value === undefined || value === '') { return false; } if (value === 'true' || value === true) { return true; } if (value === 'false' || value === false) { return false; } return value; }, z.boolean()), }); const parsed = envSchema.safeParse(process.env); if (!parsed.success) { console.error('Invalid environment variables', parsed.error.flatten().fieldErrors); process.exit(1); } if (parsed.data.NODE_ENV === 'production' && parsed.data.PDF_REGENERATE !== 'never') { console.error( `PDF_REGENERATE=${parsed.data.PDF_REGENERATE} is not allowed in production: stored consent and agreement PDFs must not change. Unset it or set PDF_REGENERATE=never.`, ); process.exit(1); } export const env = parsed.data;