import { z } from "zod"; const EnvSchema = z.object({ DATABASE_URL: z.string().min(1), UPLOAD_DIR: z.string().default("./data/uploads"), APP_URL: z.string().url().default("http://localhost:3000"), APP_SECRET: z.string().min(32, "APP_SECRET must be at least 32 chars"), ADMIN_SESSION_HOURS: z.coerce.number().int().positive().default(8), OPERATOR_SESSION_HOURS: z.coerce.number().int().positive().default(12), BOOTSTRAP_ADMIN_EMAIL: z.string().email().optional(), BOOTSTRAP_ADMIN_PASSWORD: z.string().min(1).optional(), BOOTSTRAP_ADMIN_NAME: z.string().default("Administrator"), PIN_MAX_ATTEMPTS: z.coerce.number().int().positive().default(5), PIN_LOCKOUT_MINUTES: z.coerce.number().int().positive().default(15), NODE_ENV: z.enum(["development", "production", "test"]).default("development"), }); export type Env = z.infer; function load(): Env { // During `next build` page-data collection the route modules are evaluated // without real secrets — fall back to safe placeholders so the build can // emit the module graph. Real runtime still re-validates at request time. const isBuildPhase = process.env.NEXT_PHASE === "phase-production-build" || process.env.NEXT_BUILD === "true"; const source = isBuildPhase ? { ...process.env, DATABASE_URL: process.env.DATABASE_URL ?? "file:./data/build-placeholder.db", APP_SECRET: process.env.APP_SECRET ?? "build-time-placeholder-secret-please-override-at-runtime", } : process.env; const parsed = EnvSchema.safeParse(source); if (!parsed.success) { const issues = parsed.error.issues .map((i) => ` - ${i.path.join(".")}: ${i.message}`) .join("\n"); throw new Error(`Invalid environment configuration:\n${issues}`); } return parsed.data; } export const env = load();