env.js
2.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 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<string, string|undefined>} 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}`)
}
}