import { readFileSync, existsSync } from "node:fs"; import { z } from "zod"; // Minimal .env loader so we don't need an extra dependency. function loadDotEnv(path = ".env"): void { if (!existsSync(path)) return; for (const rawLine of readFileSync(path, "utf8").split(/\r?\n/)) { const line = rawLine.trim(); if (!line || line.startsWith("#")) continue; const eq = line.indexOf("="); if (eq === -1) continue; const key = line.slice(0, eq).trim(); let value = line.slice(eq + 1).trim(); if ( (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")) ) { value = value.slice(1, -1); } if (process.env[key] === undefined) process.env[key] = value; } } loadDotEnv(); const schema = z.object({ STOAT_API_URL: z.string().url(), STOAT_BOT_TOKEN: z.string().min(1), COMMAND_PREFIX: z.string().min(1).default("!"), PORT: z.coerce.number().int().positive().default(3005), HOST: z.string().default("0.0.0.0"), PUBLIC_URL: z.string().url(), JWT_SECRET: z.string().min(16), SESSION_TTL_HOURS: z.coerce.number().positive().default(168), YTDLP_PATH: z.string().default("yt-dlp"), YTDLP_COOKIES: z.string().optional(), LOCAL_MEDIA_DIR: z.string().optional(), DEFAULT_VOLUME: z.coerce.number().min(0).max(200).default(60), MAX_QUEUE_SIZE: z.coerce.number().int().positive().default(500), SEARCH_RESULT_LIMIT: z.coerce.number().int().positive().max(25).default(10), IDLE_TIMEOUT_SECONDS: z.coerce.number().int().min(0).default(300), DJ_ROLE_NAME: z.string().default("DJ"), REQUIRE_DJ_ROLE: z .enum(["true", "false"]) .default("false") .transform((value) => value === "true"), LOG_LEVEL: z.string().default("info"), NODE_ENV: z.string().default("development"), }); const parsed = schema.safeParse(process.env); if (!parsed.success) { const issues = parsed.error.issues .map((i) => ` - ${i.path.join(".")}: ${i.message}`) .join("\n"); console.error(`Invalid configuration, check your .env file:\n${issues}`); process.exit(1); } export const config = { ...parsed.data, STOAT_API_URL: parsed.data.STOAT_API_URL.replace(/\/+$/, ""), PUBLIC_URL: parsed.data.PUBLIC_URL.replace(/\/+$/, ""), isProduction: parsed.data.NODE_ENV === "production", }; export type Config = typeof config;