@sfmc-bds/db-server 0.2.0-beta.10 → 0.2.0-beta.11

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.
@@ -0,0 +1,8 @@
1
+ import type { QueryFn } from "../lib/sqlite.js";
2
+ export interface PlayerSessionInput {
3
+ playerName: string;
4
+ xuid: string;
5
+ }
6
+ /** 将 BDS 日志中的权威 XUID 合并到玩家快照表。 */
7
+ export declare function syncPlayerSession(query: QueryFn, input: PlayerSessionInput, now?: number): void;
8
+ //# sourceMappingURL=player-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player-session.d.ts","sourceRoot":"","sources":["../../src/domain/player-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kCAAkC;AAClC,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,GAAG,SAAa,GAAG,IAAI,CAWnG"}
@@ -0,0 +1,16 @@
1
+ function syncPlayerSession(query, input, now = Date.now()) {
2
+ query(
3
+ `INSERT INTO sfmc_players (id, name, xuid, last_online, updated_at)
4
+ VALUES (?, ?, ?, ?, ?)
5
+ ON CONFLICT(id) DO UPDATE SET
6
+ name = excluded.name,
7
+ xuid = excluded.xuid,
8
+ last_online = excluded.last_online,
9
+ updated_at = excluded.updated_at`,
10
+ [input.xuid, input.playerName, input.xuid, now, now]
11
+ );
12
+ }
13
+ export {
14
+ syncPlayerSession
15
+ };
16
+ //# sourceMappingURL=player-session.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/domain/player-session.ts"],
4
+ "sourcesContent": ["import type { QueryFn } from \"../lib/sqlite.js\";\n\nexport interface PlayerSessionInput {\n playerName: string;\n xuid: string;\n}\n\n/** \u5C06 BDS \u65E5\u5FD7\u4E2D\u7684\u6743\u5A01 XUID \u5408\u5E76\u5230\u73A9\u5BB6\u5FEB\u7167\u8868\u3002 */\nexport function syncPlayerSession(query: QueryFn, input: PlayerSessionInput, now = Date.now()): void {\n query(\n `INSERT INTO sfmc_players (id, name, xuid, last_online, updated_at)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n name = excluded.name,\n xuid = excluded.xuid,\n last_online = excluded.last_online,\n updated_at = excluded.updated_at`,\n [input.xuid, input.playerName, input.xuid, now, now],\n );\n}\n"],
5
+ "mappings": "AAQO,SAAS,kBAAkB,OAAgB,OAA2B,MAAM,KAAK,IAAI,GAAS;AACnG;AAAA,IACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,CAAC,MAAM,MAAM,MAAM,YAAY,MAAM,MAAM,KAAK,GAAG;AAAA,EACrD;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=player-session.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player-session.test.d.ts","sourceRoot":"","sources":["../../src/domain/player-session.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,33 @@
1
+ import assert from "node:assert/strict";
2
+ import { DatabaseSync } from "node:sqlite";
3
+ import test from "node:test";
4
+ import { createQuery } from "../lib/sqlite.js";
5
+ import { syncPlayerSession } from "./player-session.js";
6
+ import { initSchema } from "./schema.js";
7
+ test("initSchema \u4E3A\u65E7\u73A9\u5BB6\u8868\u8865\u9F50\u5FEB\u7167\u5B57\u6BB5", () => {
8
+ const db = new DatabaseSync(":memory:");
9
+ db.exec("CREATE TABLE sfmc_players (id TEXT PRIMARY KEY, name TEXT, xuid TEXT)");
10
+ initSchema(db);
11
+ const columns = db.prepare('PRAGMA table_info("sfmc_players")').all();
12
+ const names = new Set(columns.map((column) => column.name));
13
+ for (const name of [
14
+ "spawn_dimension",
15
+ "spawn_x",
16
+ "dimension",
17
+ "game_mode",
18
+ "snapshot_hash",
19
+ "active_channel",
20
+ "subscribed_channels"
21
+ ]) {
22
+ assert.equal(names.has(name), true, name);
23
+ }
24
+ });
25
+ test("BDS \u73A9\u5BB6\u4F1A\u8BDD\u5199\u5165\u5E76\u5237\u65B0 XUID", () => {
26
+ const db = new DatabaseSync(":memory:");
27
+ initSchema(db);
28
+ const query = createQuery(db);
29
+ syncPlayerSession(query, { playerName: "Steve", xuid: "123" }, 42);
30
+ const row = db.prepare("SELECT id, name, xuid, last_online FROM sfmc_players WHERE id = ?").get("123");
31
+ assert.deepEqual({ ...row }, { id: "123", name: "Steve", xuid: "123", last_online: 42 });
32
+ });
33
+ //# sourceMappingURL=player-session.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/domain/player-session.test.ts"],
4
+ "sourcesContent": ["import assert from \"node:assert/strict\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport test from \"node:test\";\nimport { createQuery } from \"../lib/sqlite.js\";\nimport { syncPlayerSession } from \"./player-session.js\";\nimport { initSchema } from \"./schema.js\";\n\ntest(\"initSchema \u4E3A\u65E7\u73A9\u5BB6\u8868\u8865\u9F50\u5FEB\u7167\u5B57\u6BB5\", () => {\n const db = new DatabaseSync(\":memory:\");\n db.exec(\"CREATE TABLE sfmc_players (id TEXT PRIMARY KEY, name TEXT, xuid TEXT)\");\n initSchema(db);\n const columns = db.prepare('PRAGMA table_info(\"sfmc_players\")').all() as Array<{ name: string }>;\n const names = new Set(columns.map((column) => column.name));\n for (const name of [\n \"spawn_dimension\",\n \"spawn_x\",\n \"dimension\",\n \"game_mode\",\n \"snapshot_hash\",\n \"active_channel\",\n \"subscribed_channels\",\n ]) {\n assert.equal(names.has(name), true, name);\n }\n});\n\ntest(\"BDS \u73A9\u5BB6\u4F1A\u8BDD\u5199\u5165\u5E76\u5237\u65B0 XUID\", () => {\n const db = new DatabaseSync(\":memory:\");\n initSchema(db);\n const query = createQuery(db);\n syncPlayerSession(query, { playerName: \"Steve\", xuid: \"123\" }, 42);\n const row = db.prepare(\"SELECT id, name, xuid, last_online FROM sfmc_players WHERE id = ?\").get(\"123\");\n assert.deepEqual({ ...row }, { id: \"123\", name: \"Steve\", xuid: \"123\", last_online: 42 });\n});\n"],
5
+ "mappings": "AAAA,OAAO,YAAY;AACnB,SAAS,oBAAoB;AAC7B,OAAO,UAAU;AACjB,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kBAAkB;AAE3B,KAAK,iFAA0B,MAAM;AACnC,QAAM,KAAK,IAAI,aAAa,UAAU;AACtC,KAAG,KAAK,uEAAuE;AAC/E,aAAW,EAAE;AACb,QAAM,UAAU,GAAG,QAAQ,mCAAmC,EAAE,IAAI;AACpE,QAAM,QAAQ,IAAI,IAAI,QAAQ,IAAI,CAAC,WAAW,OAAO,IAAI,CAAC;AAC1D,aAAW,QAAQ;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAAG;AACD,WAAO,MAAM,MAAM,IAAI,IAAI,GAAG,MAAM,IAAI;AAAA,EAC1C;AACF,CAAC;AAED,KAAK,mEAAsB,MAAM;AAC/B,QAAM,KAAK,IAAI,aAAa,UAAU;AACtC,aAAW,EAAE;AACb,QAAM,QAAQ,YAAY,EAAE;AAC5B,oBAAkB,OAAO,EAAE,YAAY,SAAS,MAAM,MAAM,GAAG,EAAE;AACjE,QAAM,MAAM,GAAG,QAAQ,mEAAmE,EAAE,IAAI,KAAK;AACrG,SAAO,UAAU,EAAE,GAAG,IAAI,GAAG,EAAE,IAAI,OAAO,MAAM,SAAS,MAAM,OAAO,aAAa,GAAG,CAAC;AACzF,CAAC;",
6
+ "names": []
7
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/domain/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAoGjD"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/domain/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAuIjD"}
@@ -27,9 +27,44 @@ function initSchema(db) {
27
27
  level INTEGER DEFAULT 0,
28
28
  total_xp INTEGER DEFAULT 0,
29
29
  tags TEXT DEFAULT '',
30
+ spawn_dimension TEXT DEFAULT '',
31
+ spawn_x REAL,
32
+ spawn_y REAL,
33
+ spawn_z REAL,
34
+ dimension TEXT DEFAULT '',
35
+ x REAL,
36
+ y REAL,
37
+ z REAL,
38
+ game_mode TEXT DEFAULT '',
39
+ snapshot_hash TEXT DEFAULT '',
40
+ active_channel TEXT DEFAULT '',
41
+ subscribed_channels TEXT DEFAULT '[]',
30
42
  updated_at INTEGER DEFAULT 0
31
43
  )`
32
44
  );
45
+ const playerColumns = new Map(
46
+ db.prepare(`PRAGMA table_info("sfmc_players")`).all().map((column) => [
47
+ column.name,
48
+ true
49
+ ])
50
+ );
51
+ const missingPlayerColumns = [
52
+ ["spawn_dimension", "TEXT DEFAULT ''"],
53
+ ["spawn_x", "REAL"],
54
+ ["spawn_y", "REAL"],
55
+ ["spawn_z", "REAL"],
56
+ ["dimension", "TEXT DEFAULT ''"],
57
+ ["x", "REAL"],
58
+ ["y", "REAL"],
59
+ ["z", "REAL"],
60
+ ["game_mode", "TEXT DEFAULT ''"],
61
+ ["snapshot_hash", "TEXT DEFAULT ''"],
62
+ ["active_channel", "TEXT DEFAULT ''"],
63
+ ["subscribed_channels", "TEXT DEFAULT '[]'"]
64
+ ];
65
+ for (const [name, definition] of missingPlayerColumns) {
66
+ if (!playerColumns.has(name)) db.exec(`ALTER TABLE sfmc_players ADD COLUMN "${name}" ${definition}`);
67
+ }
33
68
  db.exec(
34
69
  /* sql */
35
70
  `
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/domain/schema.ts"],
4
- "sourcesContent": ["/**\n * domain/schema.ts \u2014 \u6570\u636E\u5E93 Schema \u521D\u59CB\u5316\u5B9A\u4E49\uFF08\u5E73\u53F0\u6838\u5FC3\u5E95\u5EA7\u8868\uFF09\n *\n * \u96C6\u4E2D\u7BA1\u7406\u5E73\u53F0\u6838\u5FC3\u6570\u636E\u8868\u7684 CREATE TABLE IF NOT EXISTS \u4E0E CREATE INDEX IF NOT EXISTS DDL \u8BED\u53E5\u3002\n * \u8C03\u7528\u65B9\u5F0F\uFF1AinitSchema(db) \u2014\u2014 \u5728 openDatabase \u4E4B\u540E\u3001\u4E1A\u52A1\u67E5\u8BE2\u542F\u52A8\u524D\u6267\u884C\u3002\n *\n * \u67B6\u6784\u7EA6\u5B9A\uFF1A\n * \u4E1A\u52A1\u6A21\u5757\uFF08activity-log, coop, land, chat, economy \u7B49\uFF09\u79C1\u6709\u6570\u636E\u8868\u5747\u7531\u5404\u81EA\u6A21\u5757\u5728 SAPI \u8FD0\u884C\u671F\u901A\u8FC7\n * db.defineTable(...) \u81EA\u4E3B\u58F0\u660E\u4E0E\u7EF4\u62A4\uFF0C\u4E0D\u5728\u6B64\u5904\u9759\u6001\u786C\u7F16\u7801\uFF0C\u907F\u514D\u5217\u5B9A\u4E49\u4E0E\u7D22\u5F15\u51B2\u7A81\u3002\n *\n * \u6B64\u5904\u4EC5\u5B9A\u4E49\u5E73\u53F0\u6838\u5FC3\u8FD0\u884C\u3001\u8DE8\u670D\u52A1\u5171\u4EAB\uFF08qq-bridge / HTTP \u7AEF\u70B9 / status \u63A2\u6D3B\uFF09\u7684\u57FA\u7840\u5E95\u5EA7\u8868\uFF1A\n * 1. \u4E16\u754C\u4E0E\u73A9\u5BB6\u8FD0\u884C\u671F\u72B6\u6001\u5FEB\u7167\uFF08sfmc_world, sfmc_players\uFF09\n * 2. \u8DE8\u8FDB\u7A0B\u804A\u5929\u6D88\u606F\u805A\u5408\uFF08sfmc_chat_messages\uFF09\n * 3. QQ \u8EAB\u4EFD\u7ED1\u5B9A\u4E0E\u5BA1\u6279\u961F\u5217\uFF08sfmc_qq_*\uFF09\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\n\n/**\n * \u521D\u59CB\u5316\u6570\u636E\u5E93\u7ED3\u6784\uFF0C\u521B\u5EFA\u5E73\u53F0\u6838\u5FC3\u5E95\u5EA7\u8868\u4E0E\u7D22\u5F15\u3002\n *\n * @param db SQLite \u6570\u636E\u5E93\u8FDE\u63A5\u5B9E\u4F8B\u3002\n */\nexport function initSchema(db: DatabaseSync): void {\n // (1) \u4E16\u754C\u8FD0\u884C\u671F\u72B6\u6001\uFF08\u4F9B status \u8DEF\u7531\u4E0E qq-bridge \u8BFB\u53D6\uFF0C\u5B57\u6BB5\u4E0E data-backup \u517C\u5BB9\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_world (\n id TEXT PRIMARY KEY,\n seed TEXT DEFAULT '',\n difficulty TEXT DEFAULT '',\n spawn_x INTEGER DEFAULT 0,\n spawn_y INTEGER DEFAULT 0,\n spawn_z INTEGER DEFAULT 0,\n gamerules TEXT DEFAULT '{}',\n day INTEGER DEFAULT 0,\n absolute_time INTEGER DEFAULT 0,\n moon_phase INTEGER DEFAULT 0,\n updated_at INTEGER DEFAULT 0\n )`);\n\n // (2) \u73A9\u5BB6\u8FD0\u884C\u671F\u72B6\u6001\uFF08\u4F9B status \u8DEF\u7531\u4E0E qq-bridge \u8BFB\u53D6\uFF0C\u5B57\u6BB5\u4E0E data-backup \u517C\u5BB9\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_players (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL DEFAULT '',\n xuid TEXT DEFAULT '',\n last_online INTEGER DEFAULT 0,\n level INTEGER DEFAULT 0,\n total_xp INTEGER DEFAULT 0,\n tags TEXT DEFAULT '',\n updated_at INTEGER DEFAULT 0\n )`);\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_players_name ON sfmc_players(name)`);\n\n // (3) \u804A\u5929\u6D88\u606F\u805A\u5408\uFF08\u4F9B routes/messages \u4E0E qq-bridge \u53CC\u5411\u540C\u6B65\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_chat_messages (\n id TEXT PRIMARY KEY NOT NULL,\n channel_id TEXT NOT NULL DEFAULT '',\n from_id TEXT NOT NULL DEFAULT '',\n from_name TEXT NOT NULL DEFAULT '',\n type TEXT NOT NULL DEFAULT 'text',\n content TEXT NOT NULL DEFAULT '',\n attachment TEXT DEFAULT NULL,\n show_timestamp INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL\n )`);\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_chat_messages_channel_time ON sfmc_chat_messages(channel_id, created_at ASC);\n CREATE INDEX IF NOT EXISTS idx_chat_messages_created_at ON sfmc_chat_messages(created_at ASC)`);\n\n // (4) QQ\u2194MC \u8EAB\u4EFD\u7ED1\u5B9A\uFF08\u5E73\u53F0\u8868\uFF1Bqq-bridge / \u6E38\u620F\u6A21\u5757\u5171\u7528 HTTP\uFF0C\u975E\u6A21\u5757\u79C1\u6709\u8868\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_qq_bind_pending (\n code TEXT PRIMARY KEY NOT NULL,\n qq_user_openid TEXT NOT NULL,\n qq_backend TEXT NOT NULL DEFAULT 'official',\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL\n )`);\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_qq_bindings (\n qq_user_openid TEXT PRIMARY KEY NOT NULL,\n player_xuid TEXT NOT NULL UNIQUE,\n player_name TEXT NOT NULL DEFAULT '',\n qq_backend TEXT NOT NULL DEFAULT 'official',\n bound_at INTEGER NOT NULL\n )`);\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_qq_bind_pending_openid ON sfmc_qq_bind_pending(qq_user_openid);\n CREATE INDEX IF NOT EXISTS idx_qq_bindings_xuid ON sfmc_qq_bindings(player_xuid)`);\n\n // (5) QQ \u5165\u670D\u5BA1\u6279 + \u7BA1\u7406\u52A8\u4F5C\u961F\u5217\uFF08\u5E73\u53F0\u8868\uFF1BBDS \u751F\u6548\u7531 SAPI + server-admin\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_qq_join_requests (\n id TEXT PRIMARY KEY NOT NULL,\n applicant_openid TEXT NOT NULL,\n player_name TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n qq_backend TEXT NOT NULL DEFAULT 'official',\n created_at INTEGER NOT NULL,\n decided_at INTEGER,\n decided_by TEXT,\n applied_at INTEGER,\n apply_error TEXT\n )`);\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_qq_join_status ON sfmc_qq_join_requests(status, created_at)`);\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_qq_admin_actions (\n id TEXT PRIMARY KEY NOT NULL,\n kind TEXT NOT NULL,\n target_name TEXT NOT NULL,\n reason TEXT NOT NULL DEFAULT '',\n requested_by TEXT NOT NULL DEFAULT '',\n status TEXT NOT NULL DEFAULT 'pending',\n created_at INTEGER NOT NULL,\n applied_at INTEGER,\n apply_error TEXT\n )`);\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_qq_admin_actions_status ON sfmc_qq_admin_actions(status, created_at)`);\n}\n"],
5
- "mappings": "AAuBO,SAAS,WAAW,IAAwB;AAEjD,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAad;AAGJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA,EACkD;AAGpE,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA,EAE8E;AAGhG,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA,EAEiE;AAGnF,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA,EAC2E;AAC7F,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA,EACoF;AACxG;",
4
+ "sourcesContent": ["/**\n * domain/schema.ts \u2014 \u6570\u636E\u5E93 Schema \u521D\u59CB\u5316\u5B9A\u4E49\uFF08\u5E73\u53F0\u6838\u5FC3\u5E95\u5EA7\u8868\uFF09\n *\n * \u96C6\u4E2D\u7BA1\u7406\u5E73\u53F0\u6838\u5FC3\u6570\u636E\u8868\u7684 CREATE TABLE IF NOT EXISTS \u4E0E CREATE INDEX IF NOT EXISTS DDL \u8BED\u53E5\u3002\n * \u8C03\u7528\u65B9\u5F0F\uFF1AinitSchema(db) \u2014\u2014 \u5728 openDatabase \u4E4B\u540E\u3001\u4E1A\u52A1\u67E5\u8BE2\u542F\u52A8\u524D\u6267\u884C\u3002\n *\n * \u67B6\u6784\u7EA6\u5B9A\uFF1A\n * \u4E1A\u52A1\u6A21\u5757\uFF08activity-log, coop, land, chat, economy \u7B49\uFF09\u79C1\u6709\u6570\u636E\u8868\u5747\u7531\u5404\u81EA\u6A21\u5757\u5728 SAPI \u8FD0\u884C\u671F\u901A\u8FC7\n * db.defineTable(...) \u81EA\u4E3B\u58F0\u660E\u4E0E\u7EF4\u62A4\uFF0C\u4E0D\u5728\u6B64\u5904\u9759\u6001\u786C\u7F16\u7801\uFF0C\u907F\u514D\u5217\u5B9A\u4E49\u4E0E\u7D22\u5F15\u51B2\u7A81\u3002\n *\n * \u6B64\u5904\u4EC5\u5B9A\u4E49\u5E73\u53F0\u6838\u5FC3\u8FD0\u884C\u3001\u8DE8\u670D\u52A1\u5171\u4EAB\uFF08qq-bridge / HTTP \u7AEF\u70B9 / status \u63A2\u6D3B\uFF09\u7684\u57FA\u7840\u5E95\u5EA7\u8868\uFF1A\n * 1. \u4E16\u754C\u4E0E\u73A9\u5BB6\u8FD0\u884C\u671F\u72B6\u6001\u5FEB\u7167\uFF08sfmc_world, sfmc_players\uFF09\n * 2. \u8DE8\u8FDB\u7A0B\u804A\u5929\u6D88\u606F\u805A\u5408\uFF08sfmc_chat_messages\uFF09\n * 3. QQ \u8EAB\u4EFD\u7ED1\u5B9A\u4E0E\u5BA1\u6279\u961F\u5217\uFF08sfmc_qq_*\uFF09\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\n\n/**\n * \u521D\u59CB\u5316\u6570\u636E\u5E93\u7ED3\u6784\uFF0C\u521B\u5EFA\u5E73\u53F0\u6838\u5FC3\u5E95\u5EA7\u8868\u4E0E\u7D22\u5F15\u3002\n *\n * @param db SQLite \u6570\u636E\u5E93\u8FDE\u63A5\u5B9E\u4F8B\u3002\n */\nexport function initSchema(db: DatabaseSync): void {\n // (1) \u4E16\u754C\u8FD0\u884C\u671F\u72B6\u6001\uFF08\u4F9B status \u8DEF\u7531\u4E0E qq-bridge \u8BFB\u53D6\uFF0C\u5B57\u6BB5\u4E0E data-backup \u517C\u5BB9\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_world (\n id TEXT PRIMARY KEY,\n seed TEXT DEFAULT '',\n difficulty TEXT DEFAULT '',\n spawn_x INTEGER DEFAULT 0,\n spawn_y INTEGER DEFAULT 0,\n spawn_z INTEGER DEFAULT 0,\n gamerules TEXT DEFAULT '{}',\n day INTEGER DEFAULT 0,\n absolute_time INTEGER DEFAULT 0,\n moon_phase INTEGER DEFAULT 0,\n updated_at INTEGER DEFAULT 0\n )`);\n\n // (2) \u73A9\u5BB6\u8FD0\u884C\u671F\u72B6\u6001\uFF08\u4F9B status \u8DEF\u7531\u4E0E qq-bridge \u8BFB\u53D6\uFF0C\u5B57\u6BB5\u4E0E data-backup \u517C\u5BB9\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_players (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL DEFAULT '',\n xuid TEXT DEFAULT '',\n last_online INTEGER DEFAULT 0,\n level INTEGER DEFAULT 0,\n total_xp INTEGER DEFAULT 0,\n tags TEXT DEFAULT '',\n spawn_dimension TEXT DEFAULT '',\n spawn_x REAL,\n spawn_y REAL,\n spawn_z REAL,\n dimension TEXT DEFAULT '',\n x REAL,\n y REAL,\n z REAL,\n game_mode TEXT DEFAULT '',\n snapshot_hash TEXT DEFAULT '',\n active_channel TEXT DEFAULT '',\n subscribed_channels TEXT DEFAULT '[]',\n updated_at INTEGER DEFAULT 0\n )`);\n const playerColumns = new Map(\n (db.prepare(`PRAGMA table_info(\"sfmc_players\")`).all() as Array<{ name: string }>).map((column) => [\n column.name,\n true,\n ])\n );\n const missingPlayerColumns: Array<[string, string]> = [\n [\"spawn_dimension\", \"TEXT DEFAULT ''\"],\n [\"spawn_x\", \"REAL\"],\n [\"spawn_y\", \"REAL\"],\n [\"spawn_z\", \"REAL\"],\n [\"dimension\", \"TEXT DEFAULT ''\"],\n [\"x\", \"REAL\"],\n [\"y\", \"REAL\"],\n [\"z\", \"REAL\"],\n [\"game_mode\", \"TEXT DEFAULT ''\"],\n [\"snapshot_hash\", \"TEXT DEFAULT ''\"],\n [\"active_channel\", \"TEXT DEFAULT ''\"],\n [\"subscribed_channels\", \"TEXT DEFAULT '[]'\"],\n ];\n for (const [name, definition] of missingPlayerColumns) {\n if (!playerColumns.has(name)) db.exec(`ALTER TABLE sfmc_players ADD COLUMN \"${name}\" ${definition}`);\n }\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_players_name ON sfmc_players(name)`);\n\n // (3) \u804A\u5929\u6D88\u606F\u805A\u5408\uFF08\u4F9B routes/messages \u4E0E qq-bridge \u53CC\u5411\u540C\u6B65\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_chat_messages (\n id TEXT PRIMARY KEY NOT NULL,\n channel_id TEXT NOT NULL DEFAULT '',\n from_id TEXT NOT NULL DEFAULT '',\n from_name TEXT NOT NULL DEFAULT '',\n type TEXT NOT NULL DEFAULT 'text',\n content TEXT NOT NULL DEFAULT '',\n attachment TEXT DEFAULT NULL,\n show_timestamp INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL\n )`);\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_chat_messages_channel_time ON sfmc_chat_messages(channel_id, created_at ASC);\n CREATE INDEX IF NOT EXISTS idx_chat_messages_created_at ON sfmc_chat_messages(created_at ASC)`);\n\n // (4) QQ\u2194MC \u8EAB\u4EFD\u7ED1\u5B9A\uFF08\u5E73\u53F0\u8868\uFF1Bqq-bridge / \u6E38\u620F\u6A21\u5757\u5171\u7528 HTTP\uFF0C\u975E\u6A21\u5757\u79C1\u6709\u8868\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_qq_bind_pending (\n code TEXT PRIMARY KEY NOT NULL,\n qq_user_openid TEXT NOT NULL,\n qq_backend TEXT NOT NULL DEFAULT 'official',\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL\n )`);\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_qq_bindings (\n qq_user_openid TEXT PRIMARY KEY NOT NULL,\n player_xuid TEXT NOT NULL UNIQUE,\n player_name TEXT NOT NULL DEFAULT '',\n qq_backend TEXT NOT NULL DEFAULT 'official',\n bound_at INTEGER NOT NULL\n )`);\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_qq_bind_pending_openid ON sfmc_qq_bind_pending(qq_user_openid);\n CREATE INDEX IF NOT EXISTS idx_qq_bindings_xuid ON sfmc_qq_bindings(player_xuid)`);\n\n // (5) QQ \u5165\u670D\u5BA1\u6279 + \u7BA1\u7406\u52A8\u4F5C\u961F\u5217\uFF08\u5E73\u53F0\u8868\uFF1BBDS \u751F\u6548\u7531 SAPI + server-admin\uFF09\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_qq_join_requests (\n id TEXT PRIMARY KEY NOT NULL,\n applicant_openid TEXT NOT NULL,\n player_name TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n qq_backend TEXT NOT NULL DEFAULT 'official',\n created_at INTEGER NOT NULL,\n decided_at INTEGER,\n decided_by TEXT,\n applied_at INTEGER,\n apply_error TEXT\n )`);\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_qq_join_status ON sfmc_qq_join_requests(status, created_at)`);\n db.exec(/* sql */ `\n CREATE TABLE IF NOT EXISTS sfmc_qq_admin_actions (\n id TEXT PRIMARY KEY NOT NULL,\n kind TEXT NOT NULL,\n target_name TEXT NOT NULL,\n reason TEXT NOT NULL DEFAULT '',\n requested_by TEXT NOT NULL DEFAULT '',\n status TEXT NOT NULL DEFAULT 'pending',\n created_at INTEGER NOT NULL,\n applied_at INTEGER,\n apply_error TEXT\n )`);\n db.exec(/* sql */ `\n CREATE INDEX IF NOT EXISTS idx_qq_admin_actions_status ON sfmc_qq_admin_actions(status, created_at)`);\n}\n"],
5
+ "mappings": "AAuBO,SAAS,WAAW,IAAwB;AAEjD,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAad;AAGJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBd;AACJ,QAAM,gBAAgB,IAAI;AAAA,IACvB,GAAG,QAAQ,mCAAmC,EAAE,IAAI,EAA8B,IAAI,CAAC,WAAW;AAAA,MACjG,OAAO;AAAA,MACP;AAAA,IACF,CAAC;AAAA,EACH;AACA,QAAM,uBAAgD;AAAA,IACpD,CAAC,mBAAmB,iBAAiB;AAAA,IACrC,CAAC,WAAW,MAAM;AAAA,IAClB,CAAC,WAAW,MAAM;AAAA,IAClB,CAAC,WAAW,MAAM;AAAA,IAClB,CAAC,aAAa,iBAAiB;AAAA,IAC/B,CAAC,KAAK,MAAM;AAAA,IACZ,CAAC,KAAK,MAAM;AAAA,IACZ,CAAC,KAAK,MAAM;AAAA,IACZ,CAAC,aAAa,iBAAiB;AAAA,IAC/B,CAAC,iBAAiB,iBAAiB;AAAA,IACnC,CAAC,kBAAkB,iBAAiB;AAAA,IACpC,CAAC,uBAAuB,mBAAmB;AAAA,EAC7C;AACA,aAAW,CAAC,MAAM,UAAU,KAAK,sBAAsB;AACrD,QAAI,CAAC,cAAc,IAAI,IAAI,EAAG,IAAG,KAAK,wCAAwC,IAAI,KAAK,UAAU,EAAE;AAAA,EACrG;AACA,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA,EACkD;AAGpE,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA,EAE8E;AAGhG,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA,EAEiE;AAGnF,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA,EAC2E;AAC7F,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWd;AACJ,KAAG;AAAA;AAAA,IAAe;AAAA;AAAA,EACoF;AACxG;",
6
6
  "names": []
