import { z } from "zod"; export const mintelEnvSchema = { NODE_ENV: z .enum(["development", "production", "test"]) .default("development"), NEXT_PUBLIC_BASE_URL: z.string().url().optional(), NEXT_PUBLIC_TARGET: z .enum(["development", "testing", "staging", "production"]) .optional(), TARGET: z .enum(["development", "testing", "staging", "production"]) .optional(), // Analytics (Proxy Pattern) UMAMI_WEBSITE_ID: z.string().optional(), NEXT_PUBLIC_UMAMI_WEBSITE_ID: z.string().optional(), UMAMI_API_ENDPOINT: z .string() .url() .default("https://analytics.infra.mintel.me"), // Error Tracking SENTRY_DSN: z.string().optional(), // Notifications GOTIFY_URL: z.string().url().optional(), GOTIFY_TOKEN: z.string().optional(), LOG_LEVEL: z .enum(["trace", "debug", "info", "warn", "error", "fatal"]) .default("info"), // Mail MAIL_HOST: z.string().optional(), MAIL_PORT: z.coerce.number().default(587), MAIL_USERNAME: z.string().optional(), MAIL_PASSWORD: z.string().optional(), MAIL_FROM: z.string().optional(), MAIL_RECIPIENTS: z.preprocess( (val) => (typeof val === "string" ? val.split(",").filter(Boolean) : val), z.array(z.string()).default([]), ), // Directus DIRECTUS_URL: z.string().url().default("http://localhost:8055"), DIRECTUS_ADMIN_EMAIL: z.string().optional(), DIRECTUS_ADMIN_PASSWORD: z.string().optional(), DIRECTUS_API_TOKEN: z.string().optional(), INTERNAL_DIRECTUS_URL: z.string().url().optional(), }; /** * Standard Mintel refinements for environment variables. * Enforces mandatory requirements for non-development environments. */ export const withMintelRefinements = (schema: T) => { return schema.superRefine((data: any, ctx) => { const skipValidation = process.env.SKIP_ENV_VALIDATION === "true" || process.env.SKIP_RUNTIME_ENV_VALIDATION === "true"; if (skipValidation) return; const target = data.TARGET || data.NEXT_PUBLIC_TARGET || "development"; // Strict validation for non-development environments if (target !== "development") { if (!data.MAIL_HOST) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "MAIL_HOST is required in non-development environments", path: ["MAIL_HOST"], }); } } }); }; export type MintelEnv> = z.infer< ReturnType< typeof withMintelRefinements> > >; export function validateMintelEnv< T extends z.ZodRawShape = Record, >(schemaExtension: T = {} as T): MintelEnv { const fullSchema = withMintelRefinements( z.object(mintelEnvSchema).extend(schemaExtension), ); const isBuildTime = process.env.NEXT_PHASE === "phase-production-build" || process.env.SKIP_ENV_VALIDATION === "true" || process.env.SKIP_RUNTIME_ENV_VALIDATION === "true"; const result = fullSchema.safeParse(process.env); if (!result.success) { if (isBuildTime) { console.warn( "⚠️ Some environment variables are missing during build, but skipping strict validation.", ); // Return process.env casted to the full schema type to unblock builds return process.env as unknown as z.infer; } console.error( "❌ Invalid environment variables:", result.error.flatten().fieldErrors, ); throw new Error("Invalid environment variables"); } return result.data as MintelEnv; }