@sfmc-bds/db-server 0.1.0 → 0.2.0-beta.0
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/README.md +1 -1
- package/dist/db-tables.js +16 -17
- package/dist/db-tables.js.map +7 -1
- package/dist/domain/bridge.js +54 -77
- package/dist/domain/bridge.js.map +7 -1
- package/dist/domain/economy.d.ts +83 -2
- package/dist/domain/economy.d.ts.map +1 -1
- package/dist/domain/economy.js +292 -281
- package/dist/domain/economy.js.map +7 -1
- package/dist/domain/schema.js +480 -426
- package/dist/domain/schema.js.map +7 -1
- package/dist/domain/transaction.js +32 -80
- package/dist/domain/transaction.js.map +7 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +85 -84
- package/dist/env.js.map +7 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +218 -240
- package/dist/index.js.map +7 -1
- package/dist/lib/http.js +30 -39
- package/dist/lib/http.js.map +7 -1
- package/dist/lib/idempotency-store.js +34 -47
- package/dist/lib/idempotency-store.js.map +7 -1
- package/dist/lib/idempotency.js +8 -13
- package/dist/lib/idempotency.js.map +7 -1
- package/dist/lib/identifiers.js +18 -14
- package/dist/lib/identifiers.js.map +7 -1
- package/dist/lib/log.d.ts +3 -6
- package/dist/lib/log.d.ts.map +1 -1
- package/dist/lib/log.js +13 -14
- package/dist/lib/log.js.map +7 -1
- package/dist/lib/module-state.d.ts.map +1 -1
- package/dist/lib/module-state.js +32 -21
- package/dist/lib/module-state.js.map +7 -1
- package/dist/lib/order-by.d.ts +17 -0
- package/dist/lib/order-by.d.ts.map +1 -0
- package/dist/lib/order-by.js +24 -0
- package/dist/lib/order-by.js.map +7 -0
- package/dist/lib/runtime.js +16 -31
- package/dist/lib/runtime.js.map +7 -1
- package/dist/lib/sql-helpers.js +9 -27
- package/dist/lib/sql-helpers.js.map +7 -1
- package/dist/lib/sqlite.js +40 -52
- package/dist/lib/sqlite.js.map +7 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +154 -195
- package/dist/manifest-loader.js.map +7 -1
- package/dist/manifest.d.ts +3 -7
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +76 -115
- package/dist/manifest.js.map +7 -1
- package/dist/module-auth.d.ts +17 -6
- package/dist/module-auth.d.ts.map +1 -1
- package/dist/module-auth.js +93 -107
- package/dist/module-auth.js.map +7 -1
- package/dist/module-runtime-sync.d.ts +37 -0
- package/dist/module-runtime-sync.d.ts.map +1 -0
- package/dist/module-runtime-sync.js +54 -0
- package/dist/module-runtime-sync.js.map +7 -0
- package/dist/permission-gate.d.ts.map +1 -1
- package/dist/permission-gate.js +55 -86
- package/dist/permission-gate.js.map +7 -1
- package/dist/routes/_shared.d.ts +7 -2
- package/dist/routes/_shared.d.ts.map +1 -1
- package/dist/routes/_shared.js +16 -25
- package/dist/routes/_shared.js.map +7 -1
- package/dist/routes/config.d.ts +11 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +167 -213
- package/dist/routes/config.js.map +7 -1
- package/dist/routes/db-routes.d.ts +6 -10
- package/dist/routes/db-routes.d.ts.map +1 -1
- package/dist/routes/db-routes.js +198 -172
- package/dist/routes/db-routes.js.map +7 -1
- package/dist/routes/health.js +15 -20
- package/dist/routes/health.js.map +7 -1
- package/dist/routes/messages.d.ts.map +1 -1
- package/dist/routes/messages.js +56 -63
- package/dist/routes/messages.js.map +7 -1
- package/dist/routes/module-config-routes.d.ts.map +1 -1
- package/dist/routes/module-config-routes.js +92 -111
- package/dist/routes/module-config-routes.js.map +7 -1
- package/dist/routes/modules.d.ts.map +1 -1
- package/dist/routes/modules.js +82 -88
- package/dist/routes/modules.js.map +7 -1
- package/dist/routes/service-routes.d.ts.map +1 -1
- package/dist/routes/service-routes.js +54 -61
- package/dist/routes/service-routes.js.map +7 -1
- package/dist/runtime.test.js +248 -49
- package/dist/runtime.test.js.map +7 -1
- package/dist/schema-registry.d.ts.map +1 -1
- package/dist/schema-registry.js +119 -160
- package/dist/schema-registry.js.map +7 -1
- package/dist/server.js +69 -72
- package/dist/server.js.map +7 -1
- package/dist/service-registry.d.ts.map +1 -1
- package/dist/service-registry.js +60 -67
- package/dist/service-registry.js.map +7 -1
- package/dist/services/builtin-handlers.d.ts +4 -0
- package/dist/services/builtin-handlers.d.ts.map +1 -1
- package/dist/services/builtin-handlers.js +34 -20
- package/dist/services/builtin-handlers.js.map +7 -1
- package/dist/services/economy-handlers.js +143 -132
- package/dist/services/economy-handlers.js.map +7 -1
- package/dist/tx-runner.d.ts +35 -11
- package/dist/tx-runner.d.ts.map +1 -1
- package/dist/tx-runner.js +293 -250
- package/dist/tx-runner.js.map +7 -1
- package/dist/where.js +71 -94
- package/dist/where.js.map +7 -1
- package/package.json +4 -4
package/dist/routes/db-routes.js
CHANGED
|
@@ -1,186 +1,212 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* routes/db-routes.ts — /api/sfmc/db/* 处理器
|
|
3
|
-
*
|
|
4
|
-
* 端点:
|
|
5
|
-
* POST /api/sfmc/db/define-table body {name, columns, softDelete?} → {table, created}
|
|
6
|
-
* POST /api/sfmc/db/tx body {steps[]} → TxResponse | TxError
|
|
7
|
-
* POST /api/sfmc/db/query body {table, opts?} → {rows}
|
|
8
|
-
* POST /api/sfmc/db/get body {table, id} → {row}
|
|
9
|
-
* POST /api/sfmc/db/insert body {table, row} → {row}
|
|
10
|
-
* POST /api/sfmc/db/update body {table, id, patch} → {row}
|
|
11
|
-
* POST /api/sfmc/db/delete body {table, id, hard?} → {changes}
|
|
12
|
-
* POST /api/sfmc/db/audit body {table, rowId, action, data?} → {ok}
|
|
13
|
-
* POST /api/sfmc/db/idempotent/probe body {action, key} → {replayed, cached?}
|
|
14
|
-
* POST /api/sfmc/db/idempotent/commit body {action, key, value?} → {ok}
|
|
15
|
-
*
|
|
16
|
-
* 鉴权:handle() 校验后把 {id, permissions} 写到 ctx.moduleAuth(不挂 req)。
|
|
17
|
-
*/
|
|
18
1
|
import { json as defaultJson } from "../lib/http.js";
|
|
19
|
-
import { jsonV2Fail } from "./_shared.js";
|
|
2
|
+
import { jsonV2Fail, jsonV2Ok } from "./_shared.js";
|
|
20
3
|
import { PermissionDeniedError } from "../permission-gate.js";
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
4
|
+
function createDbRoutes(depsIn) {
|
|
5
|
+
const deps = depsIn;
|
|
6
|
+
if (!deps.schemaRegistry || !deps.txRunner || !deps.idempotent) {
|
|
7
|
+
throw new Error("createDbRoutes: \u7F3A\u5C11 schemaRegistry / txRunner / idempotent");
|
|
8
|
+
}
|
|
9
|
+
const json = deps.json || defaultJson;
|
|
10
|
+
return async (ctx) => {
|
|
11
|
+
const { path, method, res } = ctx;
|
|
12
|
+
if (!path.startsWith("/api/sfmc/db/")) return false;
|
|
13
|
+
if (method !== "POST" && method !== "DELETE") return false;
|
|
14
|
+
const auth = ctx.moduleAuth ?? null;
|
|
15
|
+
if (!auth) {
|
|
16
|
+
jsonV2Fail(res, "unauthorized: module identity missing", 401, "unauthorized");
|
|
17
|
+
return true;
|
|
25
18
|
}
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
const body = await ctx.body || {};
|
|
20
|
+
const moduleId = auth.id;
|
|
21
|
+
if (path === "/api/sfmc/db/define-table") {
|
|
22
|
+
try {
|
|
23
|
+
const req2 = body;
|
|
24
|
+
delete req2.moduleId;
|
|
25
|
+
const result = deps.schemaRegistry.define(moduleId, req2);
|
|
26
|
+
jsonV2Ok(res, { table: result.table, created: result.created });
|
|
27
|
+
} catch (e) {
|
|
28
|
+
jsonV2Fail(res, e.message, 400);
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (path === "/api/sfmc/db/tx") {
|
|
33
|
+
try {
|
|
34
|
+
const txReq = body;
|
|
35
|
+
const steps = Array.isArray(txReq.steps) ? txReq.steps : [];
|
|
36
|
+
const result = await deps.txRunner.run({ moduleId, steps });
|
|
37
|
+
const status = result.ok ? 200 : 400;
|
|
38
|
+
json(res, result, status);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
jsonV2Fail(res, e.message, 500);
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
const sessionOps = [
|
|
45
|
+
{
|
|
46
|
+
path: "/api/sfmc/db/tx/begin",
|
|
47
|
+
run: () => deps.txRunner.beginSession(moduleId)
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
path: "/api/sfmc/db/tx/step",
|
|
51
|
+
requireTxId: true,
|
|
52
|
+
requireStep: true,
|
|
53
|
+
run: ({ txId, step }) => deps.txRunner.stepSession(txId, moduleId, step)
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
path: "/api/sfmc/db/tx/commit",
|
|
57
|
+
requireTxId: true,
|
|
58
|
+
run: ({ txId }) => deps.txRunner.commitSession(txId, moduleId)
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
path: "/api/sfmc/db/tx/rollback",
|
|
62
|
+
requireTxId: true,
|
|
63
|
+
run: ({ txId }) => deps.txRunner.rollbackSession(txId, moduleId)
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
for (const op of sessionOps) {
|
|
67
|
+
if (path !== op.path) continue;
|
|
68
|
+
try {
|
|
69
|
+
const txId = String(body.txId || "");
|
|
70
|
+
const step = body.step;
|
|
71
|
+
if (op.requireTxId && !txId) {
|
|
72
|
+
jsonV2Fail(res, `${op.path.split("/").pop()} \u9700\u8981 { txId }`, 400);
|
|
73
|
+
return true;
|
|
37
74
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
const req2 = body;
|
|
43
|
-
delete req2.moduleId;
|
|
44
|
-
const result = deps.schemaRegistry.define(moduleId, req2);
|
|
45
|
-
json(res, { success: true, table: result.table, created: result.created }, 200);
|
|
46
|
-
}
|
|
47
|
-
catch (e) {
|
|
48
|
-
json(res, { success: false, error: e.message }, 400);
|
|
49
|
-
}
|
|
50
|
-
return true;
|
|
75
|
+
if (op.requireStep && (!step || typeof step !== "object" || !("op" in step))) {
|
|
76
|
+
jsonV2Fail(res, "tx/step \u9700\u8981 { txId, step }", 400);
|
|
77
|
+
return true;
|
|
51
78
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
const result = await op.run(step !== void 0 ? { txId, step } : { txId });
|
|
80
|
+
json(res, result, result.ok ? 200 : 400);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
jsonV2Fail(res, e.message, 500);
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
const singles = [
|
|
87
|
+
{ match: /^\/api\/sfmc\/db\/query$/, op: "query" },
|
|
88
|
+
{ match: /^\/api\/sfmc\/db\/get$/, op: "get" },
|
|
89
|
+
{ match: /^\/api\/sfmc\/db\/insert$/, op: "insert" },
|
|
90
|
+
{ match: /^\/api\/sfmc\/db\/update$/, op: "update" },
|
|
91
|
+
{ match: /^\/api\/sfmc\/db\/delete$/, op: "delete" }
|
|
92
|
+
];
|
|
93
|
+
for (const cand of singles) {
|
|
94
|
+
if (cand.match.test(path)) {
|
|
95
|
+
try {
|
|
96
|
+
const step = buildSingleStep(cand.op, body);
|
|
97
|
+
const result = await deps.txRunner.run({ moduleId, steps: [step] });
|
|
98
|
+
if (!result.ok) {
|
|
99
|
+
const code = result.code === "permission_denied" ? 403 : 400;
|
|
100
|
+
jsonV2Fail(res, result.error, code, result.code, { step: result.step });
|
|
66
101
|
return true;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{ match: /^\/api\/sfmc\/db\/delete$/, op: "delete" },
|
|
74
|
-
];
|
|
75
|
-
for (const cand of singles) {
|
|
76
|
-
if (cand.match.test(path)) {
|
|
77
|
-
try {
|
|
78
|
-
const step = buildSingleStep(cand.op, body);
|
|
79
|
-
const result = await deps.txRunner.run({ moduleId, steps: [step] });
|
|
80
|
-
if (!result.ok) {
|
|
81
|
-
const code = result.code === "permission_denied" ? 403 : 400;
|
|
82
|
-
json(res, { success: false, error: result.error, step: result.step, code: result.code }, code);
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
const first = result.results[0];
|
|
86
|
-
json(res, { success: true, ...unwrapResult(first, cand.op) }, 200);
|
|
87
|
-
}
|
|
88
|
-
catch (e) {
|
|
89
|
-
if (e instanceof PermissionDeniedError) {
|
|
90
|
-
json(res, { success: false, error: e.message, code: "permission_denied" }, 403);
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
json(res, { success: false, error: e.message }, 500);
|
|
94
|
-
}
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (path === "/api/sfmc/db/audit") {
|
|
99
|
-
try {
|
|
100
|
-
const step = {
|
|
101
|
-
op: "audit",
|
|
102
|
-
table: String(body.table),
|
|
103
|
-
rowId: String(body.rowId),
|
|
104
|
-
action: String(body.action),
|
|
105
|
-
...(body.data ? { data: body.data } : {}),
|
|
106
|
-
};
|
|
107
|
-
const result = await deps.txRunner.run({ moduleId, steps: [step] });
|
|
108
|
-
if (!result.ok) {
|
|
109
|
-
json(res, { success: false, error: result.error }, 400);
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
json(res, { success: true });
|
|
113
|
-
}
|
|
114
|
-
catch (e) {
|
|
115
|
-
json(res, { success: false, error: e.message }, 500);
|
|
116
|
-
}
|
|
102
|
+
}
|
|
103
|
+
const first = result.results[0];
|
|
104
|
+
jsonV2Ok(res, unwrapResult(first, cand.op));
|
|
105
|
+
} catch (e) {
|
|
106
|
+
if (e instanceof PermissionDeniedError) {
|
|
107
|
+
jsonV2Fail(res, e.message, 403, "permission_denied");
|
|
117
108
|
return true;
|
|
109
|
+
}
|
|
110
|
+
jsonV2Fail(res, e.message, 500);
|
|
118
111
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (path === "/api/sfmc/db/audit") {
|
|
116
|
+
try {
|
|
117
|
+
const step = {
|
|
118
|
+
op: "audit",
|
|
119
|
+
table: String(body.table),
|
|
120
|
+
rowId: String(body.rowId),
|
|
121
|
+
action: String(body.action),
|
|
122
|
+
...body.data ? { data: body.data } : {}
|
|
123
|
+
};
|
|
124
|
+
const result = await deps.txRunner.run({ moduleId, steps: [step] });
|
|
125
|
+
if (!result.ok) {
|
|
126
|
+
jsonV2Fail(res, result.error, 400, result.code);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
jsonV2Ok(res);
|
|
130
|
+
} catch (e) {
|
|
131
|
+
jsonV2Fail(res, e.message, 500);
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
const m = path.match(/^\/api\/sfmc\/db\/idempotent\/(probe|commit)$/);
|
|
136
|
+
if (m) {
|
|
137
|
+
try {
|
|
138
|
+
if (m[1] === "probe") {
|
|
139
|
+
const r = await deps.idempotent.probe(
|
|
140
|
+
moduleId,
|
|
141
|
+
String(body.action),
|
|
142
|
+
String(body.key)
|
|
143
|
+
);
|
|
144
|
+
json(res, r);
|
|
145
|
+
} else {
|
|
146
|
+
const r = await deps.idempotent.commit(
|
|
147
|
+
moduleId,
|
|
148
|
+
String(body.action),
|
|
149
|
+
String(body.key),
|
|
150
|
+
body.value
|
|
151
|
+
);
|
|
152
|
+
jsonV2Ok(res, { committed: r.ok });
|
|
135
153
|
}
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
} catch (e) {
|
|
155
|
+
jsonV2Fail(res, e.message, 500);
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
};
|
|
138
161
|
}
|
|
139
162
|
function buildSingleStep(op, body) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
163
|
+
switch (op) {
|
|
164
|
+
case "query":
|
|
165
|
+
return {
|
|
166
|
+
op: "query",
|
|
167
|
+
table: String(body.table),
|
|
168
|
+
...body.opts ? { opts: body.opts } : {}
|
|
169
|
+
};
|
|
170
|
+
case "get":
|
|
171
|
+
return { op: "get", table: String(body.table), id: String(body.id) };
|
|
172
|
+
case "insert":
|
|
173
|
+
return {
|
|
174
|
+
op: "insert",
|
|
175
|
+
table: String(body.table),
|
|
176
|
+
row: body.row
|
|
177
|
+
};
|
|
178
|
+
case "update":
|
|
179
|
+
return {
|
|
180
|
+
op: "update",
|
|
181
|
+
table: String(body.table),
|
|
182
|
+
id: String(body.id),
|
|
183
|
+
patch: body.patch
|
|
184
|
+
};
|
|
185
|
+
case "delete":
|
|
186
|
+
return {
|
|
187
|
+
op: "delete",
|
|
188
|
+
table: String(body.table),
|
|
189
|
+
id: String(body.id),
|
|
190
|
+
hard: Boolean(body.hard)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
170
193
|
}
|
|
171
194
|
function unwrapResult(r, op) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
const step = r;
|
|
196
|
+
switch (op) {
|
|
197
|
+
case "query":
|
|
198
|
+
return step.rows ? { rows: step.rows } : {};
|
|
199
|
+
case "get":
|
|
200
|
+
case "insert":
|
|
201
|
+
case "update":
|
|
202
|
+
return step.row !== void 0 ? { row: step.row } : {};
|
|
203
|
+
case "delete":
|
|
204
|
+
return typeof step.changes === "number" ? { changes: step.changes } : {};
|
|
205
|
+
default:
|
|
206
|
+
return {};
|
|
207
|
+
}
|
|
185
208
|
}
|
|
186
|
-
|
|
209
|
+
export {
|
|
210
|
+
createDbRoutes
|
|
211
|
+
};
|
|
212
|
+
//# sourceMappingURL=db-routes.js.map
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/routes/db-routes.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * routes/db-routes.ts \u2014 /api/sfmc/db/* \u5904\u7406\u5668\n *\n * \u7AEF\u70B9:\n * POST /api/sfmc/db/define-table body {name, columns, softDelete?} \u2192 {table, created}\n * POST /api/sfmc/db/tx body {steps[]} \u2192 TxResponse | TxError (\u6279\u91CF)\n * POST /api/sfmc/db/tx/begin body {} \u2192 {txId}\n * POST /api/sfmc/db/tx/step body {txId, step} \u2192 {result}\n * POST /api/sfmc/db/tx/commit body {txId} \u2192 TxResponse\n * POST /api/sfmc/db/tx/rollback body {txId} \u2192 {ok}\n * POST /api/sfmc/db/query body {table, opts?} \u2192 {rows}\n * ...\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { json as defaultJson, type Method } from \"../lib/http.js\";\nimport { jsonV2Fail, jsonV2Ok, type ModuleAuth } from \"./_shared.js\";\nimport type {\n DefineTableRequest,\n SchemaRegistry,\n} from \"../schema-registry.js\";\nimport type {\n TxRequest,\n TxRunner,\n TxStep,\n} from \"../tx-runner.js\";\nimport { PermissionDeniedError } from \"../permission-gate.js\";\nimport type { IdempotencyStore } from \"../lib/idempotency-store.js\";\n\nexport interface DbRoutesDeps {\n schemaRegistry: SchemaRegistry;\n txRunner: TxRunner;\n idempotent: IdempotencyStore;\n json?: typeof defaultJson;\n}\n\nexport function createDbRoutes(depsIn: Partial<DbRoutesDeps>) {\n const deps = depsIn as Partial<DbRoutesDeps>;\n if (!deps.schemaRegistry || !deps.txRunner || !deps.idempotent) {\n throw new Error(\"createDbRoutes: \u7F3A\u5C11 schemaRegistry / txRunner / idempotent\");\n }\n const json = deps.json || defaultJson;\n\n return async (ctx: {\n path: string;\n method: Method | string;\n req: IncomingMessage;\n res: ServerResponse;\n body?: Promise<Record<string, unknown>> | Record<string, unknown>;\n moduleAuth?: ModuleAuth;\n }): Promise<boolean> => {\n const { path, method, res } = ctx;\n if (!path.startsWith(\"/api/sfmc/db/\")) return false;\n if (method !== \"POST\" && method !== \"DELETE\") return false;\n\n const auth = ctx.moduleAuth ?? null;\n if (!auth) {\n jsonV2Fail(res, \"unauthorized: module identity missing\", 401, \"unauthorized\");\n return true;\n }\n\n const body = (await (ctx.body as Promise<Record<string, unknown>> | undefined)) || {};\n const moduleId = auth.id;\n\n if (path === \"/api/sfmc/db/define-table\") {\n try {\n const req2 = body as unknown as DefineTableRequest & { moduleId?: string };\n delete (req2 as { moduleId?: string }).moduleId;\n const result = deps.schemaRegistry!.define(moduleId, req2);\n jsonV2Ok(res, { table: result.table, created: result.created });\n } catch (e) {\n jsonV2Fail(res, (e as Error).message, 400);\n }\n return true;\n }\n\n if (path === \"/api/sfmc/db/tx\") {\n try {\n const txReq = body as unknown as TxRequest;\n // \u5F3A\u5236\u4EE5\u300C\u9274\u6743\u8EAB\u4EFD\u300D\u6267\u884C\u4E8B\u52A1:\u5FFD\u7565 body \u91CC\u81EA\u5E26\u7684 moduleId,\n // \u65E2\u9632\u6B62\u6301 A \u7684 token \u5192\u7528 B \u7684\u6743\u9650\u8D8A\u6743,\u4E5F\u4FEE\u590D\u5BA2\u6237\u7AEF\u53EA\u53D1 {steps} \u65F6\n // moduleId \u7F3A\u5931\u5BFC\u81F4\u7684\u4E8B\u52A1\u6052\u88AB\u62D2\u3002\n const steps = Array.isArray(txReq.steps) ? txReq.steps : [];\n const result = await deps.txRunner!.run({ moduleId, steps });\n const status = result.ok ? 200 : 400;\n // TxResponse/TxError \u81EA\u8EAB\u5DF2\u5E26 ok \u5B57\u6BB5;\u539F\u6837\u56DE\u4F20\u4FDD\u6301\u5951\u7EA6\n json(res, result as unknown as Record<string, unknown>, status);\n } catch (e) {\n jsonV2Fail(res, (e as Error).message, 500);\n }\n return true;\n }\n\n // \u4EA4\u4E92\u5F0F\u4E8B\u52A1\u4F1A\u8BDD \u2014 \u4F9B SDK \u5728\u56DE\u8C03\u5185\u8BFB\u56DE query/get/call \u7ED3\u679C\n // OCP/DRY:\u65B0\u4F1A\u8BDD\u52A8\u8BCD\u53EA\u5F80\u8868\u91CC\u52A0\u4E00\u9879,\u52FF\u518D\u590D\u5236 try/catch \u56DB\u4EFD\n type SessionReply = { ok: boolean } & Record<string, unknown>;\n const sessionOps: Array<{\n path: string;\n requireTxId?: boolean;\n requireStep?: boolean;\n run: (args: { txId: string; step?: TxStep | undefined }) => Promise<SessionReply> | SessionReply;\n }> = [\n {\n path: \"/api/sfmc/db/tx/begin\",\n run: () => deps.txRunner!.beginSession(moduleId) as SessionReply,\n },\n {\n path: \"/api/sfmc/db/tx/step\",\n requireTxId: true,\n requireStep: true,\n run: ({ txId, step }) => deps.txRunner!.stepSession(txId, moduleId, step!) as Promise<SessionReply>,\n },\n {\n path: \"/api/sfmc/db/tx/commit\",\n requireTxId: true,\n run: ({ txId }) => deps.txRunner!.commitSession(txId, moduleId) as SessionReply,\n },\n {\n path: \"/api/sfmc/db/tx/rollback\",\n requireTxId: true,\n run: ({ txId }) => deps.txRunner!.rollbackSession(txId, moduleId) as SessionReply,\n },\n ];\n for (const op of sessionOps) {\n if (path !== op.path) continue;\n try {\n const txId = String(body.txId || \"\");\n const step = body.step as TxStep | undefined;\n if (op.requireTxId && !txId) {\n jsonV2Fail(res, `${op.path.split(\"/\").pop()} \u9700\u8981 { txId }`, 400);\n return true;\n }\n if (op.requireStep && (!step || typeof step !== \"object\" || !(\"op\" in step))) {\n jsonV2Fail(res, \"tx/step \u9700\u8981 { txId, step }\", 400);\n return true;\n }\n const result = await op.run(step !== undefined ? { txId, step } : { txId });\n json(res, result as Record<string, unknown>, result.ok ? 200 : 400);\n } catch (e) {\n jsonV2Fail(res, (e as Error).message, 500);\n }\n return true;\n }\n\n type SingleOp = \"query\" | \"get\" | \"insert\" | \"update\" | \"delete\";\n const singles: { match: RegExp; op: SingleOp }[] = [\n { match: /^\\/api\\/sfmc\\/db\\/query$/, op: \"query\" },\n { match: /^\\/api\\/sfmc\\/db\\/get$/, op: \"get\" },\n { match: /^\\/api\\/sfmc\\/db\\/insert$/, op: \"insert\" },\n { match: /^\\/api\\/sfmc\\/db\\/update$/, op: \"update\" },\n { match: /^\\/api\\/sfmc\\/db\\/delete$/, op: \"delete\" },\n ];\n for (const cand of singles) {\n if (cand.match.test(path)) {\n try {\n const step = buildSingleStep(cand.op, body);\n const result = await deps.txRunner!.run({ moduleId, steps: [step] });\n if (!result.ok) {\n const code = result.code === \"permission_denied\" ? 403 : 400;\n jsonV2Fail(res, result.error, code, result.code, { step: result.step });\n return true;\n }\n const first = result.results[0];\n jsonV2Ok(res, unwrapResult(first, cand.op) as Record<string, unknown>);\n } catch (e) {\n if (e instanceof PermissionDeniedError) {\n jsonV2Fail(res, e.message, 403, \"permission_denied\");\n return true;\n }\n jsonV2Fail(res, (e as Error).message, 500);\n }\n return true;\n }\n }\n\n if (path === \"/api/sfmc/db/audit\") {\n try {\n const step: TxStep = {\n op: \"audit\",\n table: String(body.table),\n rowId: String(body.rowId),\n action: String(body.action),\n ...(body.data ? { data: body.data as Record<string, unknown> } : {}),\n };\n const result = await deps.txRunner!.run({ moduleId, steps: [step] });\n if (!result.ok) {\n jsonV2Fail(res, result.error, 400, result.code);\n return true;\n }\n jsonV2Ok(res);\n } catch (e) {\n jsonV2Fail(res, (e as Error).message, 500);\n }\n return true;\n }\n\n const m = path.match(/^\\/api\\/sfmc\\/db\\/idempotent\\/(probe|commit)$/);\n if (m) {\n try {\n if (m[1] === \"probe\") {\n const r = await deps.idempotent!.probe(\n moduleId,\n String(body.action),\n String(body.key)\n );\n json(res, r as unknown as Record<string, unknown>);\n } else {\n const r = await deps.idempotent!.commit(\n moduleId,\n String(body.action),\n String(body.key),\n body.value\n );\n jsonV2Ok(res, { committed: r.ok });\n }\n } catch (e) {\n jsonV2Fail(res, (e as Error).message, 500);\n }\n return true;\n }\n\n return false;\n };\n}\n\nfunction buildSingleStep(op: \"query\" | \"get\" | \"insert\" | \"update\" | \"delete\", body: Record<string, unknown>): TxStep {\n switch (op) {\n case \"query\":\n return {\n op: \"query\",\n table: String(body.table),\n ...(body.opts ? { opts: body.opts as Parameters<typeof JSON.parse>[0] } : {}),\n } as TxStep;\n case \"get\":\n return { op: \"get\", table: String(body.table), id: String(body.id) };\n case \"insert\":\n return {\n op: \"insert\",\n table: String(body.table),\n row: body.row as Record<string, unknown>,\n };\n case \"update\":\n return {\n op: \"update\",\n table: String(body.table),\n id: String(body.id),\n patch: body.patch as Record<string, unknown>,\n };\n case \"delete\":\n return {\n op: \"delete\",\n table: String(body.table),\n id: String(body.id),\n hard: Boolean(body.hard),\n };\n }\n}\n\ninterface UnwrappedRow {\n rows?: unknown[];\n row?: unknown;\n changes?: number;\n}\n\nfunction unwrapResult(r: unknown, op: string): UnwrappedRow {\n const step = r as { op: string; rows?: unknown[]; row?: unknown; changes?: number };\n switch (op) {\n case \"query\":\n return step.rows ? { rows: step.rows } : {};\n case \"get\":\n case \"insert\":\n case \"update\":\n return step.row !== undefined ? { row: step.row } : {};\n case \"delete\":\n return typeof step.changes === \"number\" ? { changes: step.changes } : {};\n default:\n return {};\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/routes/health.js
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* routes/health.ts — 健康检查
|
|
3
|
-
*
|
|
4
|
-
* 路由列表:
|
|
5
|
-
* GET /api/health — 返回 db-server 的运行状态
|
|
6
|
-
*/
|
|
7
1
|
import { json } from "./_shared.js";
|
|
8
2
|
function createHealthRoutes() {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
3
|
+
return async function handle({ path, method, res }) {
|
|
4
|
+
if (path === "/api/health") {
|
|
5
|
+
if (method === "GET") {
|
|
6
|
+
json(res, { status: "ok", uptime: process.uptime() });
|
|
7
|
+
} else {
|
|
8
|
+
json(res, { success: false, error: "not_found" }, 404);
|
|
9
|
+
}
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
};
|
|
21
14
|
}
|
|
22
|
-
export {
|
|
23
|
-
|
|
15
|
+
export {
|
|
16
|
+
createHealthRoutes
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/routes/health.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * routes/health.ts \u2014 \u5065\u5EB7\u68C0\u67E5\n *\n * \u8DEF\u7531\u5217\u8868\uFF1A\n * GET /api/health \u2014 \u8FD4\u56DE db-server \u7684\u8FD0\u884C\u72B6\u6001\n */\n\nimport { json, type RouteFactory } from \"./_shared.js\";\n\nfunction createHealthRoutes(): ReturnType<RouteFactory> {\n return async function handle({ path, method, res }): Promise<boolean> {\n if (path === \"/api/health\") {\n if (method === \"GET\") {\n json(res, { status: \"ok\", uptime: process.uptime() });\n } else {\n json(res, { success: false, error: \"not_found\" }, 404);\n }\n return true;\n }\n return false;\n };\n}\n\nexport { createHealthRoutes };\n"],
|
|
5
|
+
"mappings": "AAOA,SAAS,YAA+B;AAExC,SAAS,qBAA+C;AACtD,SAAO,eAAe,OAAO,EAAE,MAAM,QAAQ,IAAI,GAAqB;AACpE,QAAI,SAAS,eAAe;AAC1B,UAAI,WAAW,OAAO;AACpB,aAAK,KAAK,EAAE,QAAQ,MAAM,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAAA,MACtD,OAAO;AACL,aAAK,KAAK,EAAE,SAAS,OAAO,OAAO,YAAY,GAAG,GAAG;AAAA,MACvD;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/routes/messages.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,UAAU,IAAI;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChF,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnG,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACnG;AAED,iBAAS,oBAAoB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/routes/messages.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,UAAU,IAAI;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChF,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnG,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACnG;AAED,iBAAS,oBAAoB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,IAAI,yCAOvE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,eAAe,CAAC;IACxB,GAAG,EAAE,OAAO,MAAM,EAAE,eAAe,CAAC;IACpC,GAAG,EAAE,OAAO,MAAM,EAAE,cAAc,CAAC;CACpC,KAAG,OAAO,CAAC,OAAO,CAAC,CAqDrB;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
package/dist/routes/messages.js
CHANGED
|
@@ -1,72 +1,65 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* routes/messages.ts — 聊天消息 (sfmc_chat_messages)
|
|
3
|
-
*
|
|
4
|
-
* 路由列表:
|
|
5
|
-
* GET /api/sfmc/messages — 模糊/过滤查询消息
|
|
6
|
-
* POST /api/sfmc/messages — 批量 INSERT OR REPLACE 消息(同时转发至 QQ Bridge)
|
|
7
|
-
*/
|
|
8
1
|
import { SQL } from "sql-template-strings";
|
|
9
2
|
function createMessagesRoutes({ query, body, json, forwardToQQBridge }) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
for (const m of messages) {
|
|
50
|
-
query(SQL `INSERT OR REPLACE INTO sfmc_chat_messages (
|
|
3
|
+
return async function handle({
|
|
4
|
+
path,
|
|
5
|
+
method,
|
|
6
|
+
params,
|
|
7
|
+
req,
|
|
8
|
+
res
|
|
9
|
+
}) {
|
|
10
|
+
if (path === "/api/sfmc/messages") {
|
|
11
|
+
if (method === "GET") {
|
|
12
|
+
const stmt = SQL`SELECT * FROM sfmc_chat_messages WHERE 1=1`;
|
|
13
|
+
const search = params.get("search")?.trim();
|
|
14
|
+
if (search) stmt.append(SQL` AND (content LIKE ${`%${search}%`})`);
|
|
15
|
+
const type = params.get("type")?.trim();
|
|
16
|
+
if (type) stmt.append(SQL` AND type = ${type}`);
|
|
17
|
+
const channelId = params.get("channelId")?.trim();
|
|
18
|
+
if (channelId) stmt.append(SQL` AND channel_id = ${channelId}`);
|
|
19
|
+
const from = params.get("from")?.trim();
|
|
20
|
+
if (from) stmt.append(SQL` AND from_id = ${from}`);
|
|
21
|
+
const minCreatedAt = params.get("minCreatedAt")?.trim();
|
|
22
|
+
if (minCreatedAt) stmt.append(SQL` AND created_at >= ${Number(minCreatedAt)}`);
|
|
23
|
+
const minSentAt = params.get("minSentAt")?.trim();
|
|
24
|
+
if (minSentAt) stmt.append(SQL` AND created_at >= ${Number(minSentAt)}`);
|
|
25
|
+
const maxCreatedAt = params.get("maxCreatedAt")?.trim();
|
|
26
|
+
if (maxCreatedAt) stmt.append(SQL` AND created_at <= ${Number(maxCreatedAt)}`);
|
|
27
|
+
stmt.append(SQL` ORDER BY created_at ASC`);
|
|
28
|
+
json(res, { messages: query(stmt) });
|
|
29
|
+
} else if (method === "POST") {
|
|
30
|
+
const { messages } = await body(req);
|
|
31
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
32
|
+
json(res, { success: false, error: "invalid" }, 400);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (messages.length > 100) {
|
|
36
|
+
json(res, { success: false, error: "too many requests" }, 413);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
for (const m of messages) {
|
|
40
|
+
query(
|
|
41
|
+
SQL`INSERT OR REPLACE INTO sfmc_chat_messages (
|
|
51
42
|
id, channel_id, from_id, from_name, type, content, attachment, show_timestamp, created_at
|
|
52
43
|
) VALUES (
|
|
53
44
|
${m.id}, ${m.channelId}, ${m.fromid}, ${m.fromName},
|
|
54
45
|
${m.type ?? "text"}, ${m.content}, ${m.attachment ?? null},
|
|
55
46
|
${m.showTimestamp ? 1 : 0}, ${m.timestamp}
|
|
56
|
-
)`
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
json(res, { success: true });
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
json(res, { success: false, error: "not_found" }, 404);
|
|
65
|
-
}
|
|
66
|
-
return true;
|
|
47
|
+
)`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
for (const m of messages) {
|
|
51
|
+
forwardToQQBridge(m.channelId, m.fromName, m.content, m.fromid);
|
|
67
52
|
}
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
json(res, { success: true });
|
|
54
|
+
} else {
|
|
55
|
+
json(res, { success: false, error: "not_found" }, 404);
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
};
|
|
70
61
|
}
|
|
71
|
-
export {
|
|
72
|
-
|
|
62
|
+
export {
|
|
63
|
+
createMessagesRoutes
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=messages.js.map
|