Spaces:
Running
Running
| import { z } from "zod"; | |
| import * as dotenv from "dotenv"; | |
| dotenv.config(); | |
| const envSchema = z.object({ | |
| // βββ LLM (All on NVIDIA NIM β FREE) ββββββββββββββββββββββββ | |
| NVIDIA_API_KEY: z.string().min(5), | |
| NVIDIA_NIM_BASE_URL: z.string().url().default("https://integrate.api.nvidia.com/v1"), | |
| // βββ Supabase ββββββββββββββββββββββββββββββββββββββββββββββ | |
| SUPABASE_URL: z.string().url(), | |
| SUPABASE_SERVICE_ROLE_KEY: z.string().min(10), | |
| // βββ Trigger.dev βββββββββββββββββββββββββββββββββββββββββββ | |
| TRIGGER_DEV_API_KEY: z.string().min(5), | |
| TRIGGER_DEV_PROJECT_ID: z.string().min(3), | |
| // βββ Web Research ββββββββββββββββββββββββββββββββββββββββββ | |
| SERPER_API_KEY: z.string().min(5), | |
| // βββ Email Finding βββββββββββββββββββββββββββββββββββββββββ | |
| HUNTER_API_KEY: z.string().min(5), | |
| // βββ Email Verification ββββββββββββββββββββββββββββββββββββ | |
| REOON_API_KEY: z.string().min(5), | |
| // βββ Slack βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| SLACK_BOT_TOKEN: z.string().startsWith("xoxb-"), | |
| SLACK_SIGNING_SECRET: z.string().min(5), | |
| SLACK_ALERT_CHANNEL_ID: z.string(), | |
| SLACK_REVIEW_CHANNEL_ID: z.string(), | |
| // βββ Python AI Service βββββββββββββββββββββββββββββββββββββ | |
| PYTHON_AI_SERVICE_URL: z.string().url().default("http://localhost:8000"), | |
| PYTHON_AI_SERVICE_SECRET: z.string().min(10), | |
| // βββ System Config βββββββββββββββββββββββββββββββββββββββββ | |
| NODE_ENV: z.enum(["development", "staging", "production"]).default("development"), | |
| LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"), | |
| DAILY_LEAD_QUOTA: z.coerce.number().default(10), | |
| QUALITY_SCORE_THRESHOLD: z.coerce.number().default(70), | |
| HUMAN_REVIEW_ENABLED: z.string().transform((v) => v === "true").default("true"), | |
| DAILY_EMAIL_LIMIT: z.coerce.number().default(50), | |
| DAILY_LINKEDIN_LIMIT: z.coerce.number().default(25), | |
| SCHEDULE_START_HOUR_UTC: z.coerce.number().default(4), | |
| }); | |
| type Env = z.infer<typeof envSchema>; | |
| let _env: Env; | |
| export function getEnv(): Env { | |
| if (!_env) { | |
| const result = envSchema.safeParse(process.env); | |
| if (!result.success) { | |
| console.error("β Invalid environment configuration:"); | |
| result.error.errors.forEach((e) => { | |
| console.error(` ${e.path.join(".")}: ${e.message}`); | |
| }); | |
| // If we are in build/indexing phase (SUPABASE_URL is missing during CLI build), bypass crash to allow deploy to succeed | |
| if (!process.env.SUPABASE_URL || process.env.TRIGGER_BUILD === "true") { | |
| console.warn("β οΈ Warning: Missing environment variables during build/indexing phase. Bypassing crash with mock defaults to allow deployment."); | |
| // We parse mock values with the schema to get all the default values! | |
| const parsedDefaults = envSchema.parse({ | |
| NODE_ENV: "production", // Set to production to bypass pino-pretty build crash | |
| NVIDIA_API_KEY: "mock_nvidia_key", | |
| SUPABASE_URL: "https://mock.supabase.co", | |
| SUPABASE_SERVICE_ROLE_KEY: "mock_service_role_key_long_enough", | |
| TRIGGER_DEV_API_KEY: "mock_trigger_dev_api_key", | |
| TRIGGER_DEV_PROJECT_ID: "mock_proj_id", | |
| SERPER_API_KEY: "mock_serper_key", | |
| HUNTER_API_KEY: "mock_hunter_key", | |
| REOON_API_KEY: "mock_reoon_key", | |
| SLACK_BOT_TOKEN: "xoxb-mock-bot-token", | |
| SLACK_SIGNING_SECRET: "mock_signing_secret", | |
| SLACK_ALERT_CHANNEL_ID: "mock_channel", | |
| SLACK_REVIEW_CHANNEL_ID: "mock_channel", | |
| PYTHON_AI_SERVICE_SECRET: "mock_python_secret_key_long_enough", | |
| }); | |
| _env = { ...parsedDefaults, ...process.env } as any; | |
| return _env; | |
| } | |
| process.exit(1); | |
| } | |
| _env = result.data; | |
| } | |
| return _env; | |
| } | |