grants/server/lib/env.ts
gdegelas a05331128b Atlas Green Morocco — grant strategy platform
- Full grant strategy framework for renewable energy & green hydrogen
- AI-powered grant studio, partner outreach, financial modeling
- Umami analytics with data-performance tracking
- Live Degelas metrics connected to solar.degelas.be
- Trilingual (EN/FR/AR) with i18n support
- Dockerized with Nginx frontend + Express API proxy
2026-06-01 09:44:03 +00:00

85 lines
2.9 KiB
TypeScript

import { z } from "zod";
/**
* Environment variable validation.
* Fails fast on startup if required vars are missing or invalid.
*/
const envSchema = z.object({
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
PORT: z.coerce.number().default(3001),
// Optional: if omitted, AI routes return clearly labelled demo/mock output.
// First checks AINFT_API_KEY, falls back to OPENAI_API_KEY for compatibility
AI_API_KEY: z.string().optional().or(z.literal("")),
AI_BASE_URL: z.string().url().optional().or(z.literal("")),
AI_MODEL: z.string().optional(),
AI_MODEL_COMPLEX: z.string().optional(),
OPENAI_API_KEY: z.string().optional().or(z.literal("")),
AINFT_API_KEY: z.string().optional().or(z.literal("")),
DEGELAS_API_URL: z.string().url().optional().or(z.literal("")),
DEGELAS_API_KEY: z.string().optional().or(z.literal("")),
CLIENT_ORIGIN: z.string().default("*"),
LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info"),
});
type Env = z.infer<typeof envSchema>;
export function resolvedApiKey(env: Env): string | undefined {
return env.AI_API_KEY || env.AINFT_API_KEY || env.OPENAI_API_KEY || undefined;
}
export function resolvedBaseUrl(env: Env): string | undefined {
return env.AI_BASE_URL || undefined;
}
export function resolvedModel(env: Env): string {
return env.AI_MODEL || "deepseek-v4-flash";
}
export function resolvedModelComplex(env: Env): string {
return env.AI_MODEL_COMPLEX || "deepseek-v4-pro";
}
let validated: Env | null = null;
export function validateEnv(): Env {
if (validated) return validated;
const result = envSchema.safeParse(process.env);
if (!result.success) {
const errors = result.error.issues.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
console.error("❌ Environment validation failed:\n" + errors);
process.exit(1);
}
validated = result.data;
// Log startup info (without sensitive values)
console.log("✅ Environment validated");
console.log(` NODE_ENV: ${validated.NODE_ENV}`);
console.log(` PORT: ${validated.PORT}`);
console.log(` LOG_LEVEL: ${validated.LOG_LEVEL}`);
if (validated.DEGELAS_API_URL) {
console.log(` DEGELAS_API_URL: configured`);
} else {
console.log(` DEGELAS_API_URL: not configured (using mock data)`);
}
if (resolvedApiKey(validated)) {
console.log(` AI_API_KEY: configured`);
console.log(` AI_BASE_URL: ${validated.AI_BASE_URL || "https://api.openai.com/v1 (default)"}`);
console.log(` AI_MODEL: ${validated.AI_MODEL || "deepseek-v4-flash (default)"}`);
console.log(` AI_MODEL_COMPLEX: ${validated.AI_MODEL_COMPLEX || "deepseek-v4-pro (default)"}`);
} else {
console.log(` AI_API_KEY: not configured (AI features will return mock data)`);
}
return validated;
}
export function getEnv(): Env {
if (!validated) {
throw new Error("Environment not validated. Call validateEnv() on startup.");
}
return validated;
}