7
7
  }
package/dist/index.d.ts CHANGED
@@ -28,5 +28,5 @@ declare const moduleAuth: import("./module-auth.js").ModuleAuthMap;
28
28
  declare const schemaRegistry: SchemaRegistry;
29
29
  declare const serviceRegistry: ServiceRegistry;
30
30
  declare const txRunner: TxRunner;
31
- export { db, env, query, schemaRegistry, serviceRegistry, txRunner, enabledManifests, moduleAuth };
31
+ export { db, enabledManifests, env, moduleAuth, query, schemaRegistry, serviceRegistry, txRunner };
32
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAcH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAgC1C,QAAA,MAAM,GAAG,8BAAY,CAAC;AACtB,QAAA,MAAM,EAAE,oCAA4B,CAAC;AAGrC,QAAA,MAAM,KAAK,mCAAkB,CAAC;AA4B9B,QAAA,MAAM,gBAAgB,8DAAwE,CAAC;AAU/F,QAAA,MAAM,UAAU,0CAId,CAAC;AAGH,QAAA,MAAM,cAAc,gBAAyB,CAAC;AAC9C,QAAA,MAAM,eAAe,iBAAwB,CAAC;AAE9C,QAAA,MAAM,QAAQ,UAMZ,CAAC;AAwXH,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,QAAQ,EAAE,gBAAgB,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAeH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AA2B1C,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;AAGH,QAAA,MAAM,cAAc,gBAAyB,CAAC;AAC9C,QAAA,MAAM,eAAe,iBAAwB,CAAC;AAE9C,QAAA,MAAM,QAAQ,UAMZ,CAAC;AA+YH,OAAO,EAAE,EAAE,EAAE,gBAAgB,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -1,40 +1,38 @@
1
1
  import http from "node:http";
