import { z } from 'zod'; const urlOptional = z.string().url().optional(); const stringOptional = z.string().optional(); const publicEnvSchema = z.object({ NEXT_PUBLIC_GRIDPILOT_MODE: z.enum(['pre-launch', 'alpha']).optional(), NEXT_PUBLIC_SITE_URL: urlOptional, NEXT_PUBLIC_API_BASE_URL: urlOptional, NEXT_PUBLIC_SITE_NAME: stringOptional, NEXT_PUBLIC_SUPPORT_EMAIL: stringOptional, NEXT_PUBLIC_SPONSOR_EMAIL: stringOptional, NEXT_PUBLIC_LEGAL_COMPANY_NAME: stringOptional, NEXT_PUBLIC_LEGAL_VAT_ID: stringOptional, NEXT_PUBLIC_LEGAL_REGISTERED_COUNTRY: stringOptional, NEXT_PUBLIC_LEGAL_REGISTERED_ADDRESS: stringOptional, NEXT_PUBLIC_DISCORD_URL: stringOptional, NEXT_PUBLIC_X_URL: stringOptional, }); const serverEnvSchema = z.object({ API_BASE_URL: urlOptional, KV_REST_API_URL: urlOptional, KV_REST_API_TOKEN: stringOptional, CI: stringOptional, DOCKER: stringOptional, NODE_ENV: z.enum(['development', 'production', 'test']).optional(), }); export type WebsitePublicEnv = z.infer; export type WebsiteServerEnv = z.infer; function formatZodIssues(issues: z.ZodIssue[]): string { return issues .map((issue) => { const path = issue.path.join('.') || '(root)'; return `${path}: ${issue.message}`; }) .join('; '); } /** * Parses Website env vars (server-side safe). * Only validates the variables we explicitly support. */ export function getWebsiteServerEnv(): WebsiteServerEnv { const result = serverEnvSchema.safeParse(process.env); if (!result.success) { throw new Error(`Invalid website server env: ${formatZodIssues(result.error.issues)}`); } return result.data; } /** * Parses Website public env vars (safe on both server + client). * Note: on the client, only `NEXT_PUBLIC_*` vars are actually present. */ export function getWebsitePublicEnv(): WebsitePublicEnv { const result = publicEnvSchema.safeParse(process.env); if (!result.success) { throw new Error(`Invalid website public env: ${formatZodIssues(result.error.issues)}`); } return result.data; } export function isTruthyEnv(value: string | undefined): boolean { if (!value) return false; return value !== '0' && value.toLowerCase() !== 'false'; } /** * Matches the semantics used in [`getWebsiteApiBaseUrl()`](apps/website/lib/config/apiBaseUrl.ts:6). */ export function isTestLikeEnvironment(): boolean { const { NODE_ENV, CI, DOCKER } = getWebsiteServerEnv(); return NODE_ENV === 'test' || CI === 'true' || DOCKER === 'true'; } export function isProductionEnvironment(): boolean { return getWebsiteServerEnv().NODE_ENV === 'production'; } export function isKvConfigured(): boolean { const { KV_REST_API_URL, KV_REST_API_TOKEN } = getWebsiteServerEnv(); return Boolean(KV_REST_API_URL && KV_REST_API_TOKEN); } export function assertKvConfiguredInProduction(): void { if (isProductionEnvironment() && !isKvConfigured()) { throw new Error('Missing KV_REST_API_URL/KV_REST_API_TOKEN in production environment'); } }