@sfmc-bds/db-server 0.2.0-beta.5 → 0.2.0-beta.6
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/LICENSE +663 -0
- package/dist/domain/bridge.d.ts +57 -23
- package/dist/domain/bridge.d.ts.map +1 -1
- package/dist/domain/bridge.js +77 -6
- package/dist/domain/bridge.js.map +2 -2
- package/dist/domain/bridge.test.d.ts +2 -0
- package/dist/domain/bridge.test.d.ts.map +1 -0
- package/dist/domain/bridge.test.js +87 -0
- package/dist/domain/bridge.test.js.map +7 -0
- package/dist/domain/economy.d.ts +38 -7
- package/dist/domain/economy.d.ts.map +1 -1
- package/dist/domain/economy.js.map +2 -2
- package/dist/domain/messages-bridge-filter.test.d.ts +5 -0
- package/dist/domain/messages-bridge-filter.test.d.ts.map +1 -0
- package/dist/domain/messages-bridge-filter.test.js +20 -0
- package/dist/domain/messages-bridge-filter.test.js.map +7 -0
- package/dist/domain/qq-bind-status.test.d.ts +2 -0
- package/dist/domain/qq-bind-status.test.d.ts.map +1 -0
- package/dist/domain/qq-bind-status.test.js +211 -0
- package/dist/domain/qq-bind-status.test.js.map +7 -0
- package/dist/domain/qq-events.d.ts +84 -0
- package/dist/domain/qq-events.d.ts.map +1 -0
- package/dist/domain/qq-events.js +193 -0
- package/dist/domain/qq-events.js.map +7 -0
- package/dist/domain/qq-events.test.d.ts +5 -0
- package/dist/domain/qq-events.test.d.ts.map +1 -0
- package/dist/domain/qq-events.test.js +170 -0
- package/dist/domain/qq-events.test.js.map +7 -0
- package/dist/domain/qq-join.test.d.ts +2 -0
- package/dist/domain/qq-join.test.d.ts.map +1 -0
- package/dist/domain/qq-join.test.js +353 -0
- package/dist/domain/qq-join.test.js.map +7 -0
- package/dist/domain/schema.d.ts +17 -19
- package/dist/domain/schema.d.ts.map +1 -1
- package/dist/domain/schema.js +69 -0
- package/dist/domain/schema.js.map +2 -2
- package/dist/domain/system-status.d.ts +75 -0
- package/dist/domain/system-status.d.ts.map +1 -0
- package/dist/domain/system-status.js +181 -0
- package/dist/domain/system-status.js.map +7 -0
- package/dist/domain/system-status.test.d.ts +5 -0
- package/dist/domain/system-status.test.d.ts.map +1 -0
- package/dist/domain/system-status.test.js +19 -0
- package/dist/domain/system-status.test.js.map +7 -0
- package/dist/domain/transaction.d.ts +5 -4
- package/dist/domain/transaction.d.ts.map +1 -1
- package/dist/domain/transaction.js.map +2 -2
- package/dist/env.d.ts +7 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +55 -3
- package/dist/env.js.map +2 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -19
- package/dist/index.js.map +2 -2
- package/dist/lib/idempotency-store.d.ts +17 -11
- package/dist/lib/idempotency-store.d.ts.map +1 -1
- package/dist/lib/idempotency-store.js.map +2 -2
- package/dist/lib/idempotency.d.ts +3 -4
- package/dist/lib/idempotency.d.ts.map +1 -1
- package/dist/lib/idempotency.js.map +2 -2
- package/dist/lib/module-state.d.ts +38 -8
- package/dist/lib/module-state.d.ts.map +1 -1
- package/dist/lib/module-state.js.map +2 -2
- package/dist/lib/order-by.d.ts +8 -6
- package/dist/lib/order-by.d.ts.map +1 -1
- package/dist/lib/order-by.js.map +2 -2
- package/dist/lib/sql-helpers.d.ts +25 -13
- package/dist/lib/sql-helpers.d.ts.map +1 -1
- package/dist/lib/sql-helpers.js.map +2 -2
- package/dist/lib/sqlite.d.ts +14 -5
- package/dist/lib/sqlite.d.ts.map +1 -1
- package/dist/lib/sqlite.js.map +2 -2
- package/dist/manifest-loader.d.ts +5 -2
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +16 -6
- package/dist/manifest-loader.js.map +2 -2
- package/dist/manifest.d.ts +1 -2
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +13 -8
- package/dist/manifest.js.map +2 -2
- package/dist/project-root.d.ts +1 -1
- package/dist/project-root.d.ts.map +1 -1
- package/dist/project-root.js +2 -2
- package/dist/project-root.js.map +2 -2
- package/dist/routes/_shared.d.ts +14 -5
- package/dist/routes/_shared.d.ts.map +1 -1
- package/dist/routes/_shared.js.map +2 -2
- package/dist/routes/config.d.ts +8 -11
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +17 -23
- package/dist/routes/config.js.map +2 -2
- package/dist/routes/db-routes.d.ts +6 -0
- package/dist/routes/db-routes.d.ts.map +1 -1
- package/dist/routes/db-routes.js.map +2 -2
- package/dist/routes/messages.d.ts +6 -2
- package/dist/routes/messages.d.ts.map +1 -1
- package/dist/routes/messages.js +17 -3
- package/dist/routes/messages.js.map +2 -2
- package/dist/routes/module-config-routes.d.ts +13 -9
- package/dist/routes/module-config-routes.d.ts.map +1 -1
- package/dist/routes/module-config-routes.js.map +2 -2
- package/dist/routes/modules.d.ts +6 -0
- package/dist/routes/modules.d.ts.map +1 -1
- package/dist/routes/modules.js.map +2 -2
- package/dist/routes/qq-bind.d.ts +24 -0
- package/dist/routes/qq-bind.d.ts.map +1 -0
- package/dist/routes/qq-bind.js +179 -0
- package/dist/routes/qq-bind.js.map +7 -0
- package/dist/routes/qq-events.d.ts +20 -0
- package/dist/routes/qq-events.d.ts.map +1 -0
- package/dist/routes/qq-events.js +39 -0
- package/dist/routes/qq-events.js.map +7 -0
- package/dist/routes/qq-join.d.ts +46 -0
- package/dist/routes/qq-join.d.ts.map +1 -0
- package/dist/routes/qq-join.js +277 -0
- package/dist/routes/qq-join.js.map +7 -0
- package/dist/routes/service-routes.d.ts +6 -0
- package/dist/routes/service-routes.d.ts.map +1 -1
- package/dist/routes/service-routes.js.map +2 -2
- package/dist/routes/status.d.ts +19 -0
- package/dist/routes/status.d.ts.map +1 -0
- package/dist/routes/status.js +74 -0
- package/dist/routes/status.js.map +7 -0
- package/dist/schema-registry.d.ts +16 -15
- package/dist/schema-registry.d.ts.map +1 -1
- package/dist/schema-registry.js +7 -1
- package/dist/schema-registry.js.map +2 -2
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js.map +2 -2
- package/dist/service-registry.d.ts +39 -9
- package/dist/service-registry.d.ts.map +1 -1
- package/dist/service-registry.js +30 -2
- package/dist/service-registry.js.map +2 -2
- package/dist/services/builtin-handlers.d.ts +27 -7
- package/dist/services/builtin-handlers.d.ts.map +1 -1
- package/dist/services/builtin-handlers.js.map +2 -2
- package/dist/tx-runner.d.ts +29 -3
- package/dist/tx-runner.d.ts.map +1 -1
- package/dist/tx-runner.js +29 -3
- package/dist/tx-runner.js.map +2 -2
- package/package.json +17 -15
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { DatabaseSync } from "node:sqlite";
|
|
4
|
+
import { initSchema } from "./schema.js";
|
|
5
|
+
import { createQuery } from "../lib/sqlite.js";
|
|
6
|
+
import { createQqJoinRoutes } from "../routes/qq-join.js";
|
|
7
|
+
function memDb() {
|
|
8
|
+
const db = new DatabaseSync(":memory:");
|
|
9
|
+
initSchema(db);
|
|
10
|
+
return { db, query: createQuery(db) };
|
|
11
|
+
}
|
|
12
|
+
function mockRes() {
|
|
13
|
+
let status = 200;
|
|
14
|
+
let body = {};
|
|
15
|
+
return {
|
|
16
|
+
res: {
|
|
17
|
+
writeHead(code) {
|
|
18
|
+
status = code;
|
|
19
|
+
},
|
|
20
|
+
end(s) {
|
|
21
|
+
body = JSON.parse(s);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
get status() {
|
|
25
|
+
return status;
|
|
26
|
+
},
|
|
27
|
+
get body() {
|
|
28
|
+
return body;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function makeHandle(admins = ["admin1"], flags = {}) {
|
|
33
|
+
const { query } = memDb();
|
|
34
|
+
let bodyData = {};
|
|
35
|
+
let joinFlags = {
|
|
36
|
+
allowlistEnabled: flags.allowlistEnabled !== false,
|
|
37
|
+
requireApproval: flags.requireApproval !== false,
|
|
38
|
+
treatGroupAdminsAsAdmins: flags.treatGroupAdminsAsAdmins === true
|
|
39
|
+
};
|
|
40
|
+
const handle = createQqJoinRoutes({
|
|
41
|
+
query,
|
|
42
|
+
body: async () => bodyData,
|
|
43
|
+
json: (res, data, status = 200) => {
|
|
44
|
+
res.writeHead(status);
|
|
45
|
+
res.end(JSON.stringify(data));
|
|
46
|
+
},
|
|
47
|
+
getAdminOpenids: () => admins,
|
|
48
|
+
getJoinFlags: () => ({ ...joinFlags }),
|
|
49
|
+
setJoinFlags: (partial) => {
|
|
50
|
+
joinFlags = { ...joinFlags, ...partial };
|
|
51
|
+
return { ...joinFlags };
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
query,
|
|
56
|
+
setBody(d) {
|
|
57
|
+
bodyData = d;
|
|
58
|
+
},
|
|
59
|
+
handle,
|
|
60
|
+
getFlags: () => ({ ...joinFlags })
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
test("initSchema \u542B join / admin_actions \u8868", () => {
|
|
64
|
+
const { db } = memDb();
|
|
65
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'sfmc_qq_%' ORDER BY name").all();
|
|
66
|
+
assert.ok(tables.some((t) => t.name === "sfmc_qq_join_requests"));
|
|
67
|
+
assert.ok(tables.some((t) => t.name === "sfmc_qq_admin_actions"));
|
|
68
|
+
});
|
|
69
|
+
test("join request \u2192 decide approve \u2192 apply-queue \u2192 applied", async () => {
|
|
70
|
+
const { setBody, handle } = makeHandle(["admin1"]);
|
|
71
|
+
setBody({ openid: "user1", player_name: "Steve", qq_backend: "official" });
|
|
72
|
+
let m = mockRes();
|
|
73
|
+
await handle({
|
|
74
|
+
path: "/api/sfmc/qq/join/request",
|
|
75
|
+
method: "POST",
|
|
76
|
+
params: new URLSearchParams(),
|
|
77
|
+
req: {},
|
|
78
|
+
res: m.res
|
|
79
|
+
});
|
|
80
|
+
assert.equal(m.body.success, true);
|
|
81
|
+
const id = String(m.body.id);
|
|
82
|
+
assert.match(id, /^join_/);
|
|
83
|
+
setBody({ id, decision: "approve", decided_by: "stranger" });
|
|
84
|
+
m = mockRes();
|
|
85
|
+
await handle({
|
|
86
|
+
path: "/api/sfmc/qq/join/decide",
|
|
87
|
+
method: "POST",
|
|
88
|
+
params: new URLSearchParams(),
|
|
89
|
+
req: {},
|
|
90
|
+
res: m.res
|
|
91
|
+
});
|
|
92
|
+
assert.equal(m.status, 403);
|
|
93
|
+
assert.equal(m.body.error, "not_admin");
|
|
94
|
+
setBody({ id, decision: "approve", decided_by: "admin1" });
|
|
95
|
+
m = mockRes();
|
|
96
|
+
await handle({
|
|
97
|
+
path: "/api/sfmc/qq/join/decide",
|
|
98
|
+
method: "POST",
|
|
99
|
+
params: new URLSearchParams(),
|
|
100
|
+
req: {},
|
|
101
|
+
res: m.res
|
|
102
|
+
});
|
|
103
|
+
assert.equal(m.body.success, true);
|
|
104
|
+
assert.equal(m.body.status, "approved");
|
|
105
|
+
m = mockRes();
|
|
106
|
+
await handle({
|
|
107
|
+
path: "/api/sfmc/qq/join/apply-queue",
|
|
108
|
+
method: "GET",
|
|
109
|
+
params: new URLSearchParams(),
|
|
110
|
+
req: {},
|
|
111
|
+
res: m.res
|
|
112
|
+
});
|
|
113
|
+
assert.equal(m.body.success, true);
|
|
114
|
+
const queue = m.body.queue;
|
|
115
|
+
assert.equal(queue.length, 1);
|
|
116
|
+
assert.equal(queue[0].player_name, "Steve");
|
|
117
|
+
setBody({ id, ok: true });
|
|
118
|
+
m = mockRes();
|
|
119
|
+
await handle({
|
|
120
|
+
path: "/api/sfmc/qq/join/applied",
|
|
121
|
+
method: "POST",
|
|
122
|
+
params: new URLSearchParams(),
|
|
123
|
+
req: {},
|
|
124
|
+
res: m.res
|
|
125
|
+
});
|
|
126
|
+
assert.equal(m.body.success, true);
|
|
127
|
+
m = mockRes();
|
|
128
|
+
await handle({
|
|
129
|
+
path: "/api/sfmc/qq/join/apply-queue",
|
|
130
|
+
method: "GET",
|
|
131
|
+
params: new URLSearchParams(),
|
|
132
|
+
req: {},
|
|
133
|
+
res: m.res
|
|
134
|
+
});
|
|
135
|
+
assert.equal(m.body.queue.length, 0);
|
|
136
|
+
});
|
|
137
|
+
test("join reject \u4E0D\u8FDB apply-queue", async () => {
|
|
138
|
+
const { setBody, handle } = makeHandle(["admin1"]);
|
|
139
|
+
setBody({ openid: "u2", player_name: "Alex" });
|
|
140
|
+
let m = mockRes();
|
|
141
|
+
await handle({
|
|
142
|
+
path: "/api/sfmc/qq/join/request",
|
|
143
|
+
method: "POST",
|
|
144
|
+
params: new URLSearchParams(),
|
|
145
|
+
req: {},
|
|
146
|
+
res: m.res
|
|
147
|
+
});
|
|
148
|
+
const id = String(m.body.id);
|
|
149
|
+
setBody({ id, decision: "reject", decided_by: "admin1" });
|
|
150
|
+
m = mockRes();
|
|
151
|
+
await handle({
|
|
152
|
+
path: "/api/sfmc/qq/join/decide",
|
|
153
|
+
method: "POST",
|
|
154
|
+
params: new URLSearchParams(),
|
|
155
|
+
req: {},
|
|
156
|
+
res: m.res
|
|
157
|
+
});
|
|
158
|
+
assert.equal(m.body.status, "rejected");
|
|
159
|
+
m = mockRes();
|
|
160
|
+
await handle({
|
|
161
|
+
path: "/api/sfmc/qq/join/apply-queue",
|
|
162
|
+
method: "GET",
|
|
163
|
+
params: new URLSearchParams(),
|
|
164
|
+
req: {},
|
|
165
|
+
res: m.res
|
|
166
|
+
});
|
|
167
|
+
assert.equal(m.body.queue.length, 0);
|
|
168
|
+
});
|
|
169
|
+
test("kick \u2192 action-queue \u2192 action-done", async () => {
|
|
170
|
+
const { setBody, handle } = makeHandle(["admin1"]);
|
|
171
|
+
setBody({ openid: "admin1", target_name: "Steve", reason: "test" });
|
|
172
|
+
let m = mockRes();
|
|
173
|
+
await handle({
|
|
174
|
+
path: "/api/sfmc/qq/admin/kick",
|
|
175
|
+
method: "POST",
|
|
176
|
+
params: new URLSearchParams(),
|
|
177
|
+
req: {},
|
|
178
|
+
res: m.res
|
|
179
|
+
});
|
|
180
|
+
assert.equal(m.body.success, true);
|
|
181
|
+
const id = String(m.body.id);
|
|
182
|
+
m = mockRes();
|
|
183
|
+
await handle({
|
|
184
|
+
path: "/api/sfmc/qq/admin/action-queue",
|
|
185
|
+
method: "GET",
|
|
186
|
+
params: new URLSearchParams(),
|
|
187
|
+
req: {},
|
|
188
|
+
res: m.res
|
|
189
|
+
});
|
|
190
|
+
const queue = m.body.queue;
|
|
191
|
+
assert.equal(queue.length, 1);
|
|
192
|
+
assert.equal(queue[0].kind, "kick");
|
|
193
|
+
assert.equal(queue[0].target_name, "Steve");
|
|
194
|
+
setBody({ id, ok: true });
|
|
195
|
+
m = mockRes();
|
|
196
|
+
await handle({
|
|
197
|
+
path: "/api/sfmc/qq/admin/action-done",
|
|
198
|
+
method: "POST",
|
|
199
|
+
params: new URLSearchParams(),
|
|
200
|
+
req: {},
|
|
201
|
+
res: m.res
|
|
202
|
+
});
|
|
203
|
+
assert.equal(m.body.success, true);
|
|
204
|
+
m = mockRes();
|
|
205
|
+
await handle({
|
|
206
|
+
path: "/api/sfmc/qq/admin/action-queue",
|
|
207
|
+
method: "GET",
|
|
208
|
+
params: new URLSearchParams(),
|
|
209
|
+
req: {},
|
|
210
|
+
res: m.res
|
|
211
|
+
});
|
|
212
|
+
assert.equal(m.body.queue.length, 0);
|
|
213
|
+
});
|
|
214
|
+
test("\u767D\u540D\u5355\u5173\u95ED\u65F6\u62D2\u7EDD\u7533\u8BF7\u4E14 apply-queue \u7A7A", async () => {
|
|
215
|
+
const { setBody, handle } = makeHandle(["admin1"], { allowlistEnabled: false });
|
|
216
|
+
setBody({ openid: "u", player_name: "Steve" });
|
|
217
|
+
let m = mockRes();
|
|
218
|
+
await handle({
|
|
219
|
+
path: "/api/sfmc/qq/join/request",
|
|
220
|
+
method: "POST",
|
|
221
|
+
params: new URLSearchParams(),
|
|
222
|
+
req: {},
|
|
223
|
+
res: m.res
|
|
224
|
+
});
|
|
225
|
+
assert.equal(m.status, 403);
|
|
226
|
+
assert.equal(m.body.error, "join_allowlist_disabled");
|
|
227
|
+
m = mockRes();
|
|
228
|
+
await handle({
|
|
229
|
+
path: "/api/sfmc/qq/join/apply-queue",
|
|
230
|
+
method: "GET",
|
|
231
|
+
params: new URLSearchParams(),
|
|
232
|
+
req: {},
|
|
233
|
+
res: m.res
|
|
234
|
+
});
|
|
235
|
+
assert.equal(m.body.queue.length, 0);
|
|
236
|
+
});
|
|
237
|
+
test("\u5173\u95ED\u5BA1\u6279\u65F6\u7533\u8BF7\u76F4\u63A5 approved", async () => {
|
|
238
|
+
const { setBody, handle } = makeHandle(["admin1"], { requireApproval: false });
|
|
239
|
+
setBody({ openid: "u", player_name: "Alex" });
|
|
240
|
+
let m = mockRes();
|
|
241
|
+
await handle({
|
|
242
|
+
path: "/api/sfmc/qq/join/request",
|
|
243
|
+
method: "POST",
|
|
244
|
+
params: new URLSearchParams(),
|
|
245
|
+
req: {},
|
|
246
|
+
res: m.res
|
|
247
|
+
});
|
|
248
|
+
assert.equal(m.body.success, true);
|
|
249
|
+
assert.equal(m.body.status, "approved");
|
|
250
|
+
assert.equal(m.body.auto_approved, true);
|
|
251
|
+
m = mockRes();
|
|
252
|
+
await handle({
|
|
253
|
+
path: "/api/sfmc/qq/join/apply-queue",
|
|
254
|
+
method: "GET",
|
|
255
|
+
params: new URLSearchParams(),
|
|
256
|
+
req: {},
|
|
257
|
+
res: m.res
|
|
258
|
+
});
|
|
259
|
+
assert.equal(m.body.queue.length, 1);
|
|
260
|
+
});
|
|
261
|
+
test("\u7BA1\u7406\u5458\u53EF\u6539 settings", async () => {
|
|
262
|
+
const { setBody, handle, getFlags } = makeHandle(["admin1"]);
|
|
263
|
+
let m = mockRes();
|
|
264
|
+
await handle({
|
|
265
|
+
path: "/api/sfmc/qq/join/settings",
|
|
266
|
+
method: "GET",
|
|
267
|
+
params: new URLSearchParams(),
|
|
268
|
+
req: {},
|
|
269
|
+
res: m.res
|
|
270
|
+
});
|
|
271
|
+
assert.equal(m.body.allowlist_enabled, true);
|
|
272
|
+
assert.equal(m.body.require_approval, true);
|
|
273
|
+
assert.equal(m.body.treat_group_admins_as_admins, false);
|
|
274
|
+
setBody({ openid: "stranger", require_approval: false });
|
|
275
|
+
m = mockRes();
|
|
276
|
+
await handle({
|
|
277
|
+
path: "/api/sfmc/qq/join/settings",
|
|
278
|
+
method: "POST",
|
|
279
|
+
params: new URLSearchParams(),
|
|
280
|
+
req: {},
|
|
281
|
+
res: m.res
|
|
282
|
+
});
|
|
283
|
+
assert.equal(m.status, 403);
|
|
284
|
+
setBody({ openid: "admin1", require_approval: false, allowlist_enabled: true });
|
|
285
|
+
m = mockRes();
|
|
286
|
+
await handle({
|
|
287
|
+
path: "/api/sfmc/qq/join/settings",
|
|
288
|
+
method: "POST",
|
|
289
|
+
params: new URLSearchParams(),
|
|
290
|
+
req: {},
|
|
291
|
+
res: m.res
|
|
292
|
+
});
|
|
293
|
+
assert.equal(m.body.success, true);
|
|
294
|
+
assert.equal(m.body.require_approval, false);
|
|
295
|
+
assert.equal(getFlags().requireApproval, false);
|
|
296
|
+
});
|
|
297
|
+
test("treat_group_admins_as_admins \u53EA\u8BFB\uFF1APOST \u6539\u5199\u88AB\u62D2\uFF1B\u7FA4\u7BA1\u53EF\u9274\u6743", async () => {
|
|
298
|
+
const { setBody, handle, getFlags } = makeHandle([], { treatGroupAdminsAsAdmins: true });
|
|
299
|
+
let m = mockRes();
|
|
300
|
+
await handle({
|
|
301
|
+
path: "/api/sfmc/qq/join/settings",
|
|
302
|
+
method: "GET",
|
|
303
|
+
params: new URLSearchParams(),
|
|
304
|
+
req: {},
|
|
305
|
+
res: m.res
|
|
306
|
+
});
|
|
307
|
+
assert.equal(m.body.treat_group_admins_as_admins, true);
|
|
308
|
+
setBody({ openid: "gadmin", treat_group_admins_as_admins: false, as_group_admin: true });
|
|
309
|
+
m = mockRes();
|
|
310
|
+
await handle({
|
|
311
|
+
path: "/api/sfmc/qq/join/settings",
|
|
312
|
+
method: "POST",
|
|
313
|
+
params: new URLSearchParams(),
|
|
314
|
+
req: {},
|
|
315
|
+
res: m.res
|
|
316
|
+
});
|
|
317
|
+
assert.equal(m.status, 400);
|
|
318
|
+
assert.equal(m.body.error, "immutable_field");
|
|
319
|
+
assert.equal(getFlags().treatGroupAdminsAsAdmins, true);
|
|
320
|
+
setBody({ openid: "gadmin", player_name: "Alex", qq_backend: "official" });
|
|
321
|
+
m = mockRes();
|
|
322
|
+
await handle({
|
|
323
|
+
path: "/api/sfmc/qq/join/request",
|
|
324
|
+
method: "POST",
|
|
325
|
+
params: new URLSearchParams(),
|
|
326
|
+
req: {},
|
|
327
|
+
res: m.res
|
|
328
|
+
});
|
|
329
|
+
const id = String(m.body.id);
|
|
330
|
+
setBody({ id, decision: "approve", decided_by: "gadmin", as_group_admin: true });
|
|
331
|
+
m = mockRes();
|
|
332
|
+
await handle({
|
|
333
|
+
path: "/api/sfmc/qq/join/decide",
|
|
334
|
+
method: "POST",
|
|
335
|
+
params: new URLSearchParams(),
|
|
336
|
+
req: {},
|
|
337
|
+
res: m.res
|
|
338
|
+
});
|
|
339
|
+
assert.equal(m.body.success, true);
|
|
340
|
+
assert.equal(m.body.status, "approved");
|
|
341
|
+
const denied = makeHandle([], { treatGroupAdminsAsAdmins: false });
|
|
342
|
+
denied.setBody({ id: "x", decision: "approve", decided_by: "gadmin", as_group_admin: true });
|
|
343
|
+
m = mockRes();
|
|
344
|
+
await denied.handle({
|
|
345
|
+
path: "/api/sfmc/qq/join/decide",
|
|
346
|
+
method: "POST",
|
|
347
|
+
params: new URLSearchParams(),
|
|
348
|
+
req: {},
|
|
349
|
+
res: m.res
|
|
350
|
+
});
|
|
351
|
+
assert.equal(m.status, 403);
|
|
352
|
+
});
|
|
353
|
+
//# sourceMappingURL=qq-join.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/domain/qq-join.test.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * domain/qq-join.test.ts \u2014 \u5165\u670D\u5BA1\u6279\u72B6\u6001\u673A + \u8E22\u4EBA\u961F\u5217\n */\nimport assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { initSchema } from \"./schema.js\";\nimport { createQuery } from \"../lib/sqlite.js\";\nimport { createQqJoinRoutes } from \"../routes/qq-join.js\";\n\nfunction memDb() {\n const db = new DatabaseSync(\":memory:\");\n initSchema(db);\n return { db, query: createQuery(db) };\n}\n\nfunction mockRes() {\n let status = 200;\n let body: Record<string, unknown> = {};\n return {\n res: {\n writeHead(code: number) {\n status = code;\n },\n end(s: string) {\n body = JSON.parse(s) as Record<string, unknown>;\n },\n } as unknown as import(\"http\").ServerResponse,\n get status() {\n return status;\n },\n get body() {\n return body;\n },\n };\n}\n\nfunction makeHandle(\n admins: string[] = [\"admin1\"],\n flags: {\n allowlistEnabled?: boolean;\n requireApproval?: boolean;\n treatGroupAdminsAsAdmins?: boolean;\n } = {}\n) {\n const { query } = memDb();\n let bodyData: Record<string, unknown> = {};\n let joinFlags = {\n allowlistEnabled: flags.allowlistEnabled !== false,\n requireApproval: flags.requireApproval !== false,\n treatGroupAdminsAsAdmins: flags.treatGroupAdminsAsAdmins === true,\n };\n const handle = createQqJoinRoutes({\n query,\n body: async () => bodyData,\n json: (res, data, status = 200) => {\n (res as unknown as { writeHead: (c: number) => void }).writeHead(status);\n (res as unknown as { end: (s: string) => void }).end(JSON.stringify(data));\n },\n getAdminOpenids: () => admins,\n getJoinFlags: () => ({ ...joinFlags }),\n setJoinFlags: (partial) => {\n joinFlags = { ...joinFlags, ...partial };\n return { ...joinFlags };\n },\n });\n return {\n query,\n setBody(d: Record<string, unknown>) {\n bodyData = d;\n },\n handle,\n getFlags: () => ({ ...joinFlags }),\n };\n}\n\ntest(\"initSchema \u542B join / admin_actions \u8868\", () => {\n const { db } = memDb();\n const tables = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'sfmc_qq_%' ORDER BY name\")\n .all() as Array<{ name: string }>;\n assert.ok(tables.some((t) => t.name === \"sfmc_qq_join_requests\"));\n assert.ok(tables.some((t) => t.name === \"sfmc_qq_admin_actions\"));\n});\n\ntest(\"join request \u2192 decide approve \u2192 apply-queue \u2192 applied\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"]);\n\n setBody({ openid: \"user1\", player_name: \"Steve\", qq_backend: \"official\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n const id = String(m.body.id);\n assert.match(id, /^join_/);\n\n setBody({ id, decision: \"approve\", decided_by: \"stranger\" });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 403);\n assert.equal(m.body.error, \"not_admin\");\n\n setBody({ id, decision: \"approve\", decided_by: \"admin1\" });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n assert.equal(m.body.status, \"approved\");\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n const queue = m.body.queue as Array<{ id: string; player_name: string }>;\n assert.equal(queue.length, 1);\n assert.equal(queue[0]!.player_name, \"Steve\");\n\n setBody({ id, ok: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/applied\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 0);\n});\n\ntest(\"join reject \u4E0D\u8FDB apply-queue\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"]);\n setBody({ openid: \"u2\", player_name: \"Alex\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n const id = String(m.body.id);\n\n setBody({ id, decision: \"reject\", decided_by: \"admin1\" });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.status, \"rejected\");\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 0);\n});\n\ntest(\"kick \u2192 action-queue \u2192 action-done\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"]);\n\n setBody({ openid: \"admin1\", target_name: \"Steve\", reason: \"test\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/admin/kick\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n const id = String(m.body.id);\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/admin/action-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n const queue = m.body.queue as Array<{ id: string; kind: string; target_name: string }>;\n assert.equal(queue.length, 1);\n assert.equal(queue[0]!.kind, \"kick\");\n assert.equal(queue[0]!.target_name, \"Steve\");\n\n setBody({ id, ok: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/admin/action-done\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/admin/action-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 0);\n});\n\ntest(\"\u767D\u540D\u5355\u5173\u95ED\u65F6\u62D2\u7EDD\u7533\u8BF7\u4E14 apply-queue \u7A7A\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"], { allowlistEnabled: false });\n setBody({ openid: \"u\", player_name: \"Steve\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 403);\n assert.equal(m.body.error, \"join_allowlist_disabled\");\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 0);\n});\n\ntest(\"\u5173\u95ED\u5BA1\u6279\u65F6\u7533\u8BF7\u76F4\u63A5 approved\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"], { requireApproval: false });\n setBody({ openid: \"u\", player_name: \"Alex\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n assert.equal(m.body.status, \"approved\");\n assert.equal(m.body.auto_approved, true);\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 1);\n});\n\ntest(\"\u7BA1\u7406\u5458\u53EF\u6539 settings\", async () => {\n const { setBody, handle, getFlags } = makeHandle([\"admin1\"]);\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.allowlist_enabled, true);\n assert.equal(m.body.require_approval, true);\n assert.equal(m.body.treat_group_admins_as_admins, false);\n\n setBody({ openid: \"stranger\", require_approval: false });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 403);\n\n setBody({ openid: \"admin1\", require_approval: false, allowlist_enabled: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n assert.equal(m.body.require_approval, false);\n assert.equal(getFlags().requireApproval, false);\n});\n\ntest(\"treat_group_admins_as_admins \u53EA\u8BFB\uFF1APOST \u6539\u5199\u88AB\u62D2\uFF1B\u7FA4\u7BA1\u53EF\u9274\u6743\", async () => {\n const { setBody, handle, getFlags } = makeHandle([], { treatGroupAdminsAsAdmins: true });\n\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.treat_group_admins_as_admins, true);\n\n setBody({ openid: \"gadmin\", treat_group_admins_as_admins: false, as_group_admin: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 400);\n assert.equal(m.body.error, \"immutable_field\");\n assert.equal(getFlags().treatGroupAdminsAsAdmins, true);\n\n setBody({ openid: \"gadmin\", player_name: \"Alex\", qq_backend: \"official\" });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n const id = String(m.body.id);\n\n setBody({ id, decision: \"approve\", decided_by: \"gadmin\", as_group_admin: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n assert.equal(m.body.status, \"approved\");\n\n const denied = makeHandle([], { treatGroupAdminsAsAdmins: false });\n denied.setBody({ id: \"x\", decision: \"approve\", decided_by: \"gadmin\", as_group_admin: true });\n m = mockRes();\n await denied.handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 403);\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/domain/schema.d.ts
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* domain/schema.ts — 数据库
|
|
2
|
+
* domain/schema.ts — 数据库 Schema 初始化定义(平台内嵌表唯一基线)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 集中管理平台核心数据表的 `CREATE TABLE IF NOT EXISTS` 与 `CREATE INDEX IF NOT EXISTS` DDL 语句。
|
|
5
|
+
* 调用方式:`initSchema(db)` —— 在 `openDatabase` 之后、业务查询启动前执行。
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* (13) 玩家指令用量 (14) 价格指数
|
|
17
|
-
* (15) 每日任务/统计 (16) 经济账户/流水/幂等
|
|
18
|
-
*
|
|
19
|
-
* 完整表名以 `sfmc_` 前缀,具体见下方每个 db.exec 块。
|
|
20
|
-
*
|
|
21
|
-
* 备注: ALTER / DROP 不在此处;如需迁移请新建 tools/migrate-*.ts,
|
|
22
|
-
* 保持本文件只做"全新初始化"。
|
|
7
|
+
* 业务数据域分布:
|
|
8
|
+
* (1) 世界备份 (2) 玩家备份 (3) 计分板
|
|
9
|
+
* (4) 玩家行为日志 (5) 聊天频道 (6) 聊天消息
|
|
10
|
+
* (7) 红包 (8) 合作社主体/成员/邀请/账户
|
|
11
|
+
* (9) 合作社商店项/组 (10) 合作社流水/审计
|
|
12
|
+
* (11) 领地主体/成员 (12) 领地邀请/审计/幂等
|
|
13
|
+
* (13) 指令用量 (14) 价格指数
|
|
14
|
+
* (15) 每日任务/统计 (16) 经济账户/流水/幂等
|
|
15
|
+
* (17) QQ 绑定与申请 (18) 模块自声明元数据表
|
|
23
16
|
*/
|
|
24
17
|
import type { DatabaseSync } from "node:sqlite";
|
|
18
|
+
/**
|
|
19
|
+
* 初始化数据库结构,创建所有平台内嵌表与索引。
|
|
20
|
+
*
|
|
21
|
+
* @param db SQLite 数据库连接实例。
|
|
22
|
+
*/
|
|
25
23
|
export declare function initSchema(db: DatabaseSync): void;
|
|
26
24
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/domain/schema.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/domain/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAofjD"}
|
package/dist/domain/schema.js
CHANGED
|
@@ -509,6 +509,75 @@ function initSchema(db) {
|
|
|
509
509
|
CREATE INDEX IF NOT EXISTS idx_sfmc_lands_owner ON sfmc_lands(owner_player_id, status);
|
|
510
510
|
CREATE INDEX IF NOT EXISTS idx_sfmc_lands_location ON sfmc_lands(dimension, min_x, max_x, min_z, max_z, status)`
|
|
511
511
|
);
|
|
512
|
+
db.exec(
|
|
513
|
+
/* sql */
|
|
514
|
+
`
|
|
515
|
+
CREATE TABLE IF NOT EXISTS sfmc_qq_bind_pending (
|
|
516
|
+
code TEXT PRIMARY KEY NOT NULL, -- \u77ED\u7801
|
|
517
|
+
qq_user_openid TEXT NOT NULL, -- QQ \u4FA7\u7528\u6237 openid / llbot user id
|
|
518
|
+
qq_backend TEXT NOT NULL DEFAULT 'official', -- official | llbot
|
|
519
|
+
created_at INTEGER NOT NULL,
|
|
520
|
+
expires_at INTEGER NOT NULL
|
|
521
|
+
)`
|
|
522
|
+
);
|
|
523
|
+
db.exec(
|
|
524
|
+
/* sql */
|
|
525
|
+
`
|
|
526
|
+
CREATE TABLE IF NOT EXISTS sfmc_qq_bindings (
|
|
527
|
+
qq_user_openid TEXT PRIMARY KEY NOT NULL,
|
|
528
|
+
player_xuid TEXT NOT NULL UNIQUE,
|
|
529
|
+
player_name TEXT NOT NULL DEFAULT '',
|
|
530
|
+
qq_backend TEXT NOT NULL DEFAULT 'official',
|
|
531
|
+
bound_at INTEGER NOT NULL
|
|
532
|
+
)`
|
|
533
|
+
);
|
|
534
|
+
db.exec(
|
|
535
|
+
/* sql */
|
|
536
|
+
`
|
|
537
|
+
CREATE INDEX IF NOT EXISTS idx_qq_bind_pending_openid ON sfmc_qq_bind_pending(qq_user_openid);
|
|
538
|
+
CREATE INDEX IF NOT EXISTS idx_qq_bindings_xuid ON sfmc_qq_bindings(player_xuid)`
|
|
539
|
+
);
|
|
540
|
+
db.exec(
|
|
541
|
+
/* sql */
|
|
542
|
+
`
|
|
543
|
+
CREATE TABLE IF NOT EXISTS sfmc_qq_join_requests (
|
|
544
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
545
|
+
applicant_openid TEXT NOT NULL,
|
|
546
|
+
player_name TEXT NOT NULL,
|
|
547
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
548
|
+
qq_backend TEXT NOT NULL DEFAULT 'official',
|
|
549
|
+
created_at INTEGER NOT NULL,
|
|
550
|
+
decided_at INTEGER,
|
|
551
|
+
decided_by TEXT,
|
|
552
|
+
applied_at INTEGER,
|
|
553
|
+
apply_error TEXT
|
|
554
|
+
)`
|
|
555
|
+
);
|
|
556
|
+
db.exec(
|
|
557
|
+
/* sql */
|
|
558
|
+
`
|
|
559
|
+
CREATE INDEX IF NOT EXISTS idx_qq_join_status ON sfmc_qq_join_requests(status, created_at)`
|
|
560
|
+
);
|
|
561
|
+
db.exec(
|
|
562
|
+
/* sql */
|
|
563
|
+
`
|
|
564
|
+
CREATE TABLE IF NOT EXISTS sfmc_qq_admin_actions (
|
|
565
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
566
|
+
kind TEXT NOT NULL,
|
|
567
|
+
target_name TEXT NOT NULL,
|
|
568
|
+
reason TEXT NOT NULL DEFAULT '',
|
|
569
|
+
requested_by TEXT NOT NULL DEFAULT '',
|
|
570
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
571
|
+
created_at INTEGER NOT NULL,
|
|
572
|
+
applied_at INTEGER,
|
|
573
|
+
apply_error TEXT
|
|
574
|
+
)`
|
|
575
|
+
);
|
|
576
|
+
db.exec(
|
|
577
|
+
/* sql */
|
|
578
|
+
`
|
|
579
|
+
CREATE INDEX IF NOT EXISTS idx_qq_admin_actions_status ON sfmc_qq_admin_actions(status, created_at)`
|
|
580
|
+
);
|
|
512
581
|
}
|
|
513
582
|
export {
|
|
514
583
|
initSchema
|