import { readFileSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); /** * Loads `.env` into process.env without a dependency. Node's own `--env-file` needs 20.6+; * parsing it here keeps the runbook a plain `npm start` on any Node 18+. * Real environment variables always win, so a container/CI can override the file. */ function loadDotEnv() { let raw; try { raw = readFileSync(resolve(projectRoot, '.env'), 'utf8'); } catch { return; } for (const line of raw.split(/\r?\n/)) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const eq = trimmed.indexOf('='); if (eq === -1) continue; const key = trimmed.slice(0, eq).trim(); let value = trimmed.slice(eq + 1).trim(); if (value.length >= 2 && ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))) { value = value.slice(1, -1); } if (!(key in process.env)) process.env[key] = value; } } loadDotEnv(); const botToken = (process.env.TELEGRAM_BOT_TOKEN ?? '').trim(); if (!botToken) { console.error('FATAL: TELEGRAM_BOT_TOKEN is not set. Copy .env.example to .env and paste the token from @BotFather.'); process.exit(1); } const chatIds = (process.env.TELEGRAM_CHAT_IDS ?? '') .split(',') .map((id) => id.trim()) .filter(Boolean); // The API key is mandatory, with no opt-out: an OTP relay that anyone can POST to is an open // message cannon pointed at the test group's phones. Fail to start rather than listen unprotected. const apiKey = (process.env.API_KEY ?? '').trim(); if (!apiKey) { console.error('FATAL: API_KEY is not set. Choose any shared secret, put it in .env, and configure the same value on the caller.'); process.exit(1); } if (apiKey.length < 16) { console.error(`FATAL: API_KEY is too short (${apiKey.length} chars). Use at least 16 — e.g. output of: node -e "console.log(require('crypto').randomBytes(24).toString('hex'))"`); process.exit(1); } export const config = { botToken, chatIds, apiKey, host: (process.env.HOST ?? '127.0.0.1').trim(), port: Number(process.env.PORT ?? 5010), redactCodeInLogs: (process.env.REDACT_CODE_IN_LOGS ?? 'false').toLowerCase() === 'true', };