import { z } from "zod"; /** * Helper to parse boolean from string */ const booleanString = z .string() .optional() .default("false") .transform((val) => val === "true"); /** * Environment variables schema validation using Zod */ export const envSchema = z.object({ // Environment NODE_ENV: z .enum(["development", "production", "test"]) .default("development"), PORT: z.coerce.number().default(3005), // Database DATABASE_URL: z.string().url(), // AI Engine AI_ENGINE_URL: z.string().url().default("http://localhost:8000"), AI_ENGINE_MODE: z.enum(["v28-pro-max", "dual"]).default("v28-pro-max"), // JWT JWT_SECRET: z.string().min(32), JWT_ACCESS_EXPIRATION: z.string().default("15m"), JWT_REFRESH_EXPIRATION: z.string().default("7d"), // Redis REDIS_ENABLED: z .string() .transform((val) => val === "true") .default("false" as any), REDIS_HOST: z.string().default("localhost"), REDIS_PORT: z.coerce.number().default(6379), REDIS_PASSWORD: z.string().optional(), // i18n DEFAULT_LANGUAGE: z.string().default("en"), FALLBACK_LANGUAGE: z.string().default("en"), // Gemini AI ENABLE_GEMINI: z .string() .transform((val) => val === "true") .default("false" as any), GOOGLE_API_KEY: z.string().optional(), GEMINI_DEFAULT_MODEL: z.string().default("gemini-2.5-flash"), // Social Poster SOCIAL_POSTER_ENABLED: z .string() .transform((val) => val === "true") .default("false" as any), TWITTER_API_KEY: z.string().optional(), TWITTER_API_SECRET: z.string().optional(), TWITTER_ACCESS_TOKEN: z.string().optional(), TWITTER_ACCESS_SECRET: z.string().optional(), META_PAGE_ACCESS_TOKEN: z.string().optional(), META_PAGE_ID: z.string().optional(), META_IG_USER_ID: z.string().optional(), // Optional Features ENABLE_MAIL: booleanString, ENABLE_S3: booleanString, ENABLE_WEBSOCKET: booleanString, ENABLE_MULTI_TENANCY: booleanString, // Mail (Optional) MAIL_HOST: z.string().optional(), MAIL_PORT: z.coerce.number().optional(), MAIL_USER: z.string().optional(), MAIL_PASSWORD: z.string().optional(), MAIL_FROM: z.string().optional(), // S3 (Optional) S3_ENDPOINT: z.string().optional(), S3_ACCESS_KEY: z.string().optional(), S3_SECRET_KEY: z.string().optional(), S3_BUCKET: z.string().optional(), S3_REGION: z.string().optional(), // Throttle THROTTLE_TTL: z.coerce.number().default(60000), THROTTLE_LIMIT: z.coerce.number().default(100), }); export type EnvConfig = z.infer; /** * Validate environment variables */ export function validateEnv(config: Record): EnvConfig { const result = envSchema.safeParse(config); if (!result.success) { const errors = result.error.issues.map( (err) => `${err.path.join(".")}: ${err.message}`, ); throw new Error(`Environment validation failed:\n${errors.join("\n")}`); } return result.data; }