import fs from "node:fs"; import path from "node:path"; const DEFAULT_CONFIG = { SERVER_HOST: "0.0.0.0", SERVER_PORT: 9736, MONGODB_HOST: "", MONGODB_PORT: 27017, MONGODB_DB: "arena", MONGODB_USER: "", MONGODB_PASS: "", MONGODB_URI: "", MONGODB_VISITOR_COLLECTION: "visitors", MONGODB_MAX_POOL_SIZE: 10, MONGODB_SERVER_SELECTION_TIMEOUT_MS: 5000, COOKIE_SECURE: false, }; let config; export function getConfig() { if (!config) { config = loadConfig(); } return config; } export function loadConfig(filePath = path.resolve(process.cwd(), "config.json")) { const rawConfig = fs.existsSync(filePath) ? JSON.parse(fs.readFileSync(filePath, "utf8")) : {}; return normalizeConfig(rawConfig); } export function hasMongoConfig() { const appConfig = getConfig(); return Boolean(appConfig.MONGODB_URI || appConfig.MONGODB_HOST); } export function getMongoUri() { const appConfig = getConfig(); if (appConfig.MONGODB_URI) { return appConfig.MONGODB_URI; } if (!appConfig.MONGODB_HOST) { throw new Error("MongoDB configuration is required for visitor tracking."); } const host = appConfig.MONGODB_HOST; const port = Number(appConfig.MONGODB_PORT || DEFAULT_CONFIG.MONGODB_PORT); const credentials = mongoCredentials(appConfig); return `mongodb://${credentials}${host}:${port}`; } function normalizeConfig(rawConfig) { const server = rawConfig.server || {}; const mongodb = rawConfig.mongodb || {}; return { SERVER_HOST: stringValue(rawConfig.SERVER_HOST, server.host, DEFAULT_CONFIG.SERVER_HOST), SERVER_PORT: numberValue(rawConfig.SERVER_PORT, server.port, DEFAULT_CONFIG.SERVER_PORT), MONGODB_HOST: stringValue(rawConfig.MONGODB_HOST, mongodb.host, DEFAULT_CONFIG.MONGODB_HOST), MONGODB_PORT: numberValue(rawConfig.MONGODB_PORT, mongodb.port, DEFAULT_CONFIG.MONGODB_PORT), MONGODB_DB: stringValue(rawConfig.MONGODB_DB, mongodb.db, DEFAULT_CONFIG.MONGODB_DB), MONGODB_USER: stringValue(rawConfig.MONGODB_USER, mongodb.user, DEFAULT_CONFIG.MONGODB_USER), MONGODB_PASS: stringValue(rawConfig.MONGODB_PASS, mongodb.pass, DEFAULT_CONFIG.MONGODB_PASS), MONGODB_URI: stringValue(rawConfig.MONGODB_URI, mongodb.uri, DEFAULT_CONFIG.MONGODB_URI), MONGODB_VISITOR_COLLECTION: stringValue( rawConfig.MONGODB_VISITOR_COLLECTION, mongodb.visitorCollection, DEFAULT_CONFIG.MONGODB_VISITOR_COLLECTION, ), MONGODB_MAX_POOL_SIZE: numberValue( rawConfig.MONGODB_MAX_POOL_SIZE, mongodb.maxPoolSize, DEFAULT_CONFIG.MONGODB_MAX_POOL_SIZE, ), MONGODB_SERVER_SELECTION_TIMEOUT_MS: numberValue( rawConfig.MONGODB_SERVER_SELECTION_TIMEOUT_MS, mongodb.serverSelectionTimeoutMs, DEFAULT_CONFIG.MONGODB_SERVER_SELECTION_TIMEOUT_MS, ), COOKIE_SECURE: booleanValue(rawConfig.COOKIE_SECURE, server.cookieSecure, DEFAULT_CONFIG.COOKIE_SECURE), }; } function mongoCredentials(appConfig) { if (!appConfig.MONGODB_USER) { return ""; } const user = encodeURIComponent(appConfig.MONGODB_USER); const pass = encodeURIComponent(appConfig.MONGODB_PASS || ""); return `${user}:${pass}@`; } function stringValue(...values) { const value = values.find((candidate) => typeof candidate === "string" && candidate.length > 0); return value ?? ""; } function numberValue(...values) { const value = values.find((candidate) => { const numericValue = Number(candidate); return Number.isFinite(numericValue) && numericValue > 0; }); return Number(value); } function booleanValue(...values) { const value = values.find((candidate) => typeof candidate === "boolean" || candidate === "true" || candidate === "false"); if (value === "true") { return true; } if (value === "false") { return false; } return Boolean(value); }