File size: 3,127 Bytes
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
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}`);
      });
      process.exit(1);
    }
    _env = result.data;
  }
  return _env;
}