109 lines
2.9 KiB
TypeScript
Executable File
109 lines
2.9 KiB
TypeScript
Executable File
import { z } from "zod";
|
|
|
|
/**
|
|
* Helper to parse boolean from string
|
|
*/
|
|
const booleanString = z
|
|
.string()
|
|
.optional()
|
|
.default("false")
|
|
.transform((val) => val === "true");
|
|
|
|
/**
|
|
* Environment variables schema validation using Zod
|
|
*/
|
|
export const envSchema = z.object({
|
|
// Environment
|
|
NODE_ENV: z
|
|
.enum(["development", "production", "test"])
|
|
.default("development"),
|
|
PORT: z.coerce.number().default(3005),
|
|
|
|
// Database
|
|
DATABASE_URL: z.string().url(),
|
|
// AI Engine
|
|
AI_ENGINE_URL: z.string().url().default("http://localhost:8000"),
|
|
AI_ENGINE_MODE: z.enum(["v28-pro-max", "dual"]).default("v28-pro-max"),
|
|
|
|
// JWT
|
|
JWT_SECRET: z.string().min(32),
|
|
JWT_ACCESS_EXPIRATION: z.string().default("15m"),
|
|
JWT_REFRESH_EXPIRATION: z.string().default("7d"),
|
|
|
|
// Redis
|
|
REDIS_ENABLED: z
|
|
.string()
|
|
.transform((val) => val === "true")
|
|
.default("false" as any),
|
|
REDIS_HOST: z.string().default("localhost"),
|
|
REDIS_PORT: z.coerce.number().default(6379),
|
|
REDIS_PASSWORD: z.string().optional(),
|
|
|
|
// i18n
|
|
DEFAULT_LANGUAGE: z.string().default("en"),
|
|
FALLBACK_LANGUAGE: z.string().default("en"),
|
|
|
|
// Gemini AI
|
|
ENABLE_GEMINI: z
|
|
.string()
|
|
.transform((val) => val === "true")
|
|
.default("false" as any),
|
|
GOOGLE_API_KEY: z.string().optional(),
|
|
GEMINI_DEFAULT_MODEL: z.string().default("gemini-2.5-flash"),
|
|
|
|
// Social Poster
|
|
SOCIAL_POSTER_ENABLED: z
|
|
.string()
|
|
.transform((val) => val === "true")
|
|
.default("false" as any),
|
|
TWITTER_API_KEY: z.string().optional(),
|
|
TWITTER_API_SECRET: z.string().optional(),
|
|
TWITTER_ACCESS_TOKEN: z.string().optional(),
|
|
TWITTER_ACCESS_SECRET: z.string().optional(),
|
|
META_PAGE_ACCESS_TOKEN: z.string().optional(),
|
|
META_PAGE_ID: z.string().optional(),
|
|
META_IG_USER_ID: z.string().optional(),
|
|
|
|
// Optional Features
|
|
ENABLE_MAIL: booleanString,
|
|
ENABLE_S3: booleanString,
|
|
ENABLE_WEBSOCKET: booleanString,
|
|
ENABLE_MULTI_TENANCY: booleanString,
|
|
|
|
// Mail (Optional)
|
|
MAIL_HOST: z.string().optional(),
|
|
MAIL_PORT: z.coerce.number().optional(),
|
|
MAIL_USER: z.string().optional(),
|
|
MAIL_PASSWORD: z.string().optional(),
|
|
MAIL_FROM: z.string().optional(),
|
|
|
|
// S3 (Optional)
|
|
S3_ENDPOINT: z.string().optional(),
|
|
S3_ACCESS_KEY: z.string().optional(),
|
|
S3_SECRET_KEY: z.string().optional(),
|
|
S3_BUCKET: z.string().optional(),
|
|
S3_REGION: z.string().optional(),
|
|
|
|
// Throttle
|
|
THROTTLE_TTL: z.coerce.number().default(60000),
|
|
THROTTLE_LIMIT: z.coerce.number().default(100),
|
|
});
|
|
|
|
export type EnvConfig = z.infer<typeof envSchema>;
|
|
|
|
/**
|
|
* Validate environment variables
|
|
*/
|
|
export function validateEnv(config: Record<string, unknown>): EnvConfig {
|
|
const result = envSchema.safeParse(config);
|
|
|
|
if (!result.success) {
|
|
const errors = result.error.issues.map(
|
|
(err) => `${err.path.join(".")}: ${err.message}`,
|
|
);
|
|
throw new Error(`Environment validation failed:\n${errors.join("\n")}`);
|
|
}
|
|
|
|
return result.data;
|
|
}
|