/** * voko-server 连接与基础设施配置的唯一解析点。业务运行参数由所属模块直接读取。 */ import { devMysqlUrl } from '@drama/db' /** * @typedef {{ * nodeEnv: string, * port: number, * databaseUrl: string, * logLevel: 'debug'|'info'|'warn'|'error', * authTokenSecret: string, * publicUrl: string, * email: { host: string, port: number, secure: boolean, user: string, pass: string, from: string }, * oauth: { googleClientId: string|null, googleClientSecret: string|null }, * }} VokoEnv */ /** * @param {Record} rawEnv * @returns {VokoEnv} */ export function readEnv(rawEnv) { const nodeEnv = rawEnv.NODE_ENV || 'development' const databaseUrl = rawEnv.DATABASE_URL || devMysqlUrl('voko') const scheme = new URL(databaseUrl).protocol if (scheme !== 'mysql:' && scheme !== 'mysql2:') throw new Error(`voko-server: DATABASE_URL 必须是 MySQL,收到 ${scheme}`) if (nodeEnv === 'production' && !rawEnv.AUTH_TOKEN_SECRET) throw new Error('voko-server: 生产环境必须设置 AUTH_TOKEN_SECRET') const email = { host: rawEnv.SMTP_HOST, port: Number(rawEnv.SMTP_PORT || 465), secure: rawEnv.SMTP_SECURE !== 'false', user: rawEnv.SMTP_USER, pass: rawEnv.SMTP_PASS, from: rawEnv.EMAIL_FROM, } const missing = [['SMTP_HOST', email.host], ['SMTP_USER', email.user], ['SMTP_PASS', email.pass], ['EMAIL_FROM', email.from]] .filter(([, value]) => !value) .map(([name]) => name) if (missing.length > 0) throw new Error(`voko-server: 邮件缺少配置,拒绝启动(无 fake 降级):${missing.join(', ')}`) return { nodeEnv, port: parsePort(rawEnv.PORT, 9201), databaseUrl, logLevel: parseLogLevel(rawEnv.LOG_LEVEL), authTokenSecret: rawEnv.AUTH_TOKEN_SECRET || 'voko-dev-token-secret', publicUrl: (rawEnv.PUBLIC_URL || 'http://localhost:5201').replace(/\/$/, ''), email: /** @type {VokoEnv['email']} */ (email), oauth: { googleClientId: rawEnv.GOOGLE_OAUTH_CLIENT_ID || null, googleClientSecret: rawEnv.GOOGLE_OAUTH_CLIENT_SECRET || null, }, } } /** * @param {string|undefined} raw * @param {number} fallback */ function parsePort(raw, fallback) { if (raw === undefined || raw === '') return fallback const port = Number(raw) if (!Number.isInteger(port) || port <= 0 || port > 65535) throw new Error(`voko-server: PORT 非法:${raw}`) return port } /** * @param {string|undefined} raw * @returns {'debug'|'info'|'warn'|'error'} */ function parseLogLevel(raw) { const level = raw || 'info' switch (level) { case 'debug': case 'info': case 'warn': case 'error': return level default: throw new Error(`voko-server: LOG_LEVEL 非法:${level}`) } }