Spaces:
Running
Running
File size: 4,501 Bytes
bd28470 c76b4a5 bd28470 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | 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;
}
|