2
- import { createIdempotencyStore } from "./lib/idempotency-store.js";
2
+ import { createPlatformTables } from "./db-tables.js";
3
+ import { initSchema } from "./domain/schema.js";
4
+ import { syncPlayerSession } from "./domain/player-session.js";
3
5
  import { loadEnv } from "./env.js";
4
- import { buildModuleAuth, verifyModuleAuth } from "./module-auth.js";
5
- import { loadManifestV2 } from "./manifest-loader.js";
6
+ import { createIdempotencyStore } from "./lib/idempotency-store.js";
6
7
  import { log } from "./lib/log.js";
7
8
  import { assertNodeVersion } from "./lib/runtime.js";
8
9
  import { createQuery, openDatabase } from "./lib/sqlite.js";
9
- import { createServer, startConsole } from "./server.js";
10
- import { createPlatformTables } from "./db-tables.js";
11
- import { initSchema } from "./domain/schema.js";
10
+ import { loadManifestV2 } from "./manifest-loader.js";
11
+ import { buildModuleAuth, verifyModuleAuth } from "./module-auth.js";
12
+ import { syncModuleRuntimeState } from "./module-runtime-sync.js";
12
13
  import { SchemaRegistry } from "./schema-registry.js";
14
+ import { createServer, startConsole } from "./server.js";
13
15
  import { ServiceRegistry } from "./service-registry.js";
