import { z } from 'zod'; import { config } from 'dotenv'; import { resolve } from 'path'; // Load .env file from the api package root config({ path: resolve(process.cwd(), '.env') }); const envSchema = z.object({ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'), PORT: z.string().default('4000'), DATABASE_URL: z.string(), JWT_SECRET: z.string().min(32), JWT_EXPIRES_IN: z.string().default('15m'), JWT_REFRESH_EXPIRES_IN: z.string().default('7d'), CORS_ORIGIN: z.string().default('http://localhost:3000'), // Frontend URL (for MercadoPago back_url, emails, etc.) FRONTEND_URL: z.string().default('https://horuxfin.com'), // FIEL encryption (separate from JWT to allow independent rotation) FIEL_ENCRYPTION_KEY: z.string().min(32), FIEL_STORAGE_PATH: z.string().default('/var/horux/fiel'), // MercadoPago MP_ACCESS_TOKEN: z.string().optional(), MP_WEBHOOK_SECRET: z.string().optional(), MP_NOTIFICATION_URL: z.string().optional(), // SMTP (Gmail Workspace) SMTP_HOST: z.string().default('smtp.gmail.com'), SMTP_PORT: z.string().default('587'), SMTP_USER: z.string().optional(), SMTP_PASS: z.string().optional(), SMTP_FROM: z.string().default('Horux360 '), // Admin notification email ADMIN_EMAIL: z.string().default('carlos@horuxfin.com'), // Facturapi FACTURAPI_USER_KEY: z.string().optional(), FACTURAPI_MODE: z.enum(['test', 'live']).optional(), // Cloudflare Tunnel (connector BYO-DB) CLOUDFLARE_API_TOKEN: z.string().optional(), CLOUDFLARE_ACCOUNT_ID: z.string().optional(), CLOUDFLARE_TUNNEL_DOMAIN: z.string().default('tunnel.horux.mx'), // KMS for encrypting DB connection strings and connector tokens CONNECTOR_ENCRYPTION_KEY: z.string().optional(), }); const parsed = envSchema.safeParse(process.env); if (!parsed.success) { console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors); process.exit(1); } export const env = parsed.data; // Parse CORS origins (comma-separated) into array export function getCorsOrigins(): string[] { return env.CORS_ORIGIN.split(',').map(origin => origin.trim()); }