From e5dd5c6257b7b0cd9e6b3c301e5a810c13900309 Mon Sep 17 00:00:00 2001 From: Martin Porwoll Date: Sun, 1 Mar 2026 09:18:02 +0000 Subject: [PATCH] feat: add typed config validation and logger utility Co-Authored-By: Claude Opus 4.6 --- src/config.ts | 49 +++++++++++++++++++++++++++++++++++++++++++++ src/utils/logger.ts | 31 ++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/config.ts create mode 100644 src/utils/logger.ts diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..780052f --- /dev/null +++ b/src/config.ts @@ -0,0 +1,49 @@ +import 'dotenv/config'; + +interface Config { + telegram: { + botToken: string; + allowedUserIds: number[]; + }; + payload: { + apiUrl: string; + email: string; + password: string; + }; + defaultTenantId: number; + logLevel: string; + nodeEnv: string; +} + +function requireEnv(key: string): string { + const value = process.env[key]; + if (!value) { + console.error(`Missing required environment variable: ${key}`); + process.exit(1); + } + return value; +} + +export const config: Config = { + telegram: { + botToken: requireEnv('TELEGRAM_BOT_TOKEN'), + allowedUserIds: requireEnv('ALLOWED_USER_IDS') + .split(',') + .map((id) => { + const num = Number(id.trim()); + if (Number.isNaN(num)) { + console.error(`Invalid user ID in ALLOWED_USER_IDS: "${id}"`); + process.exit(1); + } + return num; + }), + }, + payload: { + apiUrl: requireEnv('PAYLOAD_API_URL'), + email: requireEnv('PAYLOAD_ADMIN_EMAIL'), + password: requireEnv('PAYLOAD_ADMIN_PASSWORD'), + }, + defaultTenantId: Number(process.env.DEFAULT_TENANT_ID || '4'), + logLevel: process.env.LOG_LEVEL || 'info', + nodeEnv: process.env.NODE_ENV || 'development', +}; diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..f5e966b --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,31 @@ +const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 } as const; +type LogLevel = keyof typeof LEVELS; + +let minLevel: number = LEVELS.info; + +export function setLogLevel(level: LogLevel): void { + minLevel = LEVELS[level]; +} + +function formatTimestamp(): string { + return new Date().toISOString().replace('T', ' ').slice(0, 19); +} + +function log(level: LogLevel, module: string, message: string, data?: unknown): void { + if (LEVELS[level] < minLevel) return; + const prefix = `[${formatTimestamp()}] [${level.toUpperCase()}] [${module}]`; + if (data !== undefined) { + console[level === 'debug' ? 'log' : level](`${prefix} ${message}`, data); + } else { + console[level === 'debug' ? 'log' : level](`${prefix} ${message}`); + } +} + +export function createLogger(module: string) { + return { + debug: (msg: string, data?: unknown) => log('debug', module, msg, data), + info: (msg: string, data?: unknown) => log('info', module, msg, data), + warn: (msg: string, data?: unknown) => log('warn', module, msg, data), + error: (msg: string, data?: unknown) => log('error', module, msg, data), + }; +}