14
- import { TxRunner } from "./tx-runner.js";
15
16
  import { registerEnabledBuiltinServices } from "./services/builtin-handlers.js";
16
- import { syncModuleRuntimeState } from "./module-runtime-sync.js";
17
+ import { TxRunner } from "./tx-runner.js";
17
18
  import { ensureJson, patchJson, readJson } from "@sfmc-bds/sdk/node/config";
18
19
  import { join } from "node:path";
19
- import { createModuleConfigRoutes } from "./routes/module-config-routes.js";
20
+ import { jsonV2Fail } from "./routes/_shared.js";
20
21
  import { createDbRoutes } from "./routes/db-routes.js";
22
+ import { createModuleConfigRoutes } from "./routes/module-config-routes.js";
21
23
  import { createServiceRoutes } from "./routes/service-routes.js";
22
- import { jsonV2Fail } from "./routes/_shared.js";
23
- import { createStatusRoutes } from "./routes/status.js";
24
- import { createQqBindRoutes } from "./routes/qq-bind.js";
25
- import { createQqJoinRoutes } from "./routes/qq-join.js";
26
- import { createQqEventsRoutes } from "./routes/qq-events.js";
27
- import { createMessagesRoutes } from "./routes/messages.js";
28
- import { createModuleRoutes } from "./routes/modules.js";
29
- import { createConfigRoutes } from "./routes/config.js";
30
- import { createHealthRoutes } from "./routes/health.js";
31
24
  import { forwardToQQBridge, makeOutboundConfig } from "./domain/bridge.js";
