spb/phi-conseil
Public
1import { z } from "zod";23/**4 * Variables d'environnement serveur, validées au premier accès.5 * Aucune n'est exposée au navigateur (pas de préfixe NEXT_PUBLIC_ sauf6 * NEXT_PUBLIC_SITE_URL et NEXT_PUBLIC_CONTENT_PREVIEW, non sensibles).7 */8const schema = z.object({9 NODE_ENV: z.enum(["development", "test", "production"]).default("development"),10 NEXT_PUBLIC_SITE_URL: z.url().optional(),11 NEXT_PUBLIC_CONTENT_PREVIEW: z.enum(["0", "1"]).default("0"),1213 /** `smtp` = envoi réel ; `log` = journalisation locale uniquement (dev/CI). */14 CONTACT_TRANSPORT: z.enum(["smtp", "log"]).default("log"),15 CONTACT_TO: z.email().optional(),16 CONTACT_FROM: z.email().optional(),17 SMTP_HOST: z.string().optional(),18 SMTP_PORT: z.coerce.number().int().positive().optional(),19 SMTP_SECURE: z20 .enum(["true", "false"])21 .default("false")22 .transform((v) => v === "true"),23 SMTP_USER: z.string().optional(),24 SMTP_PASS: z.string().optional(),2526 /** Limitation de débit du formulaire : N requêtes par fenêtre (secondes). */27 CONTACT_RATE_LIMIT: z.coerce.number().int().positive().default(5),28 CONTACT_RATE_WINDOW_S: z.coerce.number().int().positive().default(600),29});3031export type Env = z.infer<typeof schema>;3233let cached: Env | null = null;3435export function getEnv(): Env {36 if (cached) return cached;37 const parsed = schema.safeParse(process.env);38 if (!parsed.success) {39 const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");40 throw new Error(`Variables d'environnement invalides : ${issues}`);41 }42 const env = parsed.data;43 if (env.CONTACT_TRANSPORT === "smtp") {44 const missing = (["CONTACT_TO", "CONTACT_FROM", "SMTP_HOST", "SMTP_PORT"] as const).filter(45 (k) => !env[k],46 );47 if (missing.length) {48 throw new Error(`CONTACT_TRANSPORT=smtp requiert : ${missing.join(", ")}`);49 }50 }51 cached = env;52 return env;53}5455/** Pour les tests : vider le cache après modification de process.env. */56export function resetEnvCache() {57 cached = null;58}5960export function siteUrl(fallback: string): string {61 return process.env.NEXT_PUBLIC_SITE_URL ?? fallback;62}63