/opt/mawid/apps/api/src/config
NameSizeModeActions
env.spec.ts13660644editdlrm
env.ts20720644editdlrm
Edit: /opt/mawid/apps/api/src/config/env.ts (2072B)
import { z } from 'zod'; // WhatsApp / OpenAI credentials are only exercised from Phase 2/3 onward, // so they may be empty in development and test — production requires them. const PRODUCTION_REQUIRED = [ 'OPENAI_API_KEY', 'WA_PHONE_NUMBER_ID', 'WA_BUSINESS_ACCOUNT_ID', 'WA_ACCESS_TOKEN', 'WA_WEBHOOK_VERIFY_TOKEN', 'WA_APP_SECRET', ] as const; export const envSchema = z .object({ NODE_ENV: z.enum(['development', 'test', 'production']).default('development'), API_PORT: z.coerce.number().int().positive().default(3001), DATABASE_URL: z.string().min(1), REDIS_URL: z.string().min(1), JWT_SECRET: z.string().min(16, 'JWT_SECRET must be at least 16 characters'), OPENAI_API_KEY: z.string().default(''), WA_PHONE_NUMBER_ID: z.string().default(''), WA_BUSINESS_ACCOUNT_ID: z.string().default(''), WA_ACCESS_TOKEN: z.string().default(''), WA_WEBHOOK_VERIFY_TOKEN: z.string().default(''), WA_APP_SECRET: z.string().default(''), SENTRY_DSN: z.string().default(''), APP_BASE_URL: z.string().url(), DASHBOARD_BASE_URL: z.string().url(), TZ: z.string().default('Europe/Istanbul'), FEATURE_ECHO_MODE: z .enum(['true', 'false']) .default('false') .transform((v) => v === 'true'), }) .superRefine((env, ctx) => { if (env.NODE_ENV !== 'production') return; for (const key of PRODUCTION_REQUIRED) { if (!env[key]) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: [key], message: `${key} is required in production`, }); } } }); export type Env = z.infer; /** Throws (refusing boot) when the environment is invalid. */ export function validateEnv(config: Record): Env { const parsed = envSchema.safeParse(config); if (!parsed.success) { const details = parsed.error.issues .map((i) => ` - ${i.path.join('.') || '(root)'}: ${i.message}`) .join('\n'); throw new Error(`Invalid environment configuration:\n${details}`); } return parsed.data; }