32
- import {
33
- createQqEventsAggregator,
34
- resolveQqEventsConfig
35
- } from "./domain/qq-events.js";
36
- import { isEnabled, loadModuleLock, saveModuleLock, updateModuleState } from "./lib/module-state.js";
25
+ import { createQqEventsAggregator, resolveQqEventsConfig } from "./domain/qq-events.js";
37
26
  import { body as sharedBody, json as sharedJson } from "./lib/http.js";
27
+ import { isEnabled, loadModuleLock, saveModuleLock, updateModuleState } from "./lib/module-state.js";
28
+ import { createConfigRoutes } from "./routes/config.js";
29
+ import { createHealthRoutes } from "./routes/health.js";
30
+ import { createMessagesRoutes } from "./routes/messages.js";
31
+ import { createModuleRoutes } from "./routes/modules.js";
32
+ import { createQqBindRoutes } from "./routes/qq-bind.js";
33
+ import { createQqEventsRoutes } from "./routes/qq-events.js";
34
+ import { createQqJoinRoutes } from "./routes/qq-join.js";
35
+ import { createStatusRoutes } from "./routes/status.js";
38
36
  if (!assertNodeVersion(22, 13)) {
39
37
  process.exit(2);
40
38
  }
@@ -55,9 +53,7 @@ const catalogIds = new Set(
55
53
  const enabledSet = /* @__PURE__ */ new Set();
56
54
  for (const id of Object.keys(loadedManifest.modules)) {
57
55
  if (!catalogIds.has(id)) continue;
58
- const catalogEntry = moduleCatalog.modules.find(
59
- (m) => String(m.id) === id
60
- );
56
+ const catalogEntry = moduleCatalog.modules.find((m) => String(m.id) === id);
61
57
  if (!catalogEntry) continue;
62
58
  const defaultEnabled = catalogEntry.enabledByDefault !== false;
63
59
  if (isEnabled(lockFile, id, defaultEnabled)) enabledSet.add(id);
@@ -67,9 +63,7 @@ for (const id of enabledSet) {
67
63
  const m = loadedManifest.modules[id];
68
64
  if (m) enabledManifests.set(id, m);
69
65
  }
70
- log.info(
71
- `[manifest v2] enabled: ${[...enabledSet].sort().join(", ") || "(none)"}`
72
- );
66
+ log.info(`[manifest v2] enabled: ${[...enabledSet].sort().join(", ") || "(none)"}`);
73
67
  const moduleAuth = buildModuleAuth({
74
68
  dbPath: env.DB_PATH,
75
69
  envAuthToken: env.AUTH_TOKEN,
@@ -88,9 +82,7 @@ const txRunner = new TxRunner({
88
82
  {
89
83
  const plugins = registerEnabledBuiltinServices(serviceRegistry, { query, db }, enabledSet);
90
84
  if (plugins > 0) {
91
- log.success(
92
- `[service] registered ${serviceRegistry.list().length} handlers from ${plugins} builtin plugin(s)`
93
- );
85
+ log.success(`[service] registered ${serviceRegistry.list().length} handlers from ${plugins} builtin plugin(s)`);
94
86
  }
95
87
  }
96
88
  const json = sharedJson;
@@ -326,6 +318,18 @@ async function handle(req, res) {
326
318
  }
327
319
  }
328
320
  try {
321
+ if (path === "/api/sfmc/player-session" && method === "POST") {
322
+ const data = await body(req);
323
+ const playerName = String(data.playerName ?? "").trim();
324
+ const xuid = String(data.xuid ?? "").trim();
325
+ if (!playerName || !xuid) {
326
+ json(res, { success: false, error: "playerName_and_xuid_required" }, 400);
327
+ return;
328
+ }
329
+ syncPlayerSession(query, { playerName, xuid });
330
+ json(res, { success: true });
331
+ return;
332
+ }
329
333
  if (moduleAuthCtx && (path.startsWith("/api/sfmc/db/") || path.startsWith("/api/sfmc/services") || /^\/api\/sfmc\/configs\/[A-Za-z0-9_-]+/.test(path) && !isLegacyConfigAll)) {
330
334
  const ctx = {
331
335
  path,
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\";\n\nimport { createIdempotencyStore } from \"./lib/idempotency-store.js\";\nimport { loadEnv } from \"./env.js\";\nimport { buildModuleAuth, verifyModuleAuth } from \"./module-auth.js\";\nimport { loadManifestV2 } from \"./manifest-loader.js\";\nimport { log } from \"./lib/log.js\";\nimport { assertNodeVersion } from \"./lib/runtime.js\";\nimport { createQuery, openDatabase } from \"./lib/sqlite.js\";\nimport { createServer, startConsole } from \"./server.js\";\nimport { createPlatformTables } from \"./db-tables.js\";\nimport { initSchema } from \"./domain/schema.js\";\nimport { SchemaRegistry } from \"./schema-registry.js\";\nimport { ServiceRegistry } from \"./service-registry.js\";\nimport { TxRunner } from \"./tx-runner.js\";\nimport { registerEnabledBuiltinServices } from \"./services/builtin-handlers.js\";\nimport { syncModuleRuntimeState } from \"./module-runtime-sync.js\";\n\nimport { ensureJson, patchJson, readJson } from \"@sfmc-bds/sdk/node/config\";\nimport { join } from \"node:path\";\n\nimport { createModuleConfigRoutes } from \"./routes/module-config-routes.js\";\nimport { createDbRoutes } from \"./routes/db-routes.js\";\nimport { createServiceRoutes } from \"./routes/service-routes.js\";\nimport { jsonV2Fail } from \"./routes/_shared.js\";\n\nimport { createStatusRoutes } from \"./routes/status.js\";\nimport { createQqBindRoutes } from \"./routes/qq-bind.js\";\nimport { createQqJoinRoutes, type JoinFeatureFlags } from \"./routes/qq-join.js\";\nimport { createQqEventsRoutes } from \"./routes/qq-events.js\";\nimport { createMessagesRoutes } from \"./routes/messages.js\";\nimport { createModuleRoutes } from \"./routes/modules.js\";\nimport { createConfigRoutes } from \"./routes/config.js\";\nimport { createHealthRoutes } from \"./routes/health.js\";\nimport { forwardToQQBridge, makeOutboundConfig } from \"./domain/bridge.js\";\nimport {\n createQqEventsAggregator,\n resolveQqEventsConfig,\n} from \"./domain/qq-events.js\";\nimport { isEnabled, loadModuleLock, saveModuleLock, updateModuleState } from \"./lib/module-state.js\";\nimport { body as sharedBody, json as sharedJson } from \"./lib/http.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 }>)\n .map((m) => String(m.id || \"\"))\n .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(\n (m) => String(m.id) === id\n );\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(\n `[manifest v2] enabled: ${[...enabledSet].sort().join(\", \") || \"(none)\"}`\n);\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\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(\n `[service] registered ${serviceRegistry.list().length} handlers from ${plugins} builtin plugin(s)`\n );\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/** 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();\nconst statusRoutes = createStatusRoutes({ query });\nconst qqBindRoutes = createQqBindRoutes({ query, body, json });\n\n/** \u6A21\u5757 qq-link \u7684\u914D\u7F6E\u6587\u4EF6\uFF08\u975E SDK qq_config\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\u4EC5\u6539\u672C\u6587\u4EF6\uFF0CAPI/\u7FA4\u804A\u4E0D\u53EF\u5199 */\n treat_group_admins_as_admins: false,\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 // \u6A21\u5757\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\u6A21\u5757\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\u6A21\u5757\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[\"qq_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 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 MCTOQQ_PREFIX: env.MCTOQQ_PREFIX,\n });\n}\n\nconst qqEventsAggregator = createQqEventsAggregator({\n getConfig: () => resolveQqEventsConfig(env.qqconfig[\"qq_events\"]),\n getOutbound: () => currentOutbound(),\n});\nconst qqEventsRoutes = createQqEventsRoutes({\n body,\n json,\n aggregator: qqEventsAggregator,\n});\n\nconst messagesRoutes = createMessagesRoutes({\n query,\n body,\n json,\n getBridgeChannelId: () => String(env.QQ_BRIDGE_CHANNEL_ID ?? \"\").trim(),\n forwardToQQBridge: (channelId: string, fromName: string, content: string, fromId: string) =>\n forwardToQQBridge(currentOutbound(), channelId, fromName, content, fromId),\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 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 (ctx: Record<string, unknown>) => 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 path === \"/api/sfmc/qq/admin/action-queue\" ||\n (method === \"GET\" &&\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/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 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/admin/\") ||\n path.startsWith(\"/api/sfmc/qq/events\")\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 // \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 (moduleAuthCtx && (path.startsWith(\"/api/sfmc/db/\") || path.startsWith(\"/api/sfmc/services\") || (/^\\/api\\/sfmc\\/configs\\/[A-Za-z0-9_-]+/.test(path) && !isLegacyConfigAll))) {\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 { path: string; method: string; params: URLSearchParams; req: http.IncomingMessage; res: http.ServerResponse };\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 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, env, query, schemaRegistry, serviceRegistry, txRunner, enabledManifests, moduleAuth };\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\";\n\nimport { createPlatformTables } from \"./db-tables.js\";\nimport { initSchema } from \"./domain/schema.js\";\nimport { syncPlayerSession } from \"./domain/player-session.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, 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 { ensureJson, patchJson, readJson } from \"@sfmc-bds/sdk/node/config\";\nimport { 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 { createQqEventsAggregator, resolveQqEventsConfig } 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 { createHealthRoutes } from \"./routes/health.js\";\nimport { createMessagesRoutes } from \"./routes/messages.js\";\nimport { createModuleRoutes } from \"./routes/modules.js\";\nimport { createQqBindRoutes } from \"./routes/qq-bind.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\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/** 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();\nconst statusRoutes = createStatusRoutes({ query });\nconst qqBindRoutes = createQqBindRoutes({ query, body, json });\n\n/** \u6A21\u5757 qq-link \u7684\u914D\u7F6E\u6587\u4EF6\uFF08\u975E SDK qq_config\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\u4EC5\u6539\u672C\u6587\u4EF6\uFF0CAPI/\u7FA4\u804A\u4E0D\u53EF\u5199 */\n treat_group_admins_as_admins: false,\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 // \u6A21\u5757\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\u6A21\u5757\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\u6A21\u5757\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[\"qq_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 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 MCTOQQ_PREFIX: env.MCTOQQ_PREFIX,\n });\n}\n\nconst qqEventsAggregator = createQqEventsAggregator({\n getConfig: () => resolveQqEventsConfig(env.qqconfig[\"qq_events\"]),\n getOutbound: () => currentOutbound(),\n});\nconst qqEventsRoutes = createQqEventsRoutes({\n body,\n json,\n aggregator: qqEventsAggregator,\n});\n\nconst messagesRoutes = createMessagesRoutes({\n query,\n body,\n json,\n getBridgeChannelId: () => String(env.QQ_BRIDGE_CHANNEL_ID ?? \"\").trim(),\n forwardToQQBridge: (channelId: string, fromName: string, content: string, fromId: string) =>\n forwardToQQBridge(currentOutbound(), channelId, fromName, content, fromId),\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 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 path === \"/api/sfmc/qq/admin/action-queue\" ||\n (method === \"GET\" &&\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/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 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/admin/\") ||\n path.startsWith(\"/api/sfmc/qq/events\")\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 moduleRoutesInstance(ctxBase)) return;\n if (await healthRoutes(ctxBase)) return;\n if (await statusRoutes(ctxBase)) return;\n if (await qqBindRoutes(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": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfmc-bds/db-server",
3
- "version": "0.2.0-beta.10",
3
+ "version": "0.2.0-beta.11",
4
4
  "description": "SFMC SQLite HTTP REST backend — configs, modules, gameplay data",
5
5
  "author": "Shiroha7z",
6
6
  "license": "AGPL-3.0-only",