-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.js
More file actions
65 lines (55 loc) · 2.08 KB
/
Copy pathmain.js
File metadata and controls
65 lines (55 loc) · 2.08 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
/**
* 文件职责:XChat 唯一生产入口,负责加载环境、建立数据库和启动 HTTP/WebSocket 服务。
* 架构位置:最外层 Composition Root,只进行依赖装配,不承载任何业务规则。
* 上下游依赖:上游为 systemd 或命令行;下游依次调用配置、数据库和应用工厂。
*/
import { existsSync } from "node:fs";
import process from "node:process";
import { createApp } from "./server/app.js";
import { loadConfig } from "./server/config.js";
import { createDatabase } from "./server/database/database.js";
// Node 22 原生支持 .env 加载。systemd 已注入的变量优先级更高,loadEnvFile 不会覆盖现有值。
if (existsSync(".env")) {
process.loadEnvFile(".env");
}
const config = loadConfig(process.env);
const database = await createDatabase(config);
const app = await createApp({ config, database });
let shuttingDown = false;
/**
* 执行有超时保护的优雅关闭。
* @param {string} signal 收到的系统信号,用于日志追踪。
* @returns {Promise<void>} 所有监听器、WebSocket 和数据库关闭后完成。
* @throws 关闭超时或底层资源关闭失败时设置非零退出码。
*/
async function shutdown(signal) {
if (shuttingDown) return;
shuttingDown = true;
app.log.info({ signal }, "收到退出信号,开始优雅关闭 XChat");
const timer = setTimeout(() => {
app.log.error("优雅关闭超过 15 秒,强制退出");
process.exit(1);
}, 15_000);
timer.unref();
try {
await app.close();
database.close();
clearTimeout(timer);
} catch (error) {
app.log.error({ err: error }, "关闭 XChat 资源失败");
process.exitCode = 1;
}
}
process.once("SIGTERM", () => void shutdown("SIGTERM"));
process.once("SIGINT", () => void shutdown("SIGINT"));
try {
await app.listen({ host: config.host, port: config.port });
app.log.info(
{ host: config.host, port: config.port, dataDir: config.dataDir },
"XChat 服务启动完成",
);
} catch (error) {
app.log.fatal({ err: error }, "XChat 启动失败");
database.close();
process.exit(1);
}