@sfmc-bds/db-server 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/cpu-usage.d.ts +43 -0
- package/dist/domain/cpu-usage.d.ts.map +1 -0
- package/dist/domain/cpu-usage.js +71 -0
- package/dist/domain/cpu-usage.js.map +7 -0
- package/dist/domain/metrics.d.ts +8 -0
- package/dist/domain/metrics.d.ts.map +1 -1
- package/dist/domain/metrics.js +16 -4
- package/dist/domain/metrics.js.map +2 -2
- package/dist/domain/process-resources.d.ts +2 -0
- package/dist/domain/process-resources.d.ts.map +1 -1
- package/dist/domain/process-resources.js.map +2 -2
- package/dist/domain/scoreboard.d.ts +122 -0
- package/dist/domain/scoreboard.d.ts.map +1 -0
- package/dist/domain/scoreboard.js +134 -0
- package/dist/domain/scoreboard.js.map +7 -0
- package/dist/domain/tracked-processes.d.ts +2 -0
- package/dist/domain/tracked-processes.d.ts.map +1 -1
- package/dist/domain/tracked-processes.js.map +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +2 -2
- package/dist/routes/db-routes.d.ts +1 -0
- package/dist/routes/db-routes.d.ts.map +1 -1
- package/dist/routes/db-routes.js +5 -0
- package/dist/routes/db-routes.js.map +2 -2
- package/dist/routes/scoreboard.d.ts +20 -0
- package/dist/routes/scoreboard.d.ts.map +1 -0
- package/dist/routes/scoreboard.js +30 -0
- package/dist/routes/scoreboard.js.map +7 -0
- package/dist/routes/status.d.ts +6 -1
- package/dist/routes/status.d.ts.map +1 -1
- package/dist/routes/status.js +33 -8
- package/dist/routes/status.js.map +2 -2
- package/dist/tx-runner.d.ts +10 -0
- package/dist/tx-runner.d.ts.map +1 -1
- package/dist/tx-runner.js +17 -0
- package/dist/tx-runner.js.map +2 -2
- package/package.json +9 -4
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const SCOREBOARD_REPORT_LIMITS = { objectives: 2e3, participants: 1e5, scores: 5e5, name: 512 };
|
|
2
|
+
const SCOREBOARD_WATCH_MS = 3e4;
|
|
3
|
+
const SCOREBOARD_FRESH_MS = 1e4;
|
|
4
|
+
const PARTICIPANT_TYPES = /* @__PURE__ */ new Set(["player", "fakePlayer", "entity"]);
|
|
5
|
+
const DISPLAY_SLOTS = /* @__PURE__ */ new Set(["sidebar", "list", "belowName"]);
|
|
6
|
+
const isText = (value, allowEmpty = true) => typeof value === "string" && value.length <= SCOREBOARD_REPORT_LIMITS.name && (allowEmpty || value.length > 0);
|
|
7
|
+
const isInt32 = (value) => typeof value === "number" && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647;
|
|
8
|
+
const timestamp = (value, now) => typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : now;
|
|
9
|
+
function parseScoreboardReport(data, now = Date.now()) {
|
|
10
|
+
if (data.kind === "idle") return { kind: "idle" };
|
|
11
|
+
if (!isText(data.hash, false)) return null;
|
|
12
|
+
if (data.kind === "same") return { kind: "same", hash: data.hash, generatedAt: timestamp(data.generatedAt, now) };
|
|
13
|
+
if (data.kind !== "full") return null;
|
|
14
|
+
const rawParticipants = data.participants;
|
|
15
|
+
const rawObjectives = data.objectives;
|
|
16
|
+
const rawDisplay = data.displaySlots ?? [];
|
|
17
|
+
if (!Array.isArray(rawParticipants) || rawParticipants.length > SCOREBOARD_REPORT_LIMITS.participants) return null;
|
|
18
|
+
if (!Array.isArray(rawObjectives) || rawObjectives.length > SCOREBOARD_REPORT_LIMITS.objectives) return null;
|
|
19
|
+
if (!Array.isArray(rawDisplay) || rawDisplay.length > DISPLAY_SLOTS.size) return null;
|
|
20
|
+
const participants = [];
|
|
21
|
+
const known = /* @__PURE__ */ new Set();
|
|
22
|
+
for (const row of rawParticipants) {
|
|
23
|
+
if (!row || typeof row !== "object") return null;
|
|
24
|
+
const { id, name, type, online } = row;
|
|
25
|
+
if (!isInt32(id) || known.has(id) || !isText(name) || typeof type !== "string" || !PARTICIPANT_TYPES.has(type) || typeof online !== "boolean") return null;
|
|
26
|
+
known.add(id);
|
|
27
|
+
participants.push({ id, name, type, online });
|
|
28
|
+
}
|
|
29
|
+
const objectives = [];
|
|
30
|
+
const objectiveIds = /* @__PURE__ */ new Set();
|
|
31
|
+
let scoreCount = 0;
|
|
32
|
+
for (const row of rawObjectives) {
|
|
33
|
+
if (!row || typeof row !== "object") return null;
|
|
34
|
+
const { id, displayName, scores } = row;
|
|
35
|
+
if (!isText(id, false) || objectiveIds.has(id) || !isText(displayName) || !Array.isArray(scores)) return null;
|
|
36
|
+
scoreCount += scores.length;
|
|
37
|
+
if (scoreCount > SCOREBOARD_REPORT_LIMITS.scores) return null;
|
|
38
|
+
const pairs = [];
|
|
39
|
+
for (const pair of scores) {
|
|
40
|
+
if (!Array.isArray(pair) || pair.length !== 2 || !known.has(pair[0]) || !isInt32(pair[1])) return null;
|
|
41
|
+
pairs.push([pair[0], pair[1]]);
|
|
42
|
+
}
|
|
43
|
+
objectiveIds.add(id);
|
|
44
|
+
objectives.push({ id, displayName, scores: pairs });
|
|
45
|
+
}
|
|
46
|
+
const displaySlots = [];
|
|
47
|
+
for (const row of rawDisplay) {
|
|
48
|
+
if (!row || typeof row !== "object") return null;
|
|
49
|
+
const { slot, objectiveId, sortOrder } = row;
|
|
50
|
+
if (typeof slot !== "string" || !DISPLAY_SLOTS.has(slot) || !isText(objectiveId, false)) return null;
|
|
51
|
+
if (sortOrder !== "ascending" && sortOrder !== "descending") return null;
|
|
52
|
+
displaySlots.push({ slot, objectiveId, sortOrder });
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
kind: "full",
|
|
56
|
+
hash: data.hash,
|
|
57
|
+
generatedAt: timestamp(data.generatedAt, now),
|
|
58
|
+
participants,
|
|
59
|
+
objectives,
|
|
60
|
+
displaySlots,
|
|
61
|
+
truncated: data.truncated === true
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
class ScoreboardStore {
|
|
65
|
+
snapshot = null;
|
|
66
|
+
/** 脚本最后一次确认快照与世界一致的时间(游戏内时间戳) */
|
|
67
|
+
verifiedAt = 0;
|
|
68
|
+
/** 数据服务最后一次收到确认的时间(本机时间),用于判断新鲜度 */
|
|
69
|
+
confirmedAt = 0;
|
|
70
|
+
/** 在此之前视为有人在看 */
|
|
71
|
+
watchUntil = 0;
|
|
72
|
+
/** 最后一次收到任意上报的时间(本机时间);从未收到时为 null */
|
|
73
|
+
reportedAt = null;
|
|
74
|
+
/** 处理一次上报,返回给脚本的指示 */
|
|
75
|
+
report(report, now = Date.now()) {
|
|
76
|
+
this.reportedAt = now;
|
|
77
|
+
if (report.kind === "full") {
|
|
78
|
+
const { kind: _kind, ...snapshot } = report;
|
|
79
|
+
this.snapshot = snapshot;
|
|
80
|
+
this.verifiedAt = snapshot.generatedAt;
|
|
81
|
+
this.confirmedAt = now;
|
|
82
|
+
} else if (report.kind === "same") {
|
|
83
|
+
if (!this.snapshot || this.snapshot.hash !== report.hash) return { watching: this.watching(now), needFull: true };
|
|
84
|
+
this.verifiedAt = Math.max(this.verifiedAt, report.generatedAt);
|
|
85
|
+
this.confirmedAt = now;
|
|
86
|
+
}
|
|
87
|
+
return { watching: this.watching(now), needFull: false };
|
|
88
|
+
}
|
|
89
|
+
/** 读取快照并延长关注期,使脚本继续同步 */
|
|
90
|
+
read(now = Date.now()) {
|
|
91
|
+
this.watchUntil = now + SCOREBOARD_WATCH_MS;
|
|
92
|
+
const snapshot = this.snapshot;
|
|
93
|
+
const fresh = snapshot !== null && now - this.confirmedAt <= SCOREBOARD_FRESH_MS;
|
|
94
|
+
const scriptConnected = this.reportedAt !== null && now - this.reportedAt <= SCOREBOARD_FRESH_MS;
|
|
95
|
+
if (!snapshot) {
|
|
96
|
+
return {
|
|
97
|
+
available: false,
|
|
98
|
+
fresh: false,
|
|
99
|
+
updatedAt: null,
|
|
100
|
+
objectives: [],
|
|
101
|
+
displaySlots: [],
|
|
102
|
+
truncated: false,
|
|
103
|
+
note: scriptConnected ? "\u6B63\u5728\u7B49\u5F85\u6E38\u620F\u5185\u540C\u6B65\u8BA1\u5206\u677F" : "\u6E38\u620F\u5185\u5E73\u53F0\u811A\u672C\u672A\u8FDE\u63A5\uFF0C\u8BF7\u786E\u8BA4 BDS \u6B63\u5728\u8FD0\u884C",
|
|
104
|
+
scriptConnected
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const byId = new Map(snapshot.participants.map((row) => [row.id, row]));
|
|
108
|
+
return {
|
|
109
|
+
available: true,
|
|
110
|
+
fresh,
|
|
111
|
+
updatedAt: this.verifiedAt,
|
|
112
|
+
objectives: snapshot.objectives.map((objective) => ({
|
|
113
|
+
id: objective.id,
|
|
114
|
+
displayName: objective.displayName,
|
|
115
|
+
participants: objective.scores.map(([id, score]) => ({ ...byId.get(id), score }))
|
|
116
|
+
})),
|
|
117
|
+
displaySlots: snapshot.displaySlots,
|
|
118
|
+
truncated: snapshot.truncated,
|
|
119
|
+
...fresh ? {} : { note: scriptConnected ? "\u8BA1\u5206\u677F\u5FEB\u7167\u5DF2\u8FC7\u671F\uFF0C\u6B63\u5728\u7B49\u5F85\u6E38\u620F\u5185\u91CD\u65B0\u540C\u6B65" : "\u6E38\u620F\u5185\u5E73\u53F0\u811A\u672C\u672A\u8FDE\u63A5\uFF0C\u663E\u793A\u7684\u662F\u6700\u540E\u4E00\u6B21\u540C\u6B65\u7684\u8BA1\u5206\u677F" },
|
|
120
|
+
scriptConnected
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
watching(now) {
|
|
124
|
+
return now < this.watchUntil;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
SCOREBOARD_FRESH_MS,
|
|
129
|
+
SCOREBOARD_REPORT_LIMITS,
|
|
130
|
+
SCOREBOARD_WATCH_MS,
|
|
131
|
+
ScoreboardStore,
|
|
132
|
+
parseScoreboardReport
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=scoreboard.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/domain/scoreboard.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * domain/scoreboard.ts \u2014 \u4E16\u754C\u8BA1\u5206\u677F\u5FEB\u7167\u7684\u63A5\u6536\u4E0E\u8BFB\u53D6\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\n * - \u6E38\u620F\u5185\u5E73\u53F0\u811A\u672C\uFF08SDK runtime/scoreboard-sync\uFF09\u6BCF\u7EA6 2 \u79D2 POST /api/sfmc/scoreboard/live\uFF1B\n * - CLI \u7684 scoreboard.read \u7ECF GET /api/sfmc/scoreboard \u8BFB\u53D6\uFF0C\u4F9B\u684C\u9762\u7AEF\u4E0E\u5C0F\u7A0B\u5E8F\u300C\u8BA1\u5206\u677F\u300D\u9875\u5C55\u793A\u3002\n *\n * \u904D\u5386\u5168\u90E8\u8BA1\u5206\u9879\u6709\u5F00\u9500\uFF0C\u6240\u4EE5\u53EA\u5728\u6709\u4EBA\u67E5\u770B\u65F6\u540C\u6B65\uFF1A\u6BCF\u6B21\u8BFB\u53D6\u628A\u300C\u5173\u6CE8\u671F\u300D\u5EF6\u957F WATCH_MS\uFF0C\n * \u6E38\u620F\u5185\u811A\u672C\u4ECE\u4E0A\u62A5\u7684\u54CD\u5E94\u91CC\u5F97\u77E5 watching \u540E\u624D\u8BA1\u7B97\u5FEB\u7167\uFF1B\u65E0\u4EBA\u67E5\u770B\u65F6\u53EA\u53D1\u7A7A\u95F2\u63A2\u8BE2\u3002\n * \u5FEB\u7167\u5185\u5BB9\u4E0D\u53D8\u65F6\u53EA\u4E0A\u62A5\u54C8\u5E0C\uFF08kind: \"same\"\uFF09\uFF0C\u6570\u636E\u670D\u52A1\u636E\u6B64\u786E\u8BA4\u5FEB\u7167\u4ECD\u7136\u65B0\u9C9C\uFF1B\n * \u6570\u636E\u670D\u52A1\u91CD\u542F\u4E22\u5931\u5FEB\u7167\u3001\u6216\u54C8\u5E0C\u5BF9\u4E0D\u4E0A\u65F6\uFF0C\u54CD\u5E94 needFull \u8BA9\u811A\u672C\u4E0B\u6B21\u8865\u53D1\u5B8C\u6574\u5FEB\u7167\u3002\n * \u5FEB\u7167\u53EA\u4FDD\u5B58\u5728\u5185\u5B58\u4E2D\uFF0C\u4E0D\u843D\u5E93\u3002\n */\n\n/** \u53C2\u4E0E\u8005\u7C7B\u578B\uFF08\u4E0E @sfmc-bds/management \u7684 ScoreboardParticipantType \u4E00\u81F4\uFF09 */\nexport type ScoreboardParticipantType = \"player\" | \"fakePlayer\" | \"entity\";\n/** \u663E\u793A\u4F4D\uFF08\u4E0E @sfmc-bds/management \u7684 ScoreboardDisplaySlot \u4E00\u81F4\uFF09 */\nexport type ScoreboardDisplaySlot = \"sidebar\" | \"list\" | \"belowName\";\n\n/** \u4E0A\u62A5\u4E2D\u7684\u53C2\u4E0E\u8005\uFF08\u5404\u8BA1\u5206\u9879\u5171\u7528\uFF0C\u5206\u6570\u6309 ID \u5F15\u7528\uFF09 */\nexport interface ScoreboardReportParticipant {\n id: number;\n name: string;\n type: ScoreboardParticipantType;\n online: boolean;\n}\n\n/** \u4E0A\u62A5\u4E2D\u7684\u8BA1\u5206\u9879\uFF1Bscores \u4E3A [\u53C2\u4E0E\u8005 ID, \u5206\u6570] \u5BF9\uFF0C\u907F\u514D\u5728\u6BCF\u4E2A\u8BA1\u5206\u9879\u91CC\u91CD\u590D\u53C2\u4E0E\u8005\u4FE1\u606F */\nexport interface ScoreboardReportObjective {\n id: string;\n displayName: string;\n scores: Array<[number, number]>;\n}\n\n/** \u4E0A\u62A5\u4E2D\u7684\u663E\u793A\u4F4D */\nexport interface ScoreboardReportDisplay {\n slot: ScoreboardDisplaySlot;\n objectiveId: string;\n sortOrder: \"ascending\" | \"descending\";\n}\n\n/** \u5B8C\u6574\u5FEB\u7167 */\nexport interface ScoreboardSnapshot {\n hash: string;\n /** \u6E38\u620F\u5185\u751F\u6210\u5FEB\u7167\u7684\u65F6\u95F4\uFF08\u6BEB\u79D2\u65F6\u95F4\u6233\uFF09 */\n generatedAt: number;\n participants: ScoreboardReportParticipant[];\n objectives: ScoreboardReportObjective[];\n displaySlots: ScoreboardReportDisplay[];\n truncated: boolean;\n}\n\n/** \u89E3\u6790\u540E\u7684\u4E0A\u62A5\uFF1A\u7A7A\u95F2\u63A2\u8BE2 / \u5185\u5BB9\u672A\u53D8 / \u5B8C\u6574\u5FEB\u7167 */\nexport type ScoreboardReport =\n | { kind: \"idle\" }\n | { kind: \"same\"; hash: string; generatedAt: number }\n | ({ kind: \"full\" } & ScoreboardSnapshot);\n\n/** \u4E0A\u62A5\u7684\u54CD\u5E94\uFF1A\u662F\u5426\u6709\u4EBA\u5728\u770B\uFF08\u51B3\u5B9A\u811A\u672C\u662F\u5426\u8BA1\u7B97\u5FEB\u7167\uFF09\uFF0C\u4EE5\u53CA\u662F\u5426\u9700\u8981\u8865\u53D1\u5B8C\u6574\u5FEB\u7167 */\nexport interface ScoreboardReportReply {\n watching: boolean;\n needFull: boolean;\n}\n\n/** \u4E0E @sfmc-bds/management \u7684 ScoreboardReadResult \u540C\u6784\uFF1BCLI \u4FA7\u7531\u7C7B\u578B\u68C0\u67E5\u4FDD\u8BC1\u4E24\u8FB9\u4E0D\u6F02\u79FB */\nexport interface ScoreboardReadView {\n available: boolean;\n fresh: boolean;\n updatedAt: number | null;\n objectives: Array<{\n id: string;\n displayName: string;\n participants: Array<ScoreboardReportParticipant & { score: number }>;\n }>;\n displaySlots: ScoreboardReportDisplay[];\n truncated: boolean;\n note?: string;\n /**\n * \u6E38\u620F\u5185\u811A\u672C\u6700\u8FD1\u662F\u5426\u4E0A\u62A5\u8FC7\uFF08\u542B\u7A7A\u95F2\u63A2\u8BE2\uFF09\u3002\u4E0D\u5C5E\u4E8E\u7BA1\u7406\u534F\u8BAE\uFF0CCLI \u7528\u6765\u5224\u65AD\u662F\u5426\u503C\u5F97\u7B49\u5F85\u65B0\u5FEB\u7167\uFF1A\n * \u811A\u672C\u6CA1\u5728\u4E0A\u62A5\uFF08BDS \u672A\u8FD0\u884C\u6216\u5E73\u53F0\u811A\u672C\u672A\u52A0\u8F7D\uFF09\u65F6\u76F4\u63A5\u8FD4\u56DE\uFF0C\u4E0D\u7A7A\u7B49\u3002\n */\n scriptConnected: boolean;\n}\n\n/**\n * \u4E0A\u62A5\u4F53\u79EF\u4E0A\u9650\uFF1A\u8D85\u8FC7\u5373\u89C6\u4E3A\u5F02\u5E38\u8BF7\u6C42\u62D2\u6536\u3002\u6E38\u620F\u5185\u811A\u672C\u6709\u81EA\u5DF1\u7684\u66F4\u5C0F\u4E0A\u9650\uFF08\u8D85\u51FA\u65F6\u622A\u65AD\u5E76\u6807\u8BB0 truncated\uFF09\uFF0C\n * \u8FD9\u91CC\u53EA\u9632\u6B62\u5F02\u5E38\u8BF7\u6C42\u5360\u6EE1\u5185\u5B58\u3002\n */\nexport const SCOREBOARD_REPORT_LIMITS = { objectives: 2000, participants: 100_000, scores: 500_000, name: 512 } as const;\n/** \u4E00\u6B21\u8BFB\u53D6\u540E\u4FDD\u6301\u300C\u6709\u4EBA\u5728\u770B\u300D\u7684\u65F6\u957F */\nexport const SCOREBOARD_WATCH_MS = 30_000;\n/** \u5FEB\u7167\u5728\u6700\u540E\u4E00\u6B21\u786E\u8BA4\u540E\u591A\u4E45\u5185\u89C6\u4E3A\u65B0\u9C9C\uFF08\u811A\u672C\u7EA6 2 \u79D2\u786E\u8BA4\u4E00\u6B21\uFF0C\u7559\u51FA\u51E0\u6B21\u5931\u8D25\u7684\u4F59\u91CF\uFF09 */\nexport const SCOREBOARD_FRESH_MS = 10_000;\n\nconst PARTICIPANT_TYPES = new Set<string>([\"player\", \"fakePlayer\", \"entity\"]);\nconst DISPLAY_SLOTS = new Set<string>([\"sidebar\", \"list\", \"belowName\"]);\n\n/** \u6709\u9650\u957F\u5EA6\u7684\u6587\u672C */\nconst isText = (value: unknown, allowEmpty = true): value is string =>\n typeof value === \"string\" && value.length <= SCOREBOARD_REPORT_LIMITS.name && (allowEmpty || value.length > 0);\n/** 32 \u4F4D\u6709\u7B26\u53F7\u6574\u6570\uFF08\u8BA1\u5206\u677F\u5206\u6570\u4E0E\u8EAB\u4EFD ID \u7684\u53D6\u503C\u8303\u56F4\uFF09 */\nconst isInt32 = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647;\n/** \u5408\u7406\u7684\u65F6\u95F4\u6233\uFF1B\u7F3A\u7701\u65F6\u7528\u63A5\u6536\u65F6\u95F4 */\nconst timestamp = (value: unknown, now: number): number =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value > 0 ? value : now;\n\n/**\n * \u6821\u9A8C\u5E76\u89E3\u6790\u4E00\u6B21\u4E0A\u62A5\uFF1B\u683C\u5F0F\u4E0D\u5BF9\u65F6\u8FD4\u56DE null\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1APOST /api/sfmc/scoreboard/live \u6536\u5230\u7684\u8BF7\u6C42\u4F53\u3002\n */\nexport function parseScoreboardReport(data: Record<string, unknown>, now = Date.now()): ScoreboardReport | null {\n if (data.kind === \"idle\") return { kind: \"idle\" };\n if (!isText(data.hash, false)) return null;\n if (data.kind === \"same\") return { kind: \"same\", hash: data.hash, generatedAt: timestamp(data.generatedAt, now) };\n if (data.kind !== \"full\") return null;\n\n const rawParticipants = data.participants;\n const rawObjectives = data.objectives;\n const rawDisplay = data.displaySlots ?? [];\n if (!Array.isArray(rawParticipants) || rawParticipants.length > SCOREBOARD_REPORT_LIMITS.participants) return null;\n if (!Array.isArray(rawObjectives) || rawObjectives.length > SCOREBOARD_REPORT_LIMITS.objectives) return null;\n if (!Array.isArray(rawDisplay) || rawDisplay.length > DISPLAY_SLOTS.size) return null;\n\n const participants: ScoreboardReportParticipant[] = [];\n const known = new Set<number>();\n for (const row of rawParticipants) {\n if (!row || typeof row !== \"object\") return null;\n const { id, name, type, online } = row as Record<string, unknown>;\n if (!isInt32(id) || known.has(id) || !isText(name) || typeof type !== \"string\" || !PARTICIPANT_TYPES.has(type) || typeof online !== \"boolean\") return null;\n known.add(id);\n participants.push({ id, name, type: type as ScoreboardParticipantType, online });\n }\n\n const objectives: ScoreboardReportObjective[] = [];\n const objectiveIds = new Set<string>();\n let scoreCount = 0;\n for (const row of rawObjectives) {\n if (!row || typeof row !== \"object\") return null;\n const { id, displayName, scores } = row as Record<string, unknown>;\n if (!isText(id, false) || objectiveIds.has(id) || !isText(displayName) || !Array.isArray(scores)) return null;\n scoreCount += scores.length;\n if (scoreCount > SCOREBOARD_REPORT_LIMITS.scores) return null;\n const pairs: Array<[number, number]> = [];\n for (const pair of scores) {\n if (!Array.isArray(pair) || pair.length !== 2 || !known.has(pair[0]) || !isInt32(pair[1])) return null;\n pairs.push([pair[0], pair[1]]);\n }\n objectiveIds.add(id);\n objectives.push({ id, displayName, scores: pairs });\n }\n\n const displaySlots: ScoreboardReportDisplay[] = [];\n for (const row of rawDisplay) {\n if (!row || typeof row !== \"object\") return null;\n const { slot, objectiveId, sortOrder } = row as Record<string, unknown>;\n if (typeof slot !== \"string\" || !DISPLAY_SLOTS.has(slot) || !isText(objectiveId, false)) return null;\n if (sortOrder !== \"ascending\" && sortOrder !== \"descending\") return null;\n displaySlots.push({ slot: slot as ScoreboardDisplaySlot, objectiveId, sortOrder });\n }\n\n return {\n kind: \"full\",\n hash: data.hash,\n generatedAt: timestamp(data.generatedAt, now),\n participants,\n objectives,\n displaySlots,\n truncated: data.truncated === true,\n };\n}\n\n/**\n * \u5185\u5B58\u4E2D\u7684\u8BA1\u5206\u677F\u5FEB\u7167\u4E0E\u5173\u6CE8\u72B6\u6001\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1Aroutes/scoreboard.ts \u6BCF\u4E2A\u6570\u636E\u670D\u52A1\u8FDB\u7A0B\u6301\u6709\u4E00\u4E2A\u5B9E\u4F8B\u3002\n */\nexport class ScoreboardStore {\n private snapshot: ScoreboardSnapshot | null = null;\n /** \u811A\u672C\u6700\u540E\u4E00\u6B21\u786E\u8BA4\u5FEB\u7167\u4E0E\u4E16\u754C\u4E00\u81F4\u7684\u65F6\u95F4\uFF08\u6E38\u620F\u5185\u65F6\u95F4\u6233\uFF09 */\n private verifiedAt = 0;\n /** \u6570\u636E\u670D\u52A1\u6700\u540E\u4E00\u6B21\u6536\u5230\u786E\u8BA4\u7684\u65F6\u95F4\uFF08\u672C\u673A\u65F6\u95F4\uFF09\uFF0C\u7528\u4E8E\u5224\u65AD\u65B0\u9C9C\u5EA6 */\n private confirmedAt = 0;\n /** \u5728\u6B64\u4E4B\u524D\u89C6\u4E3A\u6709\u4EBA\u5728\u770B */\n private watchUntil = 0;\n /** \u6700\u540E\u4E00\u6B21\u6536\u5230\u4EFB\u610F\u4E0A\u62A5\u7684\u65F6\u95F4\uFF08\u672C\u673A\u65F6\u95F4\uFF09\uFF1B\u4ECE\u672A\u6536\u5230\u65F6\u4E3A null */\n private reportedAt: number | null = null;\n\n /** \u5904\u7406\u4E00\u6B21\u4E0A\u62A5\uFF0C\u8FD4\u56DE\u7ED9\u811A\u672C\u7684\u6307\u793A */\n report(report: ScoreboardReport, now = Date.now()): ScoreboardReportReply {\n this.reportedAt = now;\n if (report.kind === \"full\") {\n const { kind: _kind, ...snapshot } = report;\n this.snapshot = snapshot;\n this.verifiedAt = snapshot.generatedAt;\n this.confirmedAt = now;\n } else if (report.kind === \"same\") {\n if (!this.snapshot || this.snapshot.hash !== report.hash) return { watching: this.watching(now), needFull: true };\n this.verifiedAt = Math.max(this.verifiedAt, report.generatedAt);\n this.confirmedAt = now;\n }\n return { watching: this.watching(now), needFull: false };\n }\n\n /** \u8BFB\u53D6\u5FEB\u7167\u5E76\u5EF6\u957F\u5173\u6CE8\u671F\uFF0C\u4F7F\u811A\u672C\u7EE7\u7EED\u540C\u6B65 */\n read(now = Date.now()): ScoreboardReadView {\n this.watchUntil = now + SCOREBOARD_WATCH_MS;\n const snapshot = this.snapshot;\n const fresh = snapshot !== null && now - this.confirmedAt <= SCOREBOARD_FRESH_MS;\n const scriptConnected = this.reportedAt !== null && now - this.reportedAt <= SCOREBOARD_FRESH_MS;\n if (!snapshot) {\n return {\n available: false,\n fresh: false,\n updatedAt: null,\n objectives: [],\n displaySlots: [],\n truncated: false,\n note: scriptConnected ? \"\u6B63\u5728\u7B49\u5F85\u6E38\u620F\u5185\u540C\u6B65\u8BA1\u5206\u677F\" : \"\u6E38\u620F\u5185\u5E73\u53F0\u811A\u672C\u672A\u8FDE\u63A5\uFF0C\u8BF7\u786E\u8BA4 BDS \u6B63\u5728\u8FD0\u884C\",\n scriptConnected,\n };\n }\n const byId = new Map(snapshot.participants.map((row) => [row.id, row]));\n return {\n available: true,\n fresh,\n updatedAt: this.verifiedAt,\n objectives: snapshot.objectives.map((objective) => ({\n id: objective.id,\n displayName: objective.displayName,\n participants: objective.scores.map(([id, score]) => ({ ...byId.get(id)!, score })),\n })),\n displaySlots: snapshot.displaySlots,\n truncated: snapshot.truncated,\n ...(fresh ? {} : { note: scriptConnected ? \"\u8BA1\u5206\u677F\u5FEB\u7167\u5DF2\u8FC7\u671F\uFF0C\u6B63\u5728\u7B49\u5F85\u6E38\u620F\u5185\u91CD\u65B0\u540C\u6B65\" : \"\u6E38\u620F\u5185\u5E73\u53F0\u811A\u672C\u672A\u8FDE\u63A5\uFF0C\u663E\u793A\u7684\u662F\u6700\u540E\u4E00\u6B21\u540C\u6B65\u7684\u8BA1\u5206\u677F\" }),\n scriptConnected,\n };\n }\n\n private watching(now: number): boolean {\n return now < this.watchUntil;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAwFO,MAAM,2BAA2B,EAAE,YAAY,KAAM,cAAc,KAAS,QAAQ,KAAS,MAAM,IAAI;AAEvG,MAAM,sBAAsB;AAE5B,MAAM,sBAAsB;AAEnC,MAAM,oBAAoB,oBAAI,IAAY,CAAC,UAAU,cAAc,QAAQ,CAAC;AAC5E,MAAM,gBAAgB,oBAAI,IAAY,CAAC,WAAW,QAAQ,WAAW,CAAC;AAGtE,MAAM,SAAS,CAAC,OAAgB,aAAa,SAC3C,OAAO,UAAU,YAAY,MAAM,UAAU,yBAAyB,SAAS,cAAc,MAAM,SAAS;AAE9G,MAAM,UAAU,CAAC,UACf,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,SAAS,eAAe,SAAS;AAE3F,MAAM,YAAY,CAAC,OAAgB,QACjC,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,KAAK,QAAQ,IAAI,QAAQ;AAM3E,SAAS,sBAAsB,MAA+B,MAAM,KAAK,IAAI,GAA4B;AAC9G,MAAI,KAAK,SAAS,OAAQ,QAAO,EAAE,MAAM,OAAO;AAChD,MAAI,CAAC,OAAO,KAAK,MAAM,KAAK,EAAG,QAAO;AACtC,MAAI,KAAK,SAAS,OAAQ,QAAO,EAAE,MAAM,QAAQ,MAAM,KAAK,MAAM,aAAa,UAAU,KAAK,aAAa,GAAG,EAAE;AAChH,MAAI,KAAK,SAAS,OAAQ,QAAO;AAEjC,QAAM,kBAAkB,KAAK;AAC7B,QAAM,gBAAgB,KAAK;AAC3B,QAAM,aAAa,KAAK,gBAAgB,CAAC;AACzC,MAAI,CAAC,MAAM,QAAQ,eAAe,KAAK,gBAAgB,SAAS,yBAAyB,aAAc,QAAO;AAC9G,MAAI,CAAC,MAAM,QAAQ,aAAa,KAAK,cAAc,SAAS,yBAAyB,WAAY,QAAO;AACxG,MAAI,CAAC,MAAM,QAAQ,UAAU,KAAK,WAAW,SAAS,cAAc,KAAM,QAAO;AAEjF,QAAM,eAA8C,CAAC;AACrD,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,OAAO,iBAAiB;AACjC,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,UAAM,EAAE,IAAI,MAAM,MAAM,OAAO,IAAI;AACnC,QAAI,CAAC,QAAQ,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,KAAK,OAAO,SAAS,YAAY,CAAC,kBAAkB,IAAI,IAAI,KAAK,OAAO,WAAW,UAAW,QAAO;AACtJ,UAAM,IAAI,EAAE;AACZ,iBAAa,KAAK,EAAE,IAAI,MAAM,MAAyC,OAAO,CAAC;AAAA,EACjF;AAEA,QAAM,aAA0C,CAAC;AACjD,QAAM,eAAe,oBAAI,IAAY;AACrC,MAAI,aAAa;AACjB,aAAW,OAAO,eAAe;AAC/B,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,UAAM,EAAE,IAAI,aAAa,OAAO,IAAI;AACpC,QAAI,CAAC,OAAO,IAAI,KAAK,KAAK,aAAa,IAAI,EAAE,KAAK,CAAC,OAAO,WAAW,KAAK,CAAC,MAAM,QAAQ,MAAM,EAAG,QAAO;AACzG,kBAAc,OAAO;AACrB,QAAI,aAAa,yBAAyB,OAAQ,QAAO;AACzD,UAAM,QAAiC,CAAC;AACxC,eAAW,QAAQ,QAAQ;AACzB,UAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,QAAQ,KAAK,CAAC,CAAC,EAAG,QAAO;AAClG,YAAM,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;AAAA,IAC/B;AACA,iBAAa,IAAI,EAAE;AACnB,eAAW,KAAK,EAAE,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,EACpD;AAEA,QAAM,eAA0C,CAAC;AACjD,aAAW,OAAO,YAAY;AAC5B,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,UAAM,EAAE,MAAM,aAAa,UAAU,IAAI;AACzC,QAAI,OAAO,SAAS,YAAY,CAAC,cAAc,IAAI,IAAI,KAAK,CAAC,OAAO,aAAa,KAAK,EAAG,QAAO;AAChG,QAAI,cAAc,eAAe,cAAc,aAAc,QAAO;AACpE,iBAAa,KAAK,EAAE,MAAqC,aAAa,UAAU,CAAC;AAAA,EACnF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,KAAK;AAAA,IACX,aAAa,UAAU,KAAK,aAAa,GAAG;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,KAAK,cAAc;AAAA,EAChC;AACF;AAMO,MAAM,gBAAgB;AAAA,EACnB,WAAsC;AAAA;AAAA,EAEtC,aAAa;AAAA;AAAA,EAEb,cAAc;AAAA;AAAA,EAEd,aAAa;AAAA;AAAA,EAEb,aAA4B;AAAA;AAAA,EAGpC,OAAO,QAA0B,MAAM,KAAK,IAAI,GAA0B;AACxE,SAAK,aAAa;AAClB,QAAI,OAAO,SAAS,QAAQ;AAC1B,YAAM,EAAE,MAAM,OAAO,GAAG,SAAS,IAAI;AACrC,WAAK,WAAW;AAChB,WAAK,aAAa,SAAS;AAC3B,WAAK,cAAc;AAAA,IACrB,WAAW,OAAO,SAAS,QAAQ;AACjC,UAAI,CAAC,KAAK,YAAY,KAAK,SAAS,SAAS,OAAO,KAAM,QAAO,EAAE,UAAU,KAAK,SAAS,GAAG,GAAG,UAAU,KAAK;AAChH,WAAK,aAAa,KAAK,IAAI,KAAK,YAAY,OAAO,WAAW;AAC9D,WAAK,cAAc;AAAA,IACrB;AACA,WAAO,EAAE,UAAU,KAAK,SAAS,GAAG,GAAG,UAAU,MAAM;AAAA,EACzD;AAAA;AAAA,EAGA,KAAK,MAAM,KAAK,IAAI,GAAuB;AACzC,SAAK,aAAa,MAAM;AACxB,UAAM,WAAW,KAAK;AACtB,UAAM,QAAQ,aAAa,QAAQ,MAAM,KAAK,eAAe;AAC7D,UAAM,kBAAkB,KAAK,eAAe,QAAQ,MAAM,KAAK,cAAc;AAC7E,QAAI,CAAC,UAAU;AACb,aAAO;AAAA,QACL,WAAW;AAAA,QACX,OAAO;AAAA,QACP,WAAW;AAAA,QACX,YAAY,CAAC;AAAA,QACb,cAAc,CAAC;AAAA,QACf,WAAW;AAAA,QACX,MAAM,kBAAkB,6EAAiB;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO,IAAI,IAAI,SAAS,aAAa,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;AACtE,WAAO;AAAA,MACL,WAAW;AAAA,MACX;AAAA,MACA,WAAW,KAAK;AAAA,MAChB,YAAY,SAAS,WAAW,IAAI,CAAC,eAAe;AAAA,QAClD,IAAI,UAAU;AAAA,QACd,aAAa,UAAU;AAAA,QACvB,cAAc,UAAU,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,GAAG,KAAK,IAAI,EAAE,GAAI,MAAM,EAAE;AAAA,MACnF,EAAE;AAAA,MACF,cAAc,SAAS;AAAA,MACvB,WAAW,SAAS;AAAA,MACpB,GAAI,QAAQ,CAAC,IAAI,EAAE,MAAM,kBAAkB,6HAAyB,yJAA4B;AAAA,MAChG;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,SAAS,KAAsB;AACrC,WAAO,MAAM,KAAK;AAAA,EACpB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracked-processes.d.ts","sourceRoot":"","sources":["../../src/domain/tracked-processes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,kDAAkD;AAClD,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,IAAI,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"tracked-processes.d.ts","sourceRoot":"","sources":["../../src/domain/tracked-processes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,kDAAkD;AAClD,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,IAAI,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAUD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOpD;AA6BD;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CA8BhH"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/domain/tracked-processes.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * tracked-processes.ts \u2014 \u91C7\u96C6\u672C\u5B9E\u4F8B\u5DF2\u767B\u8BB0\u7684\u5176\u5B83 SFMC \u8FDB\u7A0B\u5185\u5B58\u3002\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\u8FD0\u884C\u6307\u6807\u9664 BDS\u3001\u6570\u636E\u670D\u52A1\u5916\uFF0C\u8FD8\u8981\u5217\u51FA\u5B88\u62A4\u8FDB\u7A0B\u3001QQ \u6865\u3001\u96A7\u9053\u3001LLBot \u548C\u6A21\u5757\u9644\u5C5E\u8FDB\u7A0B\u3002\n * \u53EA\u8BFB\u8FD9\u4E9B\u8FDB\u7A0B\u81EA\u5DF1\u5199\u4E0B\u7684\u8FD0\u884C\u6001\u6587\u4EF6\uFF0C\u4E0D\u6309\u8FDB\u7A0B\u540D\u5168\u673A\u626B\u63CF\uFF0C\u907F\u514D\u628A\u540C\u673A\u53E6\u4E00\u4E2A\u7A0B\u5E8F\u7B97\u8FDB\u6765\u3002\n * \u754C\u9762\u540D\u79F0\u4E0E\u684C\u9762\u7AEF metric-series.ts \u7684 sfmcPartLabel \u4E00\u81F4\u3002\n */\n\nimport { qqRuntimeStatusPath, stateDir } from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { collectProcessResourcesMany } from \"./process-resources.js\";\n\n/** \u4E00\u6761\u5DF2\u786E\u8BA4\u4ECD\u5728\u8FD0\u884C\u7684\u8FDB\u7A0B\u3002\u5185\u5B58\u91C7\u4E0D\u5230\u7684\u4E0D\u8FD4\u56DE\uFF0C\u907F\u514D\u628A\u8FC7\u671F PID \u663E\u793A\u6210\u300C\u8D44\u6E90\u672A\u77E5\u300D\u3002 */\nexport interface TrackedProcess {\n /** daemon\u3001qq\u3001tunnel\u3001llbot\uFF0C\u6216 sidecar:<\u6A21\u5757 id> */\n key: string;\n /** \u4FA7\u680F\u548C\u66F2\u7EBF\u7528\u7684\u540D\u79F0 */\n label: string;\n pid: number;\n running: true;\n memoryMb: number;\n cpuSeconds: number | null;\n}\n\n/** \u5355\u8FDB\u7A0B\u8FD0\u884C\u6001\u6587\u4EF6\u3002\u8DEF\u5F84\u90FD\u5728 `<\u6839>/.sfmc` \u4E0B\uFF0C\u4E0E\u5199\u5165\u65B9\u4F7F\u7528\u7684\u6587\u4EF6\u540D\u4E00\u81F4\u3002 */\nconst ROLE_FILES: Array<{ key: string; label: string; file: (root: string) => string }> = [\n { key: \"daemon\", label: \"\u5B88\u62A4\u8FDB\u7A0B\", file: (root) => path.join(stateDir(root), \"daemon.json\") },\n { key: \"qq\", label: \"QQ \u6865\", file: (root) => qqRuntimeStatusPath(root) },\n { key: \"tunnel\", label: \"\u96A7\u9053\", file: (root) => path.join(stateDir(root), \"tunnel.runtime.json\") },\n { key: \"llbot\", label: \"LLBot\", file: (root) => path.join(stateDir(root), \"llbot.runtime.json\") },\n];\n\n/**\n * \u4ECE\u8FD0\u884C\u6001 JSON \u91CC\u53D6\u51FA pid\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1Adaemon.json\u3001qq.runtime.json \u7B49\u53EA\u501F pid \u5B57\u6BB5\uFF0C\u5176\u4F59\u5185\u5BB9\uFF08\u4F8B\u5982\u9274\u6743 token\uFF09\u4E0D\u8BFB\u53D6\u3001\u4E0D\u8FD4\u56DE\u3002\n */\nexport function readRecordedPid(text: string): number {\n try {\n const pid = (JSON.parse(text) as { pid?: unknown }).pid;\n return typeof pid === \"number\" && Number.isSafeInteger(pid) && pid > 0 ? pid : 0;\n } catch {\n return 0;\n }\n}\n\n/** \u9644\u5C5E\u8FDB\u7A0B\u6E05\u5355\u3002id \u53EA\u63A5\u53D7\u6A21\u5757\u6807\u8BC6\uFF0C\u907F\u514D\u628A\u4EFB\u610F\u5B57\u7B26\u4E32\u5199\u8FDB\u6307\u6807\u952E\u3002 */\nfunction readSidecars(text: string): Array<{ id: string; pid: number }> {\n try {\n const processes = (JSON.parse(text) as { processes?: unknown }).processes;\n if (!Array.isArray(processes)) return [];\n const rows: Array<{ id: string; pid: number }> = [];\n for (const item of processes) {\n if (!item || typeof item !== \"object\") continue;\n const row = item as { id?: unknown; pid?: unknown };\n if (typeof row.id !== \"string\" || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(row.id)) continue;\n if (typeof row.pid !== \"number\" || !Number.isSafeInteger(row.pid) || row.pid <= 0) continue;\n rows.push({ id: row.id, pid: row.pid });\n }\n return rows;\n } catch {\n return [];\n }\n}\n\nfunction readText(file: string): string {\n try {\n return fs.readFileSync(file, \"utf8\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * \u91C7\u96C6\u4ECD\u5728\u8FD0\u884C\u7684\u5DF2\u767B\u8BB0\u8FDB\u7A0B\u3002\n * @param root \u5B9E\u4F8B\u6839\u76EE\u5F55\u3002\n * @param skip \u5DF2\u7ECF\u5355\u72EC\u7EDF\u8BA1\u8FC7\u7684 PID\uFF08\u6570\u636E\u670D\u52A1\u3001\u672C\u5B9E\u4F8B BDS\uFF09\uFF0C\u8FD9\u91CC\u4E0D\u518D\u91CD\u590D\u8BA1\u5165\u3002\n */\nexport async function collectTrackedProcesses(root: string, skip: ReadonlySet<number>): Promise<TrackedProcess[]> {\n const wanted: Array<{ key: string; label: string; pid: number }> = [];\n const seen = new Set<number>(skip);\n for (const role of ROLE_FILES) {\n const pid = readRecordedPid(readText(role.file(root)));\n if (!pid || seen.has(pid)) continue;\n seen.add(pid);\n wanted.push({ key: role.key, label: role.label, pid });\n }\n for (const row of readSidecars(readText(path.join(stateDir(root), \"sidecars.runtime.json\")))) {\n if (seen.has(row.pid)) continue;\n seen.add(row.pid);\n wanted.push({ key: `sidecar:${row.id}`, label: row.id, pid: row.pid });\n }\n if (!wanted.length) return [];\n const measured = await collectProcessResourcesMany(wanted.map((row) => row.pid));\n const found: TrackedProcess[] = [];\n for (const row of wanted) {\n const usage = measured.get(row.pid);\n if (!usage) continue;\n found.push({\n key: row.key,\n label: row.label,\n pid: row.pid,\n running: true,\n memoryMb: usage.memoryMb,\n cpuSeconds: usage.cpuSeconds,\n });\n }\n return found;\n}\n"],
|
|
5
|
-
"mappings": "AAQA,SAAS,qBAAqB,gBAAgB;AAC9C,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,mCAAmC;
|
|
4
|
+
"sourcesContent": ["/**\n * tracked-processes.ts \u2014 \u91C7\u96C6\u672C\u5B9E\u4F8B\u5DF2\u767B\u8BB0\u7684\u5176\u5B83 SFMC \u8FDB\u7A0B\u5185\u5B58\u3002\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\u8FD0\u884C\u6307\u6807\u9664 BDS\u3001\u6570\u636E\u670D\u52A1\u5916\uFF0C\u8FD8\u8981\u5217\u51FA\u5B88\u62A4\u8FDB\u7A0B\u3001QQ \u6865\u3001\u96A7\u9053\u3001LLBot \u548C\u6A21\u5757\u9644\u5C5E\u8FDB\u7A0B\u3002\n * \u53EA\u8BFB\u8FD9\u4E9B\u8FDB\u7A0B\u81EA\u5DF1\u5199\u4E0B\u7684\u8FD0\u884C\u6001\u6587\u4EF6\uFF0C\u4E0D\u6309\u8FDB\u7A0B\u540D\u5168\u673A\u626B\u63CF\uFF0C\u907F\u514D\u628A\u540C\u673A\u53E6\u4E00\u4E2A\u7A0B\u5E8F\u7B97\u8FDB\u6765\u3002\n * \u754C\u9762\u540D\u79F0\u4E0E\u684C\u9762\u7AEF metric-series.ts \u7684 sfmcPartLabel \u4E00\u81F4\u3002\n */\n\nimport { qqRuntimeStatusPath, stateDir } from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { collectProcessResourcesMany } from \"./process-resources.js\";\n\n/** \u4E00\u6761\u5DF2\u786E\u8BA4\u4ECD\u5728\u8FD0\u884C\u7684\u8FDB\u7A0B\u3002\u5185\u5B58\u91C7\u4E0D\u5230\u7684\u4E0D\u8FD4\u56DE\uFF0C\u907F\u514D\u628A\u8FC7\u671F PID \u663E\u793A\u6210\u300C\u8D44\u6E90\u672A\u77E5\u300D\u3002 */\nexport interface TrackedProcess {\n /** daemon\u3001qq\u3001tunnel\u3001llbot\uFF0C\u6216 sidecar:<\u6A21\u5757 id> */\n key: string;\n /** \u4FA7\u680F\u548C\u66F2\u7EBF\u7528\u7684\u540D\u79F0 */\n label: string;\n pid: number;\n running: true;\n memoryMb: number;\n cpuSeconds: number | null;\n /** CPU \u5360\u7528\u7387\uFF08\u6574\u673A\u4E3A 100%\uFF09\u3002\u7531 status \u8DEF\u7531\u7684 ProcessCpuMeter \u8865\u4E0A\uFF0C\u8FD9\u91CC\u91C7\u96C6\u65F6\u4E0D\u586B\u3002 */\n cpuPercent?: number | null;\n}\n\n/** \u5355\u8FDB\u7A0B\u8FD0\u884C\u6001\u6587\u4EF6\u3002\u8DEF\u5F84\u90FD\u5728 `<\u6839>/.sfmc` \u4E0B\uFF0C\u4E0E\u5199\u5165\u65B9\u4F7F\u7528\u7684\u6587\u4EF6\u540D\u4E00\u81F4\u3002 */\nconst ROLE_FILES: Array<{ key: string; label: string; file: (root: string) => string }> = [\n { key: \"daemon\", label: \"\u5B88\u62A4\u8FDB\u7A0B\", file: (root) => path.join(stateDir(root), \"daemon.json\") },\n { key: \"qq\", label: \"QQ \u6865\", file: (root) => qqRuntimeStatusPath(root) },\n { key: \"tunnel\", label: \"\u96A7\u9053\", file: (root) => path.join(stateDir(root), \"tunnel.runtime.json\") },\n { key: \"llbot\", label: \"LLBot\", file: (root) => path.join(stateDir(root), \"llbot.runtime.json\") },\n];\n\n/**\n * \u4ECE\u8FD0\u884C\u6001 JSON \u91CC\u53D6\u51FA pid\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1Adaemon.json\u3001qq.runtime.json \u7B49\u53EA\u501F pid \u5B57\u6BB5\uFF0C\u5176\u4F59\u5185\u5BB9\uFF08\u4F8B\u5982\u9274\u6743 token\uFF09\u4E0D\u8BFB\u53D6\u3001\u4E0D\u8FD4\u56DE\u3002\n */\nexport function readRecordedPid(text: string): number {\n try {\n const pid = (JSON.parse(text) as { pid?: unknown }).pid;\n return typeof pid === \"number\" && Number.isSafeInteger(pid) && pid > 0 ? pid : 0;\n } catch {\n return 0;\n }\n}\n\n/** \u9644\u5C5E\u8FDB\u7A0B\u6E05\u5355\u3002id \u53EA\u63A5\u53D7\u6A21\u5757\u6807\u8BC6\uFF0C\u907F\u514D\u628A\u4EFB\u610F\u5B57\u7B26\u4E32\u5199\u8FDB\u6307\u6807\u952E\u3002 */\nfunction readSidecars(text: string): Array<{ id: string; pid: number }> {\n try {\n const processes = (JSON.parse(text) as { processes?: unknown }).processes;\n if (!Array.isArray(processes)) return [];\n const rows: Array<{ id: string; pid: number }> = [];\n for (const item of processes) {\n if (!item || typeof item !== \"object\") continue;\n const row = item as { id?: unknown; pid?: unknown };\n if (typeof row.id !== \"string\" || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(row.id)) continue;\n if (typeof row.pid !== \"number\" || !Number.isSafeInteger(row.pid) || row.pid <= 0) continue;\n rows.push({ id: row.id, pid: row.pid });\n }\n return rows;\n } catch {\n return [];\n }\n}\n\nfunction readText(file: string): string {\n try {\n return fs.readFileSync(file, \"utf8\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * \u91C7\u96C6\u4ECD\u5728\u8FD0\u884C\u7684\u5DF2\u767B\u8BB0\u8FDB\u7A0B\u3002\n * @param root \u5B9E\u4F8B\u6839\u76EE\u5F55\u3002\n * @param skip \u5DF2\u7ECF\u5355\u72EC\u7EDF\u8BA1\u8FC7\u7684 PID\uFF08\u6570\u636E\u670D\u52A1\u3001\u672C\u5B9E\u4F8B BDS\uFF09\uFF0C\u8FD9\u91CC\u4E0D\u518D\u91CD\u590D\u8BA1\u5165\u3002\n */\nexport async function collectTrackedProcesses(root: string, skip: ReadonlySet<number>): Promise<TrackedProcess[]> {\n const wanted: Array<{ key: string; label: string; pid: number }> = [];\n const seen = new Set<number>(skip);\n for (const role of ROLE_FILES) {\n const pid = readRecordedPid(readText(role.file(root)));\n if (!pid || seen.has(pid)) continue;\n seen.add(pid);\n wanted.push({ key: role.key, label: role.label, pid });\n }\n for (const row of readSidecars(readText(path.join(stateDir(root), \"sidecars.runtime.json\")))) {\n if (seen.has(row.pid)) continue;\n seen.add(row.pid);\n wanted.push({ key: `sidecar:${row.id}`, label: row.id, pid: row.pid });\n }\n if (!wanted.length) return [];\n const measured = await collectProcessResourcesMany(wanted.map((row) => row.pid));\n const found: TrackedProcess[] = [];\n for (const row of wanted) {\n const usage = measured.get(row.pid);\n if (!usage) continue;\n found.push({\n key: row.key,\n label: row.label,\n pid: row.pid,\n running: true,\n memoryMb: usage.memoryMb,\n cpuSeconds: usage.cpuSeconds,\n });\n }\n return found;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAiBH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAiBH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAqC1C,QAAA,MAAM,GAAG,8BAAY,CAAC;AACtB,QAAA,MAAM,EAAE,oCAA4B,CAAC;AAGrC,QAAA,MAAM,KAAK,mCAAkB,CAAC;AAwB9B,QAAA,MAAM,gBAAgB,8DAA0E,CAAC;AAQjG,QAAA,MAAM,UAAU,0CAId,CAAC;AAQH,QAAA,MAAM,cAAc,gBAAyB,CAAC;AAC9C,QAAA,MAAM,eAAe,iBAAwB,CAAC;AAE9C,QAAA,MAAM,QAAQ,UAMZ,CAAC;AAkiBH,OAAO,EAAE,EAAE,EAAE,gBAAgB,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ import { createBindConfirmRoutes } from "./routes/bind-confirm.js";
|
|
|
41
41
|
import { createQqBindRoutes } from "./routes/qq-bind.js";
|
|
42
42
|
import { createWechatBindRoutes } from "./routes/wechat-bind.js";
|
|
43
43
|
import { createWechatActivityRoutes } from "./routes/wechat-activity.js";
|
|
44
|
+
import { createScoreboardRoutes } from "./routes/scoreboard.js";
|
|
44
45
|
import { createQqEventsRoutes } from "./routes/qq-events.js";
|
|
45
46
|
import { createQqJoinRoutes } from "./routes/qq-join.js";
|
|
46
47
|
import { createStatusRoutes } from "./routes/status.js";
|
|
@@ -200,6 +201,7 @@ function setModuleEnabled(mod, enabled) {
|
|
|
200
201
|
const healthRoutes = createHealthRoutes();
|
|
201
202
|
const metricsDb = openDatabase(path.join(path.dirname(env.DB_PATH), "runtime-metrics.sqlite"));
|
|
202
203
|
const statusRoutes = createStatusRoutes({ query: createQuery(metricsDb) });
|
|
204
|
+
const scoreboardRoutes = createScoreboardRoutes();
|
|
203
205
|
for (const name of PLATFORM_MONITORING_SERVICES) serviceRegistry.registerHandler(PLATFORM_SERVICE_OWNER, name, async () => {
|
|
204
206
|
const response = await fetch(`http://127.0.0.1:${env.PORT}/api/sfmc/metrics`, { signal: AbortSignal.timeout(1e4) });
|
|
205
207
|
if (!response.ok) throw new Error("\u5E73\u53F0\u8FD0\u884C\u6307\u6807\u8BFB\u53D6\u5931\u8D25");
|
|
@@ -480,6 +482,7 @@ async function handle(req, res) {
|
|
|
480
482
|
if (await moduleRoutesInstance(ctxBase)) return;
|
|
481
483
|
if (await healthRoutes(ctxBase)) return;
|
|
482
484
|
if (await statusRoutes(ctxBase)) return;
|
|
485
|
+
if (await scoreboardRoutes(ctxBase)) return;
|
|
483
486
|
if (await qqBindRoutes(ctxBase)) return;
|
|
484
487
|
if (await bindConfirmRoutes(ctxBase)) return;
|
|
485
488
|
if (await wechatBindRoutes(ctxBase)) return;
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n *\n * \u5DE5\u4F5C\u6D41:\n * 1. \u52A0\u8F7D env\n * 2. \u6821\u9A8C Node \u7248\u672C\n * 3. openDatabase + createPlatformTables(sfmc__audit / sfmc__idempotent)\n * 4. loadManifestV2() \u2014 \u5931\u8D25 = \u542F\u52A8\u5931\u8D25\n * 5. filterEnabled(loaded, lockFileEnabled)\n * 6. buildModuleAuth({auth_token, enabled_modules}) \u2190 \u4E0E DB \u540C\u76EE\u5F55 module-tokens.json\n * 7. \u5B9E\u4F8B\u5316:SchemaRegistry / ServiceRegistry / IdempotencyStore / TxRunner\n * 8. \u88C5\u914D v2 \u8DEF\u7531 (/api/sfmc/db/* + /api/sfmc/services* + /api/sfmc/configs/:key/*)\n * \u53E6\u4FDD\u7559 messages(qq-bridge) + config/modules/health \u5E73\u53F0\u8DEF\u7531\n * 9. createServer + listen\n *\n * \u9274\u6743(handle \u5C42):\n * - /api/sfmc/db/*, /api/sfmc/services*, /api/sfmc/configs/:key(/set|notify)*\n * \u2192 module token (Authorization: Bearer ...) + ?moduleId=...\n * - \u5176\u4ED6:env.AUTH_TOKEN \u65E7 auth(NEEDS_AUTH)\n */\n\nimport http from \"node:http\";\nimport { PLATFORM_MONITORING_SERVICES, PLATFORM_SERVICE_OWNER } from \"@sfmc-bds/sdk/contracts\";\nimport { SQL } from \"sql-template-strings\";\n\nimport { createPlatformTables } from \"./db-tables.js\";\nimport { syncPlayerSession } from \"./domain/player-session.js\";\nimport { initSchema } from \"./domain/schema.js\";\nimport { loadEnv } from \"./env.js\";\nimport { createIdempotencyStore } from \"./lib/idempotency-store.js\";\nimport { log } from \"./lib/log.js\";\nimport { assertNodeVersion } from \"./lib/runtime.js\";\nimport { createQuery, openDatabase } from \"./lib/sqlite.js\";\nimport { loadManifestV2 } from \"./manifest-loader.js\";\nimport { buildModuleAuth, ensureModuleToken, persistModuleAuth, verifyModuleAuth } from \"./module-auth.js\";\nimport { syncModuleRuntimeState } from \"./module-runtime-sync.js\";\nimport { SchemaRegistry } from \"./schema-registry.js\";\nimport { createServer, startConsole } from \"./server.js\";\nimport { ServiceRegistry } from \"./service-registry.js\";\nimport { registerEnabledBuiltinServices } from \"./services/builtin-handlers.js\";\nimport { TxRunner } from \"./tx-runner.js\";\n\nimport { configPath, ensureJson, patchJson, readJson } from \"@sfmc-bds/sdk/node/config\";\nimport path, { join } from \"node:path\";\n\nimport { jsonV2Fail } from \"./routes/_shared.js\";\nimport { createDbRoutes } from \"./routes/db-routes.js\";\nimport { createModuleConfigRoutes } from \"./routes/module-config-routes.js\";\nimport { createServiceRoutes } from \"./routes/service-routes.js\";\n\nimport { forwardToQQBridge, makeOutboundConfig } from \"./domain/bridge.js\";\nimport { loadContentSnapshot } from \"./domain/installed-content.js\";\nimport { createQqEventsDispatcher, resolveQqEventsConfig, type ResolvedQqEventsConfig } from \"./domain/qq-events.js\";\nimport { body as sharedBody, json as sharedJson } from \"./lib/http.js\";\nimport { isEnabled, loadModuleLock, saveModuleLock, updateModuleState } from \"./lib/module-state.js\";\nimport { createConfigRoutes } from \"./routes/config.js\";\nimport { createContentRoutes } from \"./routes/content.js\";\nimport { createHealthRoutes } from \"./routes/health.js\";\nimport { createMessagesRoutes, gameChatForwardPayload, type ChatChannelQQFlags } from \"./routes/messages.js\";\nimport { createModuleRoutes } from \"./routes/modules.js\";\nimport { codeTakenBy, type BindProvider } from \"./domain/bind-providers.js\";\nimport { resolvePlayerLevel } from \"./domain/player-level.js\";\nimport { createQqBindProvider } from \"./domain/qq-bind.js\";\nimport { createWechatBindProvider } from \"./domain/wechat-bind.js\";\nimport { createBindConfirmRoutes } from \"./routes/bind-confirm.js\";\nimport { createQqBindRoutes } from \"./routes/qq-bind.js\";\nimport { createWechatBindRoutes } from \"./routes/wechat-bind.js\";\nimport { createWechatActivityRoutes } from \"./routes/wechat-activity.js\";\nimport { createQqEventsRoutes } from \"./routes/qq-events.js\";\nimport { createQqJoinRoutes, type JoinFeatureFlags } from \"./routes/qq-join.js\";\nimport { createStatusRoutes } from \"./routes/status.js\";\n\nif (!assertNodeVersion(22, 13)) {\n process.exit(2);\n}\n\nconst env = loadEnv();\nconst db = openDatabase(env.DB_PATH);\ncreatePlatformTables(db); // sfmc__audit / sfmc__idempotent\ninitSchema(db); // \u5E73\u53F0\u4E1A\u52A1 bootstrap \u8868(players/world/chat/...)\u2014\u2014qq-bridge \u5728 SAPI defineTable \u4E4B\u524D\u5C31\u9700\u8981\nconst query = createQuery(db);\n\n// \u2500\u2500 v2 manifest \u52A0\u8F7D(\u5931\u8D25 = \u542F\u52A8\u5931\u8D25)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst loadedManifest = loadManifestV2(); // throws on violation\nlog.success(\n `[manifest v2] loaded ${Object.keys(loadedManifest.modules).length} modules; provides ${loadedManifest.providesMap.size} services`\n);\n\n// \u2500\u2500 enabled \u96C6\u5408(\u4ECE lock file)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst lockFile = loadModuleLock(env.MODULE_LOCK_PATH);\nconst moduleCatalog = readJson<{ modules?: unknown[] }>(env.MODULE_CATALOG_PATH) ?? { modules: [] };\nconst catalogIds = new Set(\n Array.isArray(moduleCatalog.modules)\n ? (moduleCatalog.modules as Array<{ id?: string }>).map((m) => String(m.id || \"\")).filter((id) => id.length > 0)\n : []\n);\nconst enabledSet = new Set<string>();\nfor (const id of Object.keys(loadedManifest.modules)) {\n if (!catalogIds.has(id)) continue;\n const catalogEntry = (moduleCatalog.modules as Array<Record<string, unknown>>).find((m) => String(m.id) === id);\n if (!catalogEntry) continue;\n const defaultEnabled = catalogEntry.enabledByDefault !== false;\n if (isEnabled(lockFile, id, defaultEnabled)) enabledSet.add(id);\n}\nconst enabledManifests = new Map<string, NonNullable<(typeof loadedManifest.modules)[string]>>();\nfor (const id of enabledSet) {\n const m = loadedManifest.modules[id];\n if (m) enabledManifests.set(id, m);\n}\nlog.info(`[manifest v2] enabled: ${[...enabledSet].sort().join(\", \") || \"(none)\"}`);\n\n// \u2500\u2500 \u6A21\u5757 HMAC token map(\u4E0E DB \u540C\u76EE\u5F55 module-tokens.json)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst moduleAuth = buildModuleAuth({\n dbPath: env.DB_PATH,\n envAuthToken: env.AUTH_TOKEN,\n enabledModuleIds: [...enabledSet],\n});\n// \u5E73\u53F0\u5BBF\u4E3B\uFF08QQ \u7ED1\u5B9A/\u8D26\u53F7\u5FEB\u7167\uFF09\u9700\u8981\u7A33\u5B9A token\uFF0C\u7ECF configs/all.module_tokens \u4E0B\u53D1\u5230 BDS\nif (ensureModuleToken(moduleAuth, PLATFORM_SERVICE_OWNER)) {\n persistModuleAuth(env.DB_PATH, moduleAuth, env.AUTH_TOKEN);\n log.info(`[auth] \u5DF2\u6D3E\u751F\u5E73\u53F0\u5BBF\u4E3B token: ${PLATFORM_SERVICE_OWNER}`);\n}\n\n// \u2500\u2500 \u4E09\u4EF6\u5957 + \u8DEF\u7531\u5DE5\u5382 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst schemaRegistry = new SchemaRegistry(db);\nconst serviceRegistry = new ServiceRegistry();\nconst idempotent = createIdempotencyStore(db);\nconst txRunner = new TxRunner({\n db,\n query,\n schema: schemaRegistry,\n serviceRegistry,\n enabled: enabledManifests,\n});\n\n// \u2500\u2500 \u8FDB\u7A0B\u5185\u7F6E service handler(\u6269\u5C55\u70B9:BUILTIN_SERVICE_PLUGINS) \u2500\u2500\n{\n const plugins = registerEnabledBuiltinServices(serviceRegistry, { query, db }, enabledSet);\n if (plugins > 0) {\n log.success(`[service] registered ${serviceRegistry.list().length} handlers from ${plugins} builtin plugin(s)`);\n }\n}\n\nconst json = sharedJson;\nconst body = sharedBody;\n\n// \u2500\u2500 \u5DE5\u5177\u51FD\u6570 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction loadModuleCatalog() {\n return Array.isArray(moduleCatalog.modules) ? moduleCatalog.modules : [];\n}\n\nfunction buildModuleList() {\n const catalog = loadModuleCatalog();\n return catalog\n .map((raw) => {\n const entry =\n (raw as Record<string, unknown>).entry && typeof (raw as Record<string, unknown>).entry === \"object\"\n ? ((raw as Record<string, unknown>).entry as Record<string, unknown>)\n : {};\n const id = String((raw as Record<string, unknown>).id || \"\").trim();\n const configKey = String(\n (raw as Record<string, unknown>).configKey || (raw as Record<string, unknown>).config_key || \"\"\n ).trim();\n if (!id || !configKey) return null;\n const state = lockFile.modules[id];\n const enabled = isEnabled(lockFile, id, (raw as Record<string, unknown>).enabledByDefault !== false);\n return {\n id,\n module_id: id,\n name: configKey,\n configKey,\n config_key: configKey,\n display_name: String((raw as Record<string, unknown>).name || configKey),\n description: String((raw as Record<string, unknown>).description || \"\"),\n default_enabled: (raw as Record<string, unknown>).enabledByDefault !== false,\n can_disable: moduleCanDisable(raw as Record<string, unknown>),\n // ConfigManager \u8BA4 installed!==false;\u5DF2\u88C5\u5305\u9ED8\u8BA4 true\n installed: true,\n requires: Array.isArray((raw as Record<string, unknown>).requires)\n ? ((raw as Record<string, unknown>).requires as unknown[]).filter(Boolean).map(String)\n : [],\n optional: Array.isArray((raw as Record<string, unknown>).optional)\n ? ((raw as Record<string, unknown>).optional as unknown[]).filter(Boolean).map(String)\n : [],\n commands: Array.isArray((raw as Record<string, unknown>).commands)\n ? ((raw as Record<string, unknown>).commands as unknown[]).filter(Boolean).map(String)\n : [],\n entry: {\n kind: String(entry.kind || \"\"),\n path: String(entry.path || \"\"),\n init: String(entry.init || \"\"),\n },\n updated_at: state?.updatedAt ?? null,\n enabled: !!enabled,\n };\n })\n .filter(Boolean);\n}\n\n/**\n * QQ\u300C\u6A21\u5757 / \u8D44\u6E90\u5305\u300D\u5FEB\u7167\u3002\u6BCF\u6B21\u8BF7\u6C42\u73B0\u8BFB catalog \u4E0E\u78C1\u76D8\uFF0C\u88C5\u5305\u540E\u4E0D\u7528\u91CD\u542F db-server\u3002\n */\nfunction loadContentSnapshotForRequest() {\n const modules = (buildModuleList() ?? []).flatMap((raw) => {\n if (!raw || typeof raw !== \"object\") return [];\n const row = raw as { id?: unknown; display_name?: unknown; enabled?: unknown };\n const id = String(row.id ?? \"\").trim();\n if (!id) return [];\n return [\n {\n id,\n display_name: String(row.display_name || id),\n enabled: row.enabled !== false,\n },\n ];\n });\n return loadContentSnapshot({\n packagesDir: join(env.MODULES_DIR, \"packages\"),\n modules,\n bdsRoot: currentBdsRoot(),\n });\n}\n\n/**\n * BDS \u6839\u76EE\u5F55\uFF08configs/bds_updater.json \u7684 bds_path\uFF0C\u76F8\u5BF9\u8DEF\u5F84\u6309\u9879\u76EE\u6839\u89E3\u6790\uFF09\uFF1B\u672A\u914D\u7F6E\u65F6\u4E3A null\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u5185\u5BB9\u5FEB\u7167\u8BFB\u53D6\u5DF2\u88C5\u5305\uFF0C\u5FAE\u4FE1\u7ED1\u5B9A\u67E5\u8BE2\u8BFB\u53D6 BDS permissions.json \u8BA1\u7B97\u73A9\u5BB6\u539F\u751F\u7B49\u7EA7\uFF08DRY\uFF1A\u540C\u4E00\u5904\u89E3\u6790\uFF09\u3002\n */\nfunction currentBdsRoot(): string | null {\n const cfg = readJson<{ bds_path?: string }>(configPath(env.PROJECT_ROOT, \"bds_updater.json\"));\n const rawBds = typeof cfg?.bds_path === \"string\" ? cfg.bds_path.trim() : \"\";\n return rawBds ? (path.isAbsolute(rawBds) ? rawBds : path.resolve(env.PROJECT_ROOT, rawBds)) : null;\n}\n\n/** catalog \u7701\u7565 canDisable \u65F6\u9ED8\u8BA4\u5141\u8BB8\u7981\u7528(\u4E0E buildModuleList.can_disable \u540C\u6E90)\u3002 */\nfunction moduleCanDisable(raw: Record<string, unknown>): boolean {\n return raw.canDisable !== false;\n}\n\nfunction resolveModuleByKey(key: string) {\n const k = String(key || \"\").trim();\n const catalog = loadModuleCatalog();\n const raw = catalog.find(\n (m) =>\n String((m as Record<string, unknown>).id || \"\") === k ||\n String((m as Record<string, unknown>).configKey || (m as Record<string, unknown>).config_key || \"\") === k\n ) as Record<string, unknown> | undefined;\n if (!raw) return null;\n const id = String(raw.id || \"\").trim();\n const configKey = String(raw.configKey || raw.config_key || \"\").trim();\n if (!id || !configKey) return null;\n /* LSP:\u4E0E buildModuleList.can_disable \u540C\u6E90 \u2014 \u7701\u7565\u5B57\u6BB5\u89C6\u4E3A\u53EF\u7981\u7528 */\n return { id, configKey, canDisable: moduleCanDisable(raw) };\n}\n\nfunction setModuleEnabled(mod: { id: string; canDisable: boolean }, enabled: boolean) {\n // \u76F4\u63A5\u66F4\u65B0\u542F\u52A8\u65F6\u7F13\u5B58\u7684 lockFile(\u800C\u975E\u53E6\u8BFB\u4E00\u4EFD\u65B0\u526F\u672C),\n // \u5426\u5219 buildModuleList() \u8BFB\u7684\u4ECD\u662F\u65E7\u7F13\u5B58,\u5BFC\u81F4\u542F\u505C\u540E enabled \u72B6\u6001\u4E0D\u7FFB\u8F6C\u3002\n updateModuleState(lockFile, mod.id, { enabled: !!enabled });\n // DRY:\u4E0E loadModuleLock \u5BF9\u79F0\u8D70 saveModuleLock,\u52FF\u6563\u843D writeJson\n saveModuleLock(env.MODULE_LOCK_PATH, lockFile);\n\n // DIP:\u70ED\u540C\u6B65 enabledSet / tokens / manifests / builtin handlers(\u4E0D\u91CD\u542F db-server)\n syncModuleRuntimeState({\n moduleId: mod.id,\n enabled: !!enabled,\n dbPath: env.DB_PATH,\n envAuthToken: env.AUTH_TOKEN,\n enabledSet,\n enabledManifests,\n loadedManifest,\n moduleAuth,\n serviceRegistry,\n builtinDeps: { query, db },\n });\n}\n\n// \u2500\u2500 \u5E73\u53F0\u8DEF\u7531(\u975E\u6A21\u5757\u4E1A\u52A1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst healthRoutes = createHealthRoutes();\n// \u6307\u6807\u72EC\u7ACB\u843D\u5E93\uFF0C\u907F\u514D\u5199\u5165\u4E1A\u52A1\u6A21\u5757\u6B63\u5728\u6301\u6709\u7684\u8DE8\u8BF7\u6C42\u4E8B\u52A1\u3002\nconst metricsDb = openDatabase(path.join(path.dirname(env.DB_PATH), \"runtime-metrics.sqlite\"));\nconst statusRoutes = createStatusRoutes({ query: createQuery(metricsDb) });\nfor (const name of PLATFORM_MONITORING_SERVICES) serviceRegistry.registerHandler(PLATFORM_SERVICE_OWNER, name, async () => {\n const response = await fetch(`http://127.0.0.1:${env.PORT}/api/sfmc/metrics`, { signal: AbortSignal.timeout(10_000) });\n if (!response.ok) throw new Error(\"\u5E73\u53F0\u8FD0\u884C\u6307\u6807\u8BFB\u53D6\u5931\u8D25\");\n const result = await response.json() as { current: { tps: number | null; chunkEstimate: number | null } | null };\n const tps = result.current?.tps ?? null;\n const grade = tps === null ? \"unknown\" : tps >= 19.5 ? \"green\" : tps >= 15 ? \"yellow\" : tps >= 10 ? \"gold\" : \"red\";\n if (name === \"tps.current\") return tps;\n if (name === \"tps.status\") return { tps, grade, text: `\u00A77[TPS] \u00A7f${tps?.toFixed(2) ?? \"\u672A\u77E5\"} \u00A77/ 20.00` };\n return result.current ? { ...result.current, grade, totalLoadedChunks: result.current.chunkEstimate } : null;\n});\n/**\n * \u5DF2\u6CE8\u518C\u7684\u5916\u90E8\u8D26\u53F7\u7ED1\u5B9A\u6E20\u9053\uFF08QQ\u3001\u5FAE\u4FE1\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u7EDF\u4E00\u786E\u8BA4\u5165\u53E3\u6309\u987A\u5E8F\u8BE2\u95EE\uFF1B\u4E24\u4E2A\u6E20\u9053\u7533\u8BF7\u77ED\u7801\u65F6\u5171\u7528 isBindCodeTaken \u8DE8\u6E20\u9053\u67E5\u91CD\u3002\u65B0\u589E\u6E20\u9053\u5728\u6B64\u8FFD\u52A0\uFF08OCP\uFF09\u3002\n */\nconst bindProviders: readonly BindProvider[] = [createQqBindProvider(query), createWechatBindProvider(query)];\nconst isBindCodeTaken = codeTakenBy(() => bindProviders);\nconst qqBindRoutes = createQqBindRoutes({ query, body, json, isCodeTaken: isBindCodeTaken });\nconst bindConfirmRoutes = createBindConfirmRoutes({ body, json, providers: () => bindProviders });\nconst wechatBindRoutes = createWechatBindRoutes({\n query,\n body,\n json,\n isCodeTaken: isBindCodeTaken,\n // \u6BCF\u6B21\u73B0\u8BFB\u4E24\u4EFD\u6743\u9650\u6587\u4EF6\uFF1A\u7BA1\u7406\u5458\u8C03\u6574\u6743\u9650\u540E\uFF0C\u5C0F\u7A0B\u5E8F\u7BA1\u7406\u7AEF\u6388\u6743\u7ACB\u5373\u968F\u4E4B\u53D8\u5316\n resolveLevel: (player) =>\n resolvePlayerLevel(\n {\n readSfmcPermissions: () => readJson(configPath(env.PROJECT_ROOT, \"permissions.json\")),\n readBdsPermissions: () => {\n const bdsRoot = currentBdsRoot();\n return bdsRoot ? readJson(join(bdsRoot, \"permissions.json\")) : null;\n },\n },\n player\n ),\n});\nconst wechatActivityRoutes = createWechatActivityRoutes({ query, json });\n\n/** \u5E73\u53F0 QQ \u7ED1\u5B9A\u76F8\u5173\u914D\u7F6E\uFF08\u975E SDK qq_config\uFF1B\u539F qq-link \u6A21\u5757\u914D\u7F6E\u952E\uFF09 */\nconst QQ_LINK_CONFIG_KEY = \"qq_link\";\nconst QQ_LINK_DEFAULTS = {\n allowlist_enabled: true,\n require_approval: true,\n /** \u662F\u5426\u5C06 QQ \u7FA4\u4E3B/\u7FA4\u7BA1\u89C6\u4F5C SFMC \u7BA1\u7406\u5458\uFF1B\u7F3A\u7701\u5F00\u542F\uFF0C\u4EC5\u6539\u672C\u6587\u4EF6\u53EF\u5173\u95ED\uFF0CAPI/\u7FA4\u804A\u4E0D\u53EF\u5199 */\n treat_group_admins_as_admins: true,\n} as const;\n\nfunction asConfigBool(v: unknown, fallback: boolean): boolean {\n if (v === undefined || v === null) return fallback;\n if (typeof v === \"boolean\") return v;\n const s = String(v).trim().toLowerCase();\n if (s === \"true\" || s === \"1\" || s === \"yes\" || s === \"on\") return true;\n if (s === \"false\" || s === \"0\" || s === \"no\" || s === \"off\") return false;\n return fallback;\n}\n\nfunction qqLinkConfigFile(): string {\n return join(env.PROJECT_ROOT, \"configs\", `${QQ_LINK_CONFIG_KEY}.json`);\n}\n\nfunction readJoinFlags(): JoinFeatureFlags {\n // \u5E73\u53F0\u914D\u7F6E configs/qq_link.json\uFF08\u975E SDK qq_config\uFF09\n const disk = ensureJson<Record<string, unknown>>(qqLinkConfigFile(), { ...QQ_LINK_DEFAULTS });\n return {\n allowlistEnabled: asConfigBool(disk.allowlist_enabled, QQ_LINK_DEFAULTS.allowlist_enabled),\n requireApproval: asConfigBool(disk.require_approval, QQ_LINK_DEFAULTS.require_approval),\n treatGroupAdminsAsAdmins: asConfigBool(\n disk.treat_group_admins_as_admins,\n QQ_LINK_DEFAULTS.treat_group_admins_as_admins\n ),\n };\n}\n\nfunction writeJoinFlags(partial: Partial<JoinFeatureFlags>): JoinFeatureFlags {\n const file = qqLinkConfigFile();\n ensureJson<Record<string, unknown>>(file, { ...QQ_LINK_DEFAULTS });\n const patch: Record<string, boolean> = {};\n if (partial.allowlistEnabled !== undefined) patch.allowlist_enabled = partial.allowlistEnabled;\n if (partial.requireApproval !== undefined) patch.require_approval = partial.requireApproval;\n // \u6545\u610F\u4E0D\u5199 treat_group_admins_as_admins\uFF1A\u4EC5\u4EBA\u5DE5\u6539 configs/qq_link.json\n if (Object.keys(patch).length > 0) {\n patchJson(file, patch);\n }\n return readJoinFlags();\n}\n\n// \u542F\u52A8\u5373\u843D\u76D8\u5E73\u53F0\u9ED8\u8BA4\u914D\u7F6E\uFF0C\u907F\u514D\u300C\u4ECE\u672A\u70B9\u8FC7\u914D\u7F6E/\u7533\u8BF7\u300D\u65F6 configs/qq_link.json \u4E0D\u5B58\u5728\n{\n const created = !readJson(qqLinkConfigFile());\n ensureJson<Record<string, unknown>>(qqLinkConfigFile(), { ...QQ_LINK_DEFAULTS });\n if (created) {\n log.info(`\u5DF2\u5199\u5165\u5E73\u53F0\u914D\u7F6E\u9AA8\u67B6: configs/${QQ_LINK_CONFIG_KEY}.json`);\n }\n}\n\nconst qqJoinRoutes = createQqJoinRoutes({\n query,\n body,\n json,\n getAdminOpenids: () => {\n const raw = (env.qqconfig[\"official\"] as { admin_openids?: unknown } | undefined)?.admin_openids;\n if (!Array.isArray(raw)) return [];\n return raw.map((x) => String(x).trim()).filter(Boolean);\n },\n getJoinFlags: () => readJoinFlags(),\n setJoinFlags: (partial) => writeJoinFlags(partial),\n});\n\nfunction readQqEventsSettings(): ResolvedQqEventsConfig {\n const disk = readJson<Record<string, unknown>>(join(env.PROJECT_ROOT, \"configs\", \"qq_config.json\"));\n if (!disk) throw new Error(\"qq_config.json \u65E0\u6CD5\u8BFB\u53D6\uFF0C\u4E8B\u4EF6\u8BBE\u7F6E\u672A\u4FEE\u6539\");\n return resolveQqEventsConfig(disk.qq_events);\n}\n\nfunction writeQqEventsSettings(patch: Partial<ResolvedQqEventsConfig>): ResolvedQqEventsConfig {\n const file = join(env.PROJECT_ROOT, \"configs\", \"qq_config.json\");\n const settings = { ...readQqEventsSettings(), ...patch };\n patchJson(file, { qq_events: settings });\n return settings;\n}\n\nfunction currentOutbound() {\n return makeOutboundConfig({\n QQ_BACKEND: env.QQ_BACKEND,\n LLBOT_HOST: env.LLBOT_HOST,\n LLBOT_PORT: env.LLBOT_PORT,\n LLBOT_TOKEN: env.LLBOT_TOKEN,\n QQ_GROUP_ID: env.QQ_GROUP_ID,\n QQ_APP_ID: env.QQ_APP_ID,\n QQ_APP_SECRET: env.QQ_APP_SECRET,\n QQ_SANDBOX: env.QQ_SANDBOX,\n QQ_GROUP_OPENID: env.QQ_GROUP_OPENID,\n });\n}\n\nconst qqEventsDispatcher = createQqEventsDispatcher({\n getConfig: readQqEventsSettings,\n getOutbound: () => currentOutbound(),\n});\nconst qqEventsRoutes = createQqEventsRoutes({\n body,\n json,\n dispatcher: qqEventsDispatcher,\n getSettings: readQqEventsSettings,\n setSettings: writeQqEventsSettings,\n isAdmin: (openid, asGroupAdmin) => {\n const admins = (env.qqconfig[\"official\"] as { admin_openids?: unknown } | undefined)?.admin_openids;\n return (\n (Array.isArray(admins) && admins.includes(openid)) || (asGroupAdmin && readJoinFlags().treatGroupAdminsAsAdmins)\n );\n },\n});\n\n/** \u8BFB\u53D6\u5355\u4E2A\u804A\u5929\u9891\u9053\u3002\u8868\u5C1A\u672A\u7531\u804A\u5929\u6A21\u5757\u5EFA\u51FA\u65F6\u8FD4\u56DE null\uFF0C\u4E0D\u8BA9\u8BF7\u6C42 500\u3002 */\nfunction readChatChannel(channelId: string): ChatChannelQQFlags | null {\n try {\n const rows = query(SQL`SELECT * FROM sfmc_chat_channels WHERE id = ${channelId}`);\n return Array.isArray(rows) ? ((rows[0] as ChatChannelQQFlags | undefined) ?? null) : null;\n } catch (err) {\n log.warn(`\u8BFB\u53D6\u804A\u5929\u9891\u9053\u5931\u8D25: ${(err as Error).message}`);\n return null;\n }\n}\n\n/** \u6D88\u606F\u6765\u6E90\u4E3A QQ \u7684\u9891\u9053\u3002QQ \u5165\u7AD9\u53EA\u6295\u5230\u8FD9\u4E9B\u9891\u9053\u3002 */\nfunction listQQSourceChannels(): ChatChannelQQFlags[] {\n try {\n const rows = query(SQL`SELECT * FROM sfmc_chat_channels`);\n if (!Array.isArray(rows)) return [];\n return (rows as ChatChannelQQFlags[]).filter((row) => row.source_qq === 1);\n } catch (err) {\n log.warn(`\u8BFB\u53D6 QQ \u6765\u6E90\u9891\u9053\u5931\u8D25: ${(err as Error).message}`);\n return [];\n }\n}\n\nconst messagesRoutes = createMessagesRoutes({\n query,\n body,\n json,\n getChannelForQQ: readChatChannel,\n listQQSourceChannels,\n forwardToQQBridge: (channelId: string, prefix: string, fromName: string, content: string, fromId: string) =>\n forwardToQQBridge(currentOutbound(), channelId, prefix, fromName, content, fromId),\n});\n\n// \u6E38\u620F\u53D1\u8A00\u8D70 db.tx \u63D2\u5165\uFF0C\u4E0D\u7ECF\u8FC7 /api/sfmc/messages\u3002\u63D0\u4EA4\u540E\u518D\u6309\u9891\u9053\u5F00\u5173\u8F6C\u53D1\u5230 QQ\u3002\ntxRunner.setAfterCommit((inserts) => {\n for (const item of inserts) {\n if (item.table !== \"sfmc_chat_messages\") continue;\n const payload = gameChatForwardPayload(item.row, readChatChannel(String(item.row.channel_id ?? \"\")));\n if (!payload) continue;\n forwardToQQBridge(\n currentOutbound(),\n payload.channelId,\n payload.prefix,\n payload.fromName,\n payload.content,\n payload.fromId,\n );\n }\n});\nconst configRoutes = createConfigRoutes({\n json,\n projectRoot: env.PROJECT_ROOT,\n // DIP:\u8DEF\u7531\u4E0D\u8BFB lock/catalog/token \u6587\u4EF6,\u7531\u5165\u53E3\u6CE8\u5165\u4E0E /modules \u540C\u6E90\u6570\u636E\n listModules: () => buildModuleList() as Array<Record<string, unknown>>,\n getModuleTokens: () => ({ ...moduleAuth.tokens }),\n});\nconst contentRoutes = createContentRoutes({\n json,\n loadSnapshot: loadContentSnapshotForRequest,\n});\nconst moduleRoutesInstance = createModuleRoutes({\n loadModuleCatalog,\n buildModuleList: buildModuleList as unknown as () => Array<Record<string, unknown>>,\n resolveModuleByKey,\n setModuleEnabled,\n body,\n json,\n});\n\n// \u2500\u2500 v2 \u8DEF\u7531\u5DE5\u5382 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// \u7C7B\u578B\u65AD\u8A00\u6210 unknown \u51FD\u6570 \u2014 v2 routes \u7684 ctx \u7C7B\u578B\u4E0E RouteCtx \u4E0D\u517C\u5BB9,\n// \u4F46\u8C03\u7528\u5951\u7EA6({path, method, params, req, res, body?})\u662F\u7A33\u5B9A\u7684\u3002\nconst dbRoutes = createDbRoutes({ schemaRegistry, txRunner, idempotent, json }) as unknown as (\n ctx: Record<string, unknown>\n) => Promise<boolean>;\nconst serviceRoutes = createServiceRoutes({\n serviceRegistry,\n enabled: enabledManifests,\n json,\n}) as unknown as (ctx: Record<string, unknown>) => Promise<boolean>;\nconst moduleConfigRoutes = createModuleConfigRoutes({\n projectRoot: env.PROJECT_ROOT,\n enabled: enabledManifests,\n json,\n}) as unknown as (ctx: Record<string, unknown>) => Promise<boolean>;\n\n// \u2500\u2500 \u4E3B\u8BF7\u6C42\u5904\u7406\u5668 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function handle(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\n const url = new URL(req.url || \"/\", `http://${req.headers.host || \"localhost\"}`);\n const path = url.pathname;\n const method = req.method || \"GET\";\n const params = url.searchParams;\n\n // loopback \u7ED1\u5B9A\n const remote = req.socket.remoteAddress || \"\";\n if (remote && !remote.startsWith(\"127.\") && remote !== \"::1\" && remote !== \"::ffff:127.\") {\n json(res, { success: false, error: \"forbidden\" }, 403);\n return;\n }\n\n // \u2500\u2500 \u9884\u8BFB body(\u6240\u6709\u8DEF\u7531\u5171\u4EAB) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n await body(req);\n\n // \u2500\u2500 v2 \u6A21\u5757\u8EAB\u4EFD\u6821\u9A8C \u2192 \u5199\u5165\u8DEF\u7531 ctx.moduleAuth(LoD:\u4E0D\u6302 req \u79C1\u6709\u5B57\u6BB5) \u2500\u2500\n // \u6CE8\u610F:`/api/sfmc/configs/all` \u662F\u65E7\u7684\u4E00\u6B21\u6027\u914D\u7F6E\u5FEB\u7167\u7AEF\u70B9(SAPI ConfigManager.init\n // \u542F\u52A8\u5FC5\u7528),\u4E0D\u5C5E\u4E8E v2 \u6A21\u5757\u914D\u7F6E\u547D\u540D\u7A7A\u95F4(configs/<\u6A21\u5757 configKey>),\u5FC5\u987B\u8C41\u514D,\n // \u5426\u5219\u4F1A\u88AB\u6A21\u5757\u9274\u6743\u7F51\u5173\u62E6\u6210 401,\u5BFC\u81F4\u63D2\u4EF6\u7AEF\u8D77\u4E0D\u6765\u3002\n const isLegacyConfigAll = path === \"/api/sfmc/configs/all\";\n const needsModuleAuth =\n path.startsWith(\"/api/sfmc/db/\") ||\n path.startsWith(\"/api/sfmc/services\") ||\n (/^\\/api\\/sfmc\\/configs\\/[A-Za-z0-9_-]+(?:\\/(?:set|notify))?$/.test(path) && !isLegacyConfigAll);\n let moduleAuthCtx: { id: string; permissions: string[] } | null = null;\n if (needsModuleAuth) {\n const id = verifyModuleAuth({\n headers: req.headers,\n params,\n auth: moduleAuth,\n enabledModuleIds: enabledSet,\n });\n if (!id) {\n // LSP: v2 \u6A21\u5757\u95E8\u4E0E service-routes \u540C\u7528 ok \u65B9\u8A00\n jsonV2Fail(res, \"unauthorized: module identity invalid\", 401, \"unauthorized\");\n return;\n }\n const manifest = enabledManifests.get(id);\n moduleAuthCtx = {\n id,\n permissions: manifest?.permissions ?? [],\n };\n } else {\n // \u2500\u2500 \u65E7 env token \u9274\u6743(\u53EA\u5BF9 POST/PUT \u751F\u6548) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const PUBLIC_GET =\n path === \"/api/health\" ||\n path === \"/api/sfmc/status\" ||\n path === \"/api/sfmc/qq/join/apply-queue\" ||\n path === \"/api/sfmc/qq/join/settings\" ||\n (method === \"GET\" &&\n (path === \"/api/sfmc/content\" ||\n path === \"/api/sfmc/modules\" ||\n path === \"/api/sfmc/modules/catalog\" ||\n path.startsWith(\"/api/sfmc/modules/\") ||\n path === \"/api/sfmc/qq/bind/me\" ||\n path === \"/api/sfmc/wechat/bind/me\" ||\n path === \"/api/sfmc/qq/join/pending\"));\n // bind/join/events POST \u4E0E messages \u4E00\u6837\u8D70\u65E7 AUTH_TOKEN\uFF08\u82E5\u914D\u7F6E\uFF09\uFF1B\u6E38\u620F/\u6865\u540C\u4E3A loopback\n // \u5FAE\u4FE1\u7ED1\u5B9A\uFF08mp-gateway \u8C03\u7528\uFF09\u4E0E\u7EDF\u4E00\u786E\u8BA4\u5165\u53E3\uFF08\u6E38\u620F\u8C03\u7528\uFF09\u4E0E QQ \u7ED1\u5B9A\u540C\u4E00\u4FE1\u4EFB\u8FB9\u754C\uFF1A\u4EC5\u672C\u673A\n const NEEDS_AUTH =\n !PUBLIC_GET &&\n method !== \"GET\" &&\n !(\n path.startsWith(\"/api/sfmc/qq/bind/\") ||\n path.startsWith(\"/api/sfmc/qq/join/\") ||\n path.startsWith(\"/api/sfmc/qq/events\") ||\n path.startsWith(\"/api/sfmc/wechat/bind/\") ||\n path === \"/api/sfmc/bind/confirm\"\n );\n if (env.AUTH_TOKEN && NEEDS_AUTH) {\n const auth = req.headers[\"authorization\"] || \"\";\n const provided = auth.startsWith(\"Bearer \") ? auth.slice(7) : (req.headers[\"x-db-token\"] as string) || \"\";\n if (provided !== env.AUTH_TOKEN) {\n json(res, { success: false, error: \"unauthorized\" }, 401);\n return;\n }\n }\n }\n\n try {\n if (path === \"/api/sfmc/player-session\" && method === \"POST\") {\n const data = (await body(req)) as Record<string, unknown>;\n const playerName = String(data.playerName ?? \"\").trim();\n const xuid = String(data.xuid ?? \"\").trim();\n if (!playerName || !xuid) {\n json(res, { success: false, error: \"playerName_and_xuid_required\" }, 400);\n return;\n }\n syncPlayerSession(query, { playerName, xuid });\n json(res, { success: true });\n return;\n }\n // \u2500\u2500 v2 \u8DEF\u7531(\u4F18\u5148\u5339\u914D) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (\n moduleAuthCtx &&\n (path.startsWith(\"/api/sfmc/db/\") ||\n path.startsWith(\"/api/sfmc/services\") ||\n (/^\\/api\\/sfmc\\/configs\\/[A-Za-z0-9_-]+/.test(path) && !isLegacyConfigAll))\n ) {\n const ctx: Record<string, unknown> = {\n path,\n method,\n params,\n req,\n res,\n moduleAuth: moduleAuthCtx,\n };\n // body \u5DF2\u5728\u4E0A\u9762 `await body(req)` \u9884\u8BFB\u5E76\u7F13\u5B58\u5230 req._bodyPromise;\n // \u8FD9\u91CC\u590D\u7528\u7F13\u5B58(\u539F\u5B9E\u73B0\u8BFB\u7684 req._body \u4ECE\u672A\u88AB\u8D4B\u503C,\u5BFC\u81F4\u6240\u6709 v2 \u8DEF\u7531 body \u6052\u4E3A\u7A7A)\u3002\n ctx[\"body\"] = await body(req);\n if (path.startsWith(\"/api/sfmc/db/\")) {\n if (await dbRoutes(ctx)) return;\n } else if (path.startsWith(\"/api/sfmc/services\")) {\n if (await serviceRoutes(ctx)) return;\n } else {\n if (await moduleConfigRoutes(ctx)) return;\n }\n }\n\n // \u2500\u2500 \u5E73\u53F0\u8DEF\u7531 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const ctxBase = { path, method, params, req, res } as {\n path: string;\n method: string;\n params: URLSearchParams;\n req: http.IncomingMessage;\n res: http.ServerResponse;\n };\n if (await contentRoutes(ctxBase)) return;\n if (await moduleRoutesInstance(ctxBase)) return;\n if (await healthRoutes(ctxBase)) return;\n if (await statusRoutes(ctxBase)) return;\n if (await qqBindRoutes(ctxBase)) return;\n if (await bindConfirmRoutes(ctxBase)) return;\n if (await wechatBindRoutes(ctxBase)) return;\n if (await wechatActivityRoutes(ctxBase)) return;\n if (await qqJoinRoutes(ctxBase)) return;\n if (await qqEventsRoutes(ctxBase)) return;\n if (await messagesRoutes(ctxBase)) return;\n if (await configRoutes(ctxBase)) return;\n\n json(res, { success: false, error: \"not_found\" }, 404);\n } catch (err) {\n log.err(err, \"DogeDB\");\n json(res, { success: false, error: (err as Error).message }, 500);\n }\n}\n\n// \u2500\u2500 \u542F\u52A8 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst server = createServer({\n env: { PORT: env.PORT, HOST: env.HOST, AUTH_TOKEN: env.AUTH_TOKEN },\n handle,\n});\nstartConsole(server, db);\n\nexport { db, enabledManifests, env, moduleAuth, query, schemaRegistry, serviceRegistry, txRunner };\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n *\n * \u5DE5\u4F5C\u6D41:\n * 1. \u52A0\u8F7D env\n * 2. \u6821\u9A8C Node \u7248\u672C\n * 3. openDatabase + createPlatformTables(sfmc__audit / sfmc__idempotent)\n * 4. loadManifestV2() \u2014 \u5931\u8D25 = \u542F\u52A8\u5931\u8D25\n * 5. filterEnabled(loaded, lockFileEnabled)\n * 6. buildModuleAuth({auth_token, enabled_modules}) \u2190 \u4E0E DB \u540C\u76EE\u5F55 module-tokens.json\n * 7. \u5B9E\u4F8B\u5316:SchemaRegistry / ServiceRegistry / IdempotencyStore / TxRunner\n * 8. \u88C5\u914D v2 \u8DEF\u7531 (/api/sfmc/db/* + /api/sfmc/services* + /api/sfmc/configs/:key/*)\n * \u53E6\u4FDD\u7559 messages(qq-bridge) + config/modules/health \u5E73\u53F0\u8DEF\u7531\n * 9. createServer + listen\n *\n * \u9274\u6743(handle \u5C42):\n * - /api/sfmc/db/*, /api/sfmc/services*, /api/sfmc/configs/:key(/set|notify)*\n * \u2192 module token (Authorization: Bearer ...) + ?moduleId=...\n * - \u5176\u4ED6:env.AUTH_TOKEN \u65E7 auth(NEEDS_AUTH)\n */\n\nimport http from \"node:http\";\nimport { PLATFORM_MONITORING_SERVICES, PLATFORM_SERVICE_OWNER } from \"@sfmc-bds/sdk/contracts\";\nimport { SQL } from \"sql-template-strings\";\n\nimport { createPlatformTables } from \"./db-tables.js\";\nimport { syncPlayerSession } from \"./domain/player-session.js\";\nimport { initSchema } from \"./domain/schema.js\";\nimport { loadEnv } from \"./env.js\";\nimport { createIdempotencyStore } from \"./lib/idempotency-store.js\";\nimport { log } from \"./lib/log.js\";\nimport { assertNodeVersion } from \"./lib/runtime.js\";\nimport { createQuery, openDatabase } from \"./lib/sqlite.js\";\nimport { loadManifestV2 } from \"./manifest-loader.js\";\nimport { buildModuleAuth, ensureModuleToken, persistModuleAuth, verifyModuleAuth } from \"./module-auth.js\";\nimport { syncModuleRuntimeState } from \"./module-runtime-sync.js\";\nimport { SchemaRegistry } from \"./schema-registry.js\";\nimport { createServer, startConsole } from \"./server.js\";\nimport { ServiceRegistry } from \"./service-registry.js\";\nimport { registerEnabledBuiltinServices } from \"./services/builtin-handlers.js\";\nimport { TxRunner } from \"./tx-runner.js\";\n\nimport { configPath, ensureJson, patchJson, readJson } from \"@sfmc-bds/sdk/node/config\";\nimport path, { join } from \"node:path\";\n\nimport { jsonV2Fail } from \"./routes/_shared.js\";\nimport { createDbRoutes } from \"./routes/db-routes.js\";\nimport { createModuleConfigRoutes } from \"./routes/module-config-routes.js\";\nimport { createServiceRoutes } from \"./routes/service-routes.js\";\n\nimport { forwardToQQBridge, makeOutboundConfig } from \"./domain/bridge.js\";\nimport { loadContentSnapshot } from \"./domain/installed-content.js\";\nimport { createQqEventsDispatcher, resolveQqEventsConfig, type ResolvedQqEventsConfig } from \"./domain/qq-events.js\";\nimport { body as sharedBody, json as sharedJson } from \"./lib/http.js\";\nimport { isEnabled, loadModuleLock, saveModuleLock, updateModuleState } from \"./lib/module-state.js\";\nimport { createConfigRoutes } from \"./routes/config.js\";\nimport { createContentRoutes } from \"./routes/content.js\";\nimport { createHealthRoutes } from \"./routes/health.js\";\nimport { createMessagesRoutes, gameChatForwardPayload, type ChatChannelQQFlags } from \"./routes/messages.js\";\nimport { createModuleRoutes } from \"./routes/modules.js\";\nimport { codeTakenBy, type BindProvider } from \"./domain/bind-providers.js\";\nimport { resolvePlayerLevel } from \"./domain/player-level.js\";\nimport { createQqBindProvider } from \"./domain/qq-bind.js\";\nimport { createWechatBindProvider } from \"./domain/wechat-bind.js\";\nimport { createBindConfirmRoutes } from \"./routes/bind-confirm.js\";\nimport { createQqBindRoutes } from \"./routes/qq-bind.js\";\nimport { createWechatBindRoutes } from \"./routes/wechat-bind.js\";\nimport { createWechatActivityRoutes } from \"./routes/wechat-activity.js\";\nimport { createScoreboardRoutes } from \"./routes/scoreboard.js\";\nimport { createQqEventsRoutes } from \"./routes/qq-events.js\";\nimport { createQqJoinRoutes, type JoinFeatureFlags } from \"./routes/qq-join.js\";\nimport { createStatusRoutes } from \"./routes/status.js\";\n\nif (!assertNodeVersion(22, 13)) {\n process.exit(2);\n}\n\nconst env = loadEnv();\nconst db = openDatabase(env.DB_PATH);\ncreatePlatformTables(db); // sfmc__audit / sfmc__idempotent\ninitSchema(db); // \u5E73\u53F0\u4E1A\u52A1 bootstrap \u8868(players/world/chat/...)\u2014\u2014qq-bridge \u5728 SAPI defineTable \u4E4B\u524D\u5C31\u9700\u8981\nconst query = createQuery(db);\n\n// \u2500\u2500 v2 manifest \u52A0\u8F7D(\u5931\u8D25 = \u542F\u52A8\u5931\u8D25)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst loadedManifest = loadManifestV2(); // throws on violation\nlog.success(\n `[manifest v2] loaded ${Object.keys(loadedManifest.modules).length} modules; provides ${loadedManifest.providesMap.size} services`\n);\n\n// \u2500\u2500 enabled \u96C6\u5408(\u4ECE lock file)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst lockFile = loadModuleLock(env.MODULE_LOCK_PATH);\nconst moduleCatalog = readJson<{ modules?: unknown[] }>(env.MODULE_CATALOG_PATH) ?? { modules: [] };\nconst catalogIds = new Set(\n Array.isArray(moduleCatalog.modules)\n ? (moduleCatalog.modules as Array<{ id?: string }>).map((m) => String(m.id || \"\")).filter((id) => id.length > 0)\n : []\n);\nconst enabledSet = new Set<string>();\nfor (const id of Object.keys(loadedManifest.modules)) {\n if (!catalogIds.has(id)) continue;\n const catalogEntry = (moduleCatalog.modules as Array<Record<string, unknown>>).find((m) => String(m.id) === id);\n if (!catalogEntry) continue;\n const defaultEnabled = catalogEntry.enabledByDefault !== false;\n if (isEnabled(lockFile, id, defaultEnabled)) enabledSet.add(id);\n}\nconst enabledManifests = new Map<string, NonNullable<(typeof loadedManifest.modules)[string]>>();\nfor (const id of enabledSet) {\n const m = loadedManifest.modules[id];\n if (m) enabledManifests.set(id, m);\n}\nlog.info(`[manifest v2] enabled: ${[...enabledSet].sort().join(\", \") || \"(none)\"}`);\n\n// \u2500\u2500 \u6A21\u5757 HMAC token map(\u4E0E DB \u540C\u76EE\u5F55 module-tokens.json)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst moduleAuth = buildModuleAuth({\n dbPath: env.DB_PATH,\n envAuthToken: env.AUTH_TOKEN,\n enabledModuleIds: [...enabledSet],\n});\n// \u5E73\u53F0\u5BBF\u4E3B\uFF08QQ \u7ED1\u5B9A/\u8D26\u53F7\u5FEB\u7167\uFF09\u9700\u8981\u7A33\u5B9A token\uFF0C\u7ECF configs/all.module_tokens \u4E0B\u53D1\u5230 BDS\nif (ensureModuleToken(moduleAuth, PLATFORM_SERVICE_OWNER)) {\n persistModuleAuth(env.DB_PATH, moduleAuth, env.AUTH_TOKEN);\n log.info(`[auth] \u5DF2\u6D3E\u751F\u5E73\u53F0\u5BBF\u4E3B token: ${PLATFORM_SERVICE_OWNER}`);\n}\n\n// \u2500\u2500 \u4E09\u4EF6\u5957 + \u8DEF\u7531\u5DE5\u5382 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst schemaRegistry = new SchemaRegistry(db);\nconst serviceRegistry = new ServiceRegistry();\nconst idempotent = createIdempotencyStore(db);\nconst txRunner = new TxRunner({\n db,\n query,\n schema: schemaRegistry,\n serviceRegistry,\n enabled: enabledManifests,\n});\n\n// \u2500\u2500 \u8FDB\u7A0B\u5185\u7F6E service handler(\u6269\u5C55\u70B9:BUILTIN_SERVICE_PLUGINS) \u2500\u2500\n{\n const plugins = registerEnabledBuiltinServices(serviceRegistry, { query, db }, enabledSet);\n if (plugins > 0) {\n log.success(`[service] registered ${serviceRegistry.list().length} handlers from ${plugins} builtin plugin(s)`);\n }\n}\n\nconst json = sharedJson;\nconst body = sharedBody;\n\n// \u2500\u2500 \u5DE5\u5177\u51FD\u6570 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction loadModuleCatalog() {\n return Array.isArray(moduleCatalog.modules) ? moduleCatalog.modules : [];\n}\n\nfunction buildModuleList() {\n const catalog = loadModuleCatalog();\n return catalog\n .map((raw) => {\n const entry =\n (raw as Record<string, unknown>).entry && typeof (raw as Record<string, unknown>).entry === \"object\"\n ? ((raw as Record<string, unknown>).entry as Record<string, unknown>)\n : {};\n const id = String((raw as Record<string, unknown>).id || \"\").trim();\n const configKey = String(\n (raw as Record<string, unknown>).configKey || (raw as Record<string, unknown>).config_key || \"\"\n ).trim();\n if (!id || !configKey) return null;\n const state = lockFile.modules[id];\n const enabled = isEnabled(lockFile, id, (raw as Record<string, unknown>).enabledByDefault !== false);\n return {\n id,\n module_id: id,\n name: configKey,\n configKey,\n config_key: configKey,\n display_name: String((raw as Record<string, unknown>).name || configKey),\n description: String((raw as Record<string, unknown>).description || \"\"),\n default_enabled: (raw as Record<string, unknown>).enabledByDefault !== false,\n can_disable: moduleCanDisable(raw as Record<string, unknown>),\n // ConfigManager \u8BA4 installed!==false;\u5DF2\u88C5\u5305\u9ED8\u8BA4 true\n installed: true,\n requires: Array.isArray((raw as Record<string, unknown>).requires)\n ? ((raw as Record<string, unknown>).requires as unknown[]).filter(Boolean).map(String)\n : [],\n optional: Array.isArray((raw as Record<string, unknown>).optional)\n ? ((raw as Record<string, unknown>).optional as unknown[]).filter(Boolean).map(String)\n : [],\n commands: Array.isArray((raw as Record<string, unknown>).commands)\n ? ((raw as Record<string, unknown>).commands as unknown[]).filter(Boolean).map(String)\n : [],\n entry: {\n kind: String(entry.kind || \"\"),\n path: String(entry.path || \"\"),\n init: String(entry.init || \"\"),\n },\n updated_at: state?.updatedAt ?? null,\n enabled: !!enabled,\n };\n })\n .filter(Boolean);\n}\n\n/**\n * QQ\u300C\u6A21\u5757 / \u8D44\u6E90\u5305\u300D\u5FEB\u7167\u3002\u6BCF\u6B21\u8BF7\u6C42\u73B0\u8BFB catalog \u4E0E\u78C1\u76D8\uFF0C\u88C5\u5305\u540E\u4E0D\u7528\u91CD\u542F db-server\u3002\n */\nfunction loadContentSnapshotForRequest() {\n const modules = (buildModuleList() ?? []).flatMap((raw) => {\n if (!raw || typeof raw !== \"object\") return [];\n const row = raw as { id?: unknown; display_name?: unknown; enabled?: unknown };\n const id = String(row.id ?? \"\").trim();\n if (!id) return [];\n return [\n {\n id,\n display_name: String(row.display_name || id),\n enabled: row.enabled !== false,\n },\n ];\n });\n return loadContentSnapshot({\n packagesDir: join(env.MODULES_DIR, \"packages\"),\n modules,\n bdsRoot: currentBdsRoot(),\n });\n}\n\n/**\n * BDS \u6839\u76EE\u5F55\uFF08configs/bds_updater.json \u7684 bds_path\uFF0C\u76F8\u5BF9\u8DEF\u5F84\u6309\u9879\u76EE\u6839\u89E3\u6790\uFF09\uFF1B\u672A\u914D\u7F6E\u65F6\u4E3A null\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u5185\u5BB9\u5FEB\u7167\u8BFB\u53D6\u5DF2\u88C5\u5305\uFF0C\u5FAE\u4FE1\u7ED1\u5B9A\u67E5\u8BE2\u8BFB\u53D6 BDS permissions.json \u8BA1\u7B97\u73A9\u5BB6\u539F\u751F\u7B49\u7EA7\uFF08DRY\uFF1A\u540C\u4E00\u5904\u89E3\u6790\uFF09\u3002\n */\nfunction currentBdsRoot(): string | null {\n const cfg = readJson<{ bds_path?: string }>(configPath(env.PROJECT_ROOT, \"bds_updater.json\"));\n const rawBds = typeof cfg?.bds_path === \"string\" ? cfg.bds_path.trim() : \"\";\n return rawBds ? (path.isAbsolute(rawBds) ? rawBds : path.resolve(env.PROJECT_ROOT, rawBds)) : null;\n}\n\n/** catalog \u7701\u7565 canDisable \u65F6\u9ED8\u8BA4\u5141\u8BB8\u7981\u7528(\u4E0E buildModuleList.can_disable \u540C\u6E90)\u3002 */\nfunction moduleCanDisable(raw: Record<string, unknown>): boolean {\n return raw.canDisable !== false;\n}\n\nfunction resolveModuleByKey(key: string) {\n const k = String(key || \"\").trim();\n const catalog = loadModuleCatalog();\n const raw = catalog.find(\n (m) =>\n String((m as Record<string, unknown>).id || \"\") === k ||\n String((m as Record<string, unknown>).configKey || (m as Record<string, unknown>).config_key || \"\") === k\n ) as Record<string, unknown> | undefined;\n if (!raw) return null;\n const id = String(raw.id || \"\").trim();\n const configKey = String(raw.configKey || raw.config_key || \"\").trim();\n if (!id || !configKey) return null;\n /* LSP:\u4E0E buildModuleList.can_disable \u540C\u6E90 \u2014 \u7701\u7565\u5B57\u6BB5\u89C6\u4E3A\u53EF\u7981\u7528 */\n return { id, configKey, canDisable: moduleCanDisable(raw) };\n}\n\nfunction setModuleEnabled(mod: { id: string; canDisable: boolean }, enabled: boolean) {\n // \u76F4\u63A5\u66F4\u65B0\u542F\u52A8\u65F6\u7F13\u5B58\u7684 lockFile(\u800C\u975E\u53E6\u8BFB\u4E00\u4EFD\u65B0\u526F\u672C),\n // \u5426\u5219 buildModuleList() \u8BFB\u7684\u4ECD\u662F\u65E7\u7F13\u5B58,\u5BFC\u81F4\u542F\u505C\u540E enabled \u72B6\u6001\u4E0D\u7FFB\u8F6C\u3002\n updateModuleState(lockFile, mod.id, { enabled: !!enabled });\n // DRY:\u4E0E loadModuleLock \u5BF9\u79F0\u8D70 saveModuleLock,\u52FF\u6563\u843D writeJson\n saveModuleLock(env.MODULE_LOCK_PATH, lockFile);\n\n // DIP:\u70ED\u540C\u6B65 enabledSet / tokens / manifests / builtin handlers(\u4E0D\u91CD\u542F db-server)\n syncModuleRuntimeState({\n moduleId: mod.id,\n enabled: !!enabled,\n dbPath: env.DB_PATH,\n envAuthToken: env.AUTH_TOKEN,\n enabledSet,\n enabledManifests,\n loadedManifest,\n moduleAuth,\n serviceRegistry,\n builtinDeps: { query, db },\n });\n}\n\n// \u2500\u2500 \u5E73\u53F0\u8DEF\u7531(\u975E\u6A21\u5757\u4E1A\u52A1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst healthRoutes = createHealthRoutes();\n// \u6307\u6807\u72EC\u7ACB\u843D\u5E93\uFF0C\u907F\u514D\u5199\u5165\u4E1A\u52A1\u6A21\u5757\u6B63\u5728\u6301\u6709\u7684\u8DE8\u8BF7\u6C42\u4E8B\u52A1\u3002\nconst metricsDb = openDatabase(path.join(path.dirname(env.DB_PATH), \"runtime-metrics.sqlite\"));\nconst statusRoutes = createStatusRoutes({ query: createQuery(metricsDb) });\n/** \u4E16\u754C\u8BA1\u5206\u677F\u5FEB\u7167\uFF08\u53EA\u5728\u5185\u5B58\u4E2D\uFF0C\u4F9B\u684C\u9762\u7AEF / \u5C0F\u7A0B\u5E8F\u300C\u8BA1\u5206\u677F\u300D\u9875\u7ECF CLI \u8BFB\u53D6\uFF09 */\nconst scoreboardRoutes = createScoreboardRoutes();\nfor (const name of PLATFORM_MONITORING_SERVICES) serviceRegistry.registerHandler(PLATFORM_SERVICE_OWNER, name, async () => {\n const response = await fetch(`http://127.0.0.1:${env.PORT}/api/sfmc/metrics`, { signal: AbortSignal.timeout(10_000) });\n if (!response.ok) throw new Error(\"\u5E73\u53F0\u8FD0\u884C\u6307\u6807\u8BFB\u53D6\u5931\u8D25\");\n const result = await response.json() as { current: { tps: number | null; chunkEstimate: number | null } | null };\n const tps = result.current?.tps ?? null;\n const grade = tps === null ? \"unknown\" : tps >= 19.5 ? \"green\" : tps >= 15 ? \"yellow\" : tps >= 10 ? \"gold\" : \"red\";\n if (name === \"tps.current\") return tps;\n if (name === \"tps.status\") return { tps, grade, text: `\u00A77[TPS] \u00A7f${tps?.toFixed(2) ?? \"\u672A\u77E5\"} \u00A77/ 20.00` };\n return result.current ? { ...result.current, grade, totalLoadedChunks: result.current.chunkEstimate } : null;\n});\n/**\n * \u5DF2\u6CE8\u518C\u7684\u5916\u90E8\u8D26\u53F7\u7ED1\u5B9A\u6E20\u9053\uFF08QQ\u3001\u5FAE\u4FE1\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u7EDF\u4E00\u786E\u8BA4\u5165\u53E3\u6309\u987A\u5E8F\u8BE2\u95EE\uFF1B\u4E24\u4E2A\u6E20\u9053\u7533\u8BF7\u77ED\u7801\u65F6\u5171\u7528 isBindCodeTaken \u8DE8\u6E20\u9053\u67E5\u91CD\u3002\u65B0\u589E\u6E20\u9053\u5728\u6B64\u8FFD\u52A0\uFF08OCP\uFF09\u3002\n */\nconst bindProviders: readonly BindProvider[] = [createQqBindProvider(query), createWechatBindProvider(query)];\nconst isBindCodeTaken = codeTakenBy(() => bindProviders);\nconst qqBindRoutes = createQqBindRoutes({ query, body, json, isCodeTaken: isBindCodeTaken });\nconst bindConfirmRoutes = createBindConfirmRoutes({ body, json, providers: () => bindProviders });\nconst wechatBindRoutes = createWechatBindRoutes({\n query,\n body,\n json,\n isCodeTaken: isBindCodeTaken,\n // \u6BCF\u6B21\u73B0\u8BFB\u4E24\u4EFD\u6743\u9650\u6587\u4EF6\uFF1A\u7BA1\u7406\u5458\u8C03\u6574\u6743\u9650\u540E\uFF0C\u5C0F\u7A0B\u5E8F\u7BA1\u7406\u7AEF\u6388\u6743\u7ACB\u5373\u968F\u4E4B\u53D8\u5316\n resolveLevel: (player) =>\n resolvePlayerLevel(\n {\n readSfmcPermissions: () => readJson(configPath(env.PROJECT_ROOT, \"permissions.json\")),\n readBdsPermissions: () => {\n const bdsRoot = currentBdsRoot();\n return bdsRoot ? readJson(join(bdsRoot, \"permissions.json\")) : null;\n },\n },\n player\n ),\n});\nconst wechatActivityRoutes = createWechatActivityRoutes({ query, json });\n\n/** \u5E73\u53F0 QQ \u7ED1\u5B9A\u76F8\u5173\u914D\u7F6E\uFF08\u975E SDK qq_config\uFF1B\u539F qq-link \u6A21\u5757\u914D\u7F6E\u952E\uFF09 */\nconst QQ_LINK_CONFIG_KEY = \"qq_link\";\nconst QQ_LINK_DEFAULTS = {\n allowlist_enabled: true,\n require_approval: true,\n /** \u662F\u5426\u5C06 QQ \u7FA4\u4E3B/\u7FA4\u7BA1\u89C6\u4F5C SFMC \u7BA1\u7406\u5458\uFF1B\u7F3A\u7701\u5F00\u542F\uFF0C\u4EC5\u6539\u672C\u6587\u4EF6\u53EF\u5173\u95ED\uFF0CAPI/\u7FA4\u804A\u4E0D\u53EF\u5199 */\n treat_group_admins_as_admins: true,\n} as const;\n\nfunction asConfigBool(v: unknown, fallback: boolean): boolean {\n if (v === undefined || v === null) return fallback;\n if (typeof v === \"boolean\") return v;\n const s = String(v).trim().toLowerCase();\n if (s === \"true\" || s === \"1\" || s === \"yes\" || s === \"on\") return true;\n if (s === \"false\" || s === \"0\" || s === \"no\" || s === \"off\") return false;\n return fallback;\n}\n\nfunction qqLinkConfigFile(): string {\n return join(env.PROJECT_ROOT, \"configs\", `${QQ_LINK_CONFIG_KEY}.json`);\n}\n\nfunction readJoinFlags(): JoinFeatureFlags {\n // \u5E73\u53F0\u914D\u7F6E configs/qq_link.json\uFF08\u975E SDK qq_config\uFF09\n const disk = ensureJson<Record<string, unknown>>(qqLinkConfigFile(), { ...QQ_LINK_DEFAULTS });\n return {\n allowlistEnabled: asConfigBool(disk.allowlist_enabled, QQ_LINK_DEFAULTS.allowlist_enabled),\n requireApproval: asConfigBool(disk.require_approval, QQ_LINK_DEFAULTS.require_approval),\n treatGroupAdminsAsAdmins: asConfigBool(\n disk.treat_group_admins_as_admins,\n QQ_LINK_DEFAULTS.treat_group_admins_as_admins\n ),\n };\n}\n\nfunction writeJoinFlags(partial: Partial<JoinFeatureFlags>): JoinFeatureFlags {\n const file = qqLinkConfigFile();\n ensureJson<Record<string, unknown>>(file, { ...QQ_LINK_DEFAULTS });\n const patch: Record<string, boolean> = {};\n if (partial.allowlistEnabled !== undefined) patch.allowlist_enabled = partial.allowlistEnabled;\n if (partial.requireApproval !== undefined) patch.require_approval = partial.requireApproval;\n // \u6545\u610F\u4E0D\u5199 treat_group_admins_as_admins\uFF1A\u4EC5\u4EBA\u5DE5\u6539 configs/qq_link.json\n if (Object.keys(patch).length > 0) {\n patchJson(file, patch);\n }\n return readJoinFlags();\n}\n\n// \u542F\u52A8\u5373\u843D\u76D8\u5E73\u53F0\u9ED8\u8BA4\u914D\u7F6E\uFF0C\u907F\u514D\u300C\u4ECE\u672A\u70B9\u8FC7\u914D\u7F6E/\u7533\u8BF7\u300D\u65F6 configs/qq_link.json \u4E0D\u5B58\u5728\n{\n const created = !readJson(qqLinkConfigFile());\n ensureJson<Record<string, unknown>>(qqLinkConfigFile(), { ...QQ_LINK_DEFAULTS });\n if (created) {\n log.info(`\u5DF2\u5199\u5165\u5E73\u53F0\u914D\u7F6E\u9AA8\u67B6: configs/${QQ_LINK_CONFIG_KEY}.json`);\n }\n}\n\nconst qqJoinRoutes = createQqJoinRoutes({\n query,\n body,\n json,\n getAdminOpenids: () => {\n const raw = (env.qqconfig[\"official\"] as { admin_openids?: unknown } | undefined)?.admin_openids;\n if (!Array.isArray(raw)) return [];\n return raw.map((x) => String(x).trim()).filter(Boolean);\n },\n getJoinFlags: () => readJoinFlags(),\n setJoinFlags: (partial) => writeJoinFlags(partial),\n});\n\nfunction readQqEventsSettings(): ResolvedQqEventsConfig {\n const disk = readJson<Record<string, unknown>>(join(env.PROJECT_ROOT, \"configs\", \"qq_config.json\"));\n if (!disk) throw new Error(\"qq_config.json \u65E0\u6CD5\u8BFB\u53D6\uFF0C\u4E8B\u4EF6\u8BBE\u7F6E\u672A\u4FEE\u6539\");\n return resolveQqEventsConfig(disk.qq_events);\n}\n\nfunction writeQqEventsSettings(patch: Partial<ResolvedQqEventsConfig>): ResolvedQqEventsConfig {\n const file = join(env.PROJECT_ROOT, \"configs\", \"qq_config.json\");\n const settings = { ...readQqEventsSettings(), ...patch };\n patchJson(file, { qq_events: settings });\n return settings;\n}\n\nfunction currentOutbound() {\n return makeOutboundConfig({\n QQ_BACKEND: env.QQ_BACKEND,\n LLBOT_HOST: env.LLBOT_HOST,\n LLBOT_PORT: env.LLBOT_PORT,\n LLBOT_TOKEN: env.LLBOT_TOKEN,\n QQ_GROUP_ID: env.QQ_GROUP_ID,\n QQ_APP_ID: env.QQ_APP_ID,\n QQ_APP_SECRET: env.QQ_APP_SECRET,\n QQ_SANDBOX: env.QQ_SANDBOX,\n QQ_GROUP_OPENID: env.QQ_GROUP_OPENID,\n });\n}\n\nconst qqEventsDispatcher = createQqEventsDispatcher({\n getConfig: readQqEventsSettings,\n getOutbound: () => currentOutbound(),\n});\nconst qqEventsRoutes = createQqEventsRoutes({\n body,\n json,\n dispatcher: qqEventsDispatcher,\n getSettings: readQqEventsSettings,\n setSettings: writeQqEventsSettings,\n isAdmin: (openid, asGroupAdmin) => {\n const admins = (env.qqconfig[\"official\"] as { admin_openids?: unknown } | undefined)?.admin_openids;\n return (\n (Array.isArray(admins) && admins.includes(openid)) || (asGroupAdmin && readJoinFlags().treatGroupAdminsAsAdmins)\n );\n },\n});\n\n/** \u8BFB\u53D6\u5355\u4E2A\u804A\u5929\u9891\u9053\u3002\u8868\u5C1A\u672A\u7531\u804A\u5929\u6A21\u5757\u5EFA\u51FA\u65F6\u8FD4\u56DE null\uFF0C\u4E0D\u8BA9\u8BF7\u6C42 500\u3002 */\nfunction readChatChannel(channelId: string): ChatChannelQQFlags | null {\n try {\n const rows = query(SQL`SELECT * FROM sfmc_chat_channels WHERE id = ${channelId}`);\n return Array.isArray(rows) ? ((rows[0] as ChatChannelQQFlags | undefined) ?? null) : null;\n } catch (err) {\n log.warn(`\u8BFB\u53D6\u804A\u5929\u9891\u9053\u5931\u8D25: ${(err as Error).message}`);\n return null;\n }\n}\n\n/** \u6D88\u606F\u6765\u6E90\u4E3A QQ \u7684\u9891\u9053\u3002QQ \u5165\u7AD9\u53EA\u6295\u5230\u8FD9\u4E9B\u9891\u9053\u3002 */\nfunction listQQSourceChannels(): ChatChannelQQFlags[] {\n try {\n const rows = query(SQL`SELECT * FROM sfmc_chat_channels`);\n if (!Array.isArray(rows)) return [];\n return (rows as ChatChannelQQFlags[]).filter((row) => row.source_qq === 1);\n } catch (err) {\n log.warn(`\u8BFB\u53D6 QQ \u6765\u6E90\u9891\u9053\u5931\u8D25: ${(err as Error).message}`);\n return [];\n }\n}\n\nconst messagesRoutes = createMessagesRoutes({\n query,\n body,\n json,\n getChannelForQQ: readChatChannel,\n listQQSourceChannels,\n forwardToQQBridge: (channelId: string, prefix: string, fromName: string, content: string, fromId: string) =>\n forwardToQQBridge(currentOutbound(), channelId, prefix, fromName, content, fromId),\n});\n\n// \u6E38\u620F\u53D1\u8A00\u8D70 db.tx \u63D2\u5165\uFF0C\u4E0D\u7ECF\u8FC7 /api/sfmc/messages\u3002\u63D0\u4EA4\u540E\u518D\u6309\u9891\u9053\u5F00\u5173\u8F6C\u53D1\u5230 QQ\u3002\ntxRunner.setAfterCommit((inserts) => {\n for (const item of inserts) {\n if (item.table !== \"sfmc_chat_messages\") continue;\n const payload = gameChatForwardPayload(item.row, readChatChannel(String(item.row.channel_id ?? \"\")));\n if (!payload) continue;\n forwardToQQBridge(\n currentOutbound(),\n payload.channelId,\n payload.prefix,\n payload.fromName,\n payload.content,\n payload.fromId,\n );\n }\n});\nconst configRoutes = createConfigRoutes({\n json,\n projectRoot: env.PROJECT_ROOT,\n // DIP:\u8DEF\u7531\u4E0D\u8BFB lock/catalog/token \u6587\u4EF6,\u7531\u5165\u53E3\u6CE8\u5165\u4E0E /modules \u540C\u6E90\u6570\u636E\n listModules: () => buildModuleList() as Array<Record<string, unknown>>,\n getModuleTokens: () => ({ ...moduleAuth.tokens }),\n});\nconst contentRoutes = createContentRoutes({\n json,\n loadSnapshot: loadContentSnapshotForRequest,\n});\nconst moduleRoutesInstance = createModuleRoutes({\n loadModuleCatalog,\n buildModuleList: buildModuleList as unknown as () => Array<Record<string, unknown>>,\n resolveModuleByKey,\n setModuleEnabled,\n body,\n json,\n});\n\n// \u2500\u2500 v2 \u8DEF\u7531\u5DE5\u5382 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// \u7C7B\u578B\u65AD\u8A00\u6210 unknown \u51FD\u6570 \u2014 v2 routes \u7684 ctx \u7C7B\u578B\u4E0E RouteCtx \u4E0D\u517C\u5BB9,\n// \u4F46\u8C03\u7528\u5951\u7EA6({path, method, params, req, res, body?})\u662F\u7A33\u5B9A\u7684\u3002\nconst dbRoutes = createDbRoutes({ schemaRegistry, txRunner, idempotent, json }) as unknown as (\n ctx: Record<string, unknown>\n) => Promise<boolean>;\nconst serviceRoutes = createServiceRoutes({\n serviceRegistry,\n enabled: enabledManifests,\n json,\n}) as unknown as (ctx: Record<string, unknown>) => Promise<boolean>;\nconst moduleConfigRoutes = createModuleConfigRoutes({\n projectRoot: env.PROJECT_ROOT,\n enabled: enabledManifests,\n json,\n}) as unknown as (ctx: Record<string, unknown>) => Promise<boolean>;\n\n// \u2500\u2500 \u4E3B\u8BF7\u6C42\u5904\u7406\u5668 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function handle(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\n const url = new URL(req.url || \"/\", `http://${req.headers.host || \"localhost\"}`);\n const path = url.pathname;\n const method = req.method || \"GET\";\n const params = url.searchParams;\n\n // loopback \u7ED1\u5B9A\n const remote = req.socket.remoteAddress || \"\";\n if (remote && !remote.startsWith(\"127.\") && remote !== \"::1\" && remote !== \"::ffff:127.\") {\n json(res, { success: false, error: \"forbidden\" }, 403);\n return;\n }\n\n // \u2500\u2500 \u9884\u8BFB body(\u6240\u6709\u8DEF\u7531\u5171\u4EAB) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n await body(req);\n\n // \u2500\u2500 v2 \u6A21\u5757\u8EAB\u4EFD\u6821\u9A8C \u2192 \u5199\u5165\u8DEF\u7531 ctx.moduleAuth(LoD:\u4E0D\u6302 req \u79C1\u6709\u5B57\u6BB5) \u2500\u2500\n // \u6CE8\u610F:`/api/sfmc/configs/all` \u662F\u65E7\u7684\u4E00\u6B21\u6027\u914D\u7F6E\u5FEB\u7167\u7AEF\u70B9(SAPI ConfigManager.init\n // \u542F\u52A8\u5FC5\u7528),\u4E0D\u5C5E\u4E8E v2 \u6A21\u5757\u914D\u7F6E\u547D\u540D\u7A7A\u95F4(configs/<\u6A21\u5757 configKey>),\u5FC5\u987B\u8C41\u514D,\n // \u5426\u5219\u4F1A\u88AB\u6A21\u5757\u9274\u6743\u7F51\u5173\u62E6\u6210 401,\u5BFC\u81F4\u63D2\u4EF6\u7AEF\u8D77\u4E0D\u6765\u3002\n const isLegacyConfigAll = path === \"/api/sfmc/configs/all\";\n const needsModuleAuth =\n path.startsWith(\"/api/sfmc/db/\") ||\n path.startsWith(\"/api/sfmc/services\") ||\n (/^\\/api\\/sfmc\\/configs\\/[A-Za-z0-9_-]+(?:\\/(?:set|notify))?$/.test(path) && !isLegacyConfigAll);\n let moduleAuthCtx: { id: string; permissions: string[] } | null = null;\n if (needsModuleAuth) {\n const id = verifyModuleAuth({\n headers: req.headers,\n params,\n auth: moduleAuth,\n enabledModuleIds: enabledSet,\n });\n if (!id) {\n // LSP: v2 \u6A21\u5757\u95E8\u4E0E service-routes \u540C\u7528 ok \u65B9\u8A00\n jsonV2Fail(res, \"unauthorized: module identity invalid\", 401, \"unauthorized\");\n return;\n }\n const manifest = enabledManifests.get(id);\n moduleAuthCtx = {\n id,\n permissions: manifest?.permissions ?? [],\n };\n } else {\n // \u2500\u2500 \u65E7 env token \u9274\u6743(\u53EA\u5BF9 POST/PUT \u751F\u6548) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const PUBLIC_GET =\n path === \"/api/health\" ||\n path === \"/api/sfmc/status\" ||\n path === \"/api/sfmc/qq/join/apply-queue\" ||\n path === \"/api/sfmc/qq/join/settings\" ||\n (method === \"GET\" &&\n (path === \"/api/sfmc/content\" ||\n path === \"/api/sfmc/modules\" ||\n path === \"/api/sfmc/modules/catalog\" ||\n path.startsWith(\"/api/sfmc/modules/\") ||\n path === \"/api/sfmc/qq/bind/me\" ||\n path === \"/api/sfmc/wechat/bind/me\" ||\n path === \"/api/sfmc/qq/join/pending\"));\n // bind/join/events POST \u4E0E messages \u4E00\u6837\u8D70\u65E7 AUTH_TOKEN\uFF08\u82E5\u914D\u7F6E\uFF09\uFF1B\u6E38\u620F/\u6865\u540C\u4E3A loopback\n // \u5FAE\u4FE1\u7ED1\u5B9A\uFF08mp-gateway \u8C03\u7528\uFF09\u4E0E\u7EDF\u4E00\u786E\u8BA4\u5165\u53E3\uFF08\u6E38\u620F\u8C03\u7528\uFF09\u4E0E QQ \u7ED1\u5B9A\u540C\u4E00\u4FE1\u4EFB\u8FB9\u754C\uFF1A\u4EC5\u672C\u673A\n const NEEDS_AUTH =\n !PUBLIC_GET &&\n method !== \"GET\" &&\n !(\n path.startsWith(\"/api/sfmc/qq/bind/\") ||\n path.startsWith(\"/api/sfmc/qq/join/\") ||\n path.startsWith(\"/api/sfmc/qq/events\") ||\n path.startsWith(\"/api/sfmc/wechat/bind/\") ||\n path === \"/api/sfmc/bind/confirm\"\n );\n if (env.AUTH_TOKEN && NEEDS_AUTH) {\n const auth = req.headers[\"authorization\"] || \"\";\n const provided = auth.startsWith(\"Bearer \") ? auth.slice(7) : (req.headers[\"x-db-token\"] as string) || \"\";\n if (provided !== env.AUTH_TOKEN) {\n json(res, { success: false, error: \"unauthorized\" }, 401);\n return;\n }\n }\n }\n\n try {\n if (path === \"/api/sfmc/player-session\" && method === \"POST\") {\n const data = (await body(req)) as Record<string, unknown>;\n const playerName = String(data.playerName ?? \"\").trim();\n const xuid = String(data.xuid ?? \"\").trim();\n if (!playerName || !xuid) {\n json(res, { success: false, error: \"playerName_and_xuid_required\" }, 400);\n return;\n }\n syncPlayerSession(query, { playerName, xuid });\n json(res, { success: true });\n return;\n }\n // \u2500\u2500 v2 \u8DEF\u7531(\u4F18\u5148\u5339\u914D) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (\n moduleAuthCtx &&\n (path.startsWith(\"/api/sfmc/db/\") ||\n path.startsWith(\"/api/sfmc/services\") ||\n (/^\\/api\\/sfmc\\/configs\\/[A-Za-z0-9_-]+/.test(path) && !isLegacyConfigAll))\n ) {\n const ctx: Record<string, unknown> = {\n path,\n method,\n params,\n req,\n res,\n moduleAuth: moduleAuthCtx,\n };\n // body \u5DF2\u5728\u4E0A\u9762 `await body(req)` \u9884\u8BFB\u5E76\u7F13\u5B58\u5230 req._bodyPromise;\n // \u8FD9\u91CC\u590D\u7528\u7F13\u5B58(\u539F\u5B9E\u73B0\u8BFB\u7684 req._body \u4ECE\u672A\u88AB\u8D4B\u503C,\u5BFC\u81F4\u6240\u6709 v2 \u8DEF\u7531 body \u6052\u4E3A\u7A7A)\u3002\n ctx[\"body\"] = await body(req);\n if (path.startsWith(\"/api/sfmc/db/\")) {\n if (await dbRoutes(ctx)) return;\n } else if (path.startsWith(\"/api/sfmc/services\")) {\n if (await serviceRoutes(ctx)) return;\n } else {\n if (await moduleConfigRoutes(ctx)) return;\n }\n }\n\n // \u2500\u2500 \u5E73\u53F0\u8DEF\u7531 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const ctxBase = { path, method, params, req, res } as {\n path: string;\n method: string;\n params: URLSearchParams;\n req: http.IncomingMessage;\n res: http.ServerResponse;\n };\n if (await contentRoutes(ctxBase)) return;\n if (await moduleRoutesInstance(ctxBase)) return;\n if (await healthRoutes(ctxBase)) return;\n if (await statusRoutes(ctxBase)) return;\n if (await scoreboardRoutes(ctxBase)) return;\n if (await qqBindRoutes(ctxBase)) return;\n if (await bindConfirmRoutes(ctxBase)) return;\n if (await wechatBindRoutes(ctxBase)) return;\n if (await wechatActivityRoutes(ctxBase)) return;\n if (await qqJoinRoutes(ctxBase)) return;\n if (await qqEventsRoutes(ctxBase)) return;\n if (await messagesRoutes(ctxBase)) return;\n if (await configRoutes(ctxBase)) return;\n\n json(res, { success: false, error: \"not_found\" }, 404);\n } catch (err) {\n log.err(err, \"DogeDB\");\n json(res, { success: false, error: (err as Error).message }, 500);\n }\n}\n\n// \u2500\u2500 \u542F\u52A8 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst server = createServer({\n env: { PORT: env.PORT, HOST: env.HOST, AUTH_TOKEN: env.AUTH_TOKEN },\n handle,\n});\nstartConsole(server, db);\n\nexport { db, enabledManifests, env, moduleAuth, query, schemaRegistry, serviceRegistry, txRunner };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["path"]
|
|
7
7
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* POST /api/sfmc/db/tx/step body {txId, step} → {result}
|
|
9
9
|
* POST /api/sfmc/db/tx/commit body {txId} → TxResponse
|
|
10
10
|
* POST /api/sfmc/db/tx/rollback body {txId} → {ok}
|
|
11
|
+
* POST /api/sfmc/db/tx/reap body {} → {ok, aborted}(回收本模块遗留会话)
|
|
11
12
|
* POST /api/sfmc/db/query body {table, opts?} → {rows}
|
|
12
13
|
* ...
|
|
13
14
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-routes.d.ts","sourceRoot":"","sources":["../../src/routes/db-routes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"db-routes.d.ts","sourceRoot":"","sources":["../../src/routes/db-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGpE,OAAO,KAAK,EAAsB,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAAa,QAAQ,EAAU,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,IAAI,CAAC,EAAE,OAAO,WAAW,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,SAOvC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,GAAG,EAAE,eAAe,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClE,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,KAAG,OAAO,CAAC,OAAO,CAAC,CAyKrB"}
|
package/dist/routes/db-routes.js
CHANGED
|
@@ -63,6 +63,11 @@ function createDbRoutes(depsIn) {
|
|
|
63
63
|
path: "/api/sfmc/db/tx/rollback",
|
|
64
64
|
requireTxId: true,
|
|
65
65
|
run: ({ txId }) => deps.txRunner.rollbackSession(txId, moduleId)
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
// 新脚本实例启动时回收本模块旧实例遗留的会话;只作用于鉴权身份自己的会话
|
|
69
|
+
path: "/api/sfmc/db/tx/reap",
|
|
70
|
+
run: () => ({ ok: true, aborted: deps.txRunner.abortModuleSessions(moduleId) })
|
|
66
71
|
}
|
|
67
72
|
];
|
|
68
73
|
for (const op of sessionOps) {
|