asaihost-nodejs 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +518 -0
  2. package/dist/InterventionLog-25EHE4QR.cjs +30 -0
  3. package/dist/InterventionLog-25EHE4QR.cjs.map +1 -0
  4. package/dist/InterventionLog-R6CCPU2B.es.js +30 -0
  5. package/dist/InterventionLog-R6CCPU2B.es.js.map +1 -0
  6. package/dist/LogStore-ECXX34WC.es.js +54 -0
  7. package/dist/LogStore-ECXX34WC.es.js.map +1 -0
  8. package/dist/LogStore-M2AUBNGO.cjs +54 -0
  9. package/dist/LogStore-M2AUBNGO.cjs.map +1 -0
  10. package/dist/asaihost-nodejs.cjs +6761 -0
  11. package/dist/asaihost-nodejs.cjs.map +1 -0
  12. package/dist/asaihost-nodejs.d.cts +406 -0
  13. package/dist/asaihost-nodejs.d.ts +406 -0
  14. package/dist/asaihost-nodejs.es.js +6761 -0
  15. package/dist/asaihost-nodejs.es.js.map +1 -0
  16. package/dist/chunk-4WJDC4M4.es.js +311 -0
  17. package/dist/chunk-4WJDC4M4.es.js.map +1 -0
  18. package/dist/chunk-6U2I5EON.es.js +12 -0
  19. package/dist/chunk-6U2I5EON.es.js.map +1 -0
  20. package/dist/chunk-DROOHKB3.es.js +87 -0
  21. package/dist/chunk-DROOHKB3.es.js.map +1 -0
  22. package/dist/chunk-E7V3EBDB.es.js +435 -0
  23. package/dist/chunk-E7V3EBDB.es.js.map +1 -0
  24. package/dist/chunk-EOGZPRML.es.js +740 -0
  25. package/dist/chunk-EOGZPRML.es.js.map +1 -0
  26. package/dist/chunk-G4WVDUBH.cjs +463 -0
  27. package/dist/chunk-G4WVDUBH.cjs.map +1 -0
  28. package/dist/chunk-HHPGUJ75.cjs +162 -0
  29. package/dist/chunk-HHPGUJ75.cjs.map +1 -0
  30. package/dist/chunk-KOFCBB3N.cjs +87 -0
  31. package/dist/chunk-KOFCBB3N.cjs.map +1 -0
  32. package/dist/chunk-KUZQ36IJ.es.js +162 -0
  33. package/dist/chunk-KUZQ36IJ.es.js.map +1 -0
  34. package/dist/chunk-LOMSUPPU.es.js +1202 -0
  35. package/dist/chunk-LOMSUPPU.es.js.map +1 -0
  36. package/dist/chunk-MCMADJQR.es.js +463 -0
  37. package/dist/chunk-MCMADJQR.es.js.map +1 -0
  38. package/dist/chunk-NLBFIRDB.cjs +105 -0
  39. package/dist/chunk-NLBFIRDB.cjs.map +1 -0
  40. package/dist/chunk-O6LVLSNB.cjs +40 -0
  41. package/dist/chunk-O6LVLSNB.cjs.map +1 -0
  42. package/dist/chunk-QWVR2HNT.cjs +435 -0
  43. package/dist/chunk-QWVR2HNT.cjs.map +1 -0
  44. package/dist/chunk-SMVZ3ZDJ.cjs +12 -0
  45. package/dist/chunk-SMVZ3ZDJ.cjs.map +1 -0
  46. package/dist/chunk-UTKVJE7N.es.js +40 -0
  47. package/dist/chunk-UTKVJE7N.es.js.map +1 -0
  48. package/dist/chunk-VLMM735Q.cjs +1202 -0
  49. package/dist/chunk-VLMM735Q.cjs.map +1 -0
  50. package/dist/chunk-W7DVKGX5.cjs +311 -0
  51. package/dist/chunk-W7DVKGX5.cjs.map +1 -0
  52. package/dist/chunk-X7VHW2TD.cjs +740 -0
  53. package/dist/chunk-X7VHW2TD.cjs.map +1 -0
  54. package/dist/chunk-XBIN4C2V.es.js +105 -0
  55. package/dist/chunk-XBIN4C2V.es.js.map +1 -0
  56. package/dist/intervention-hooks-FI4EHOL5.es.js +21 -0
  57. package/dist/intervention-hooks-FI4EHOL5.es.js.map +1 -0
  58. package/dist/intervention-hooks-LE6X4C6N.cjs +21 -0
  59. package/dist/intervention-hooks-LE6X4C6N.cjs.map +1 -0
  60. package/dist/log-alert-GLCULMQP.es.js +51 -0
  61. package/dist/log-alert-GLCULMQP.es.js.map +1 -0
  62. package/dist/log-alert-LAJ6VSAG.cjs +51 -0
  63. package/dist/log-alert-LAJ6VSAG.cjs.map +1 -0
  64. package/dist/log-formats-FKFMQKNO.cjs +31 -0
  65. package/dist/log-formats-FKFMQKNO.cjs.map +1 -0
  66. package/dist/log-formats-I5E32H5F.es.js +31 -0
  67. package/dist/log-formats-I5E32H5F.es.js.map +1 -0
  68. package/dist/password-63OY27RX.es.js +20 -0
  69. package/dist/password-63OY27RX.es.js.map +1 -0
  70. package/dist/password-NZ5EGDWG.cjs +20 -0
  71. package/dist/password-NZ5EGDWG.cjs.map +1 -0
  72. package/dist/user-store-J7LWEXSW.es.js +12 -0
  73. package/dist/user-store-J7LWEXSW.es.js.map +1 -0
  74. package/dist/user-store-SX4N6PQ4.cjs +12 -0
  75. package/dist/user-store-SX4N6PQ4.cjs.map +1 -0
  76. package/package.json +79 -0
@@ -0,0 +1,740 @@
1
+ import {
2
+ asRows,
3
+ getDataServer,
4
+ qNum,
5
+ qStr,
6
+ readJsonBlob,
7
+ resolveStoreConfig,
8
+ writeJsonBlob
9
+ } from "./chunk-KUZQ36IJ.es.js";
10
+ import {
11
+ __name
12
+ } from "./chunk-6U2I5EON.es.js";
13
+
14
+ // src/sysserver/api/login/store/user-db.ts
15
+ var USER_FIELDS = [
16
+ "id",
17
+ "us",
18
+ "email",
19
+ "passHash",
20
+ "passSalt",
21
+ "lv",
22
+ "role",
23
+ "status",
24
+ "failCount",
25
+ "lockedUntil",
26
+ "mustChangePassword",
27
+ "passwordChangedAt",
28
+ "passHistory",
29
+ "createdAt",
30
+ "updatedAt"
31
+ ];
32
+ var SESSION_FIELDS = ["id", "us", "ip", "createdAt", "expiresAt", "lastActivityAt", "pri", "pub"];
33
+ var FILE_USERS = "users.json";
34
+ var FILE_SESSIONS = "sessions.json";
35
+ var TABLE_USERS = "auth_users";
36
+ var TABLE_SESSIONS = "auth_sessions";
37
+ function getUserStoreConfig($asai) {
38
+ const resolved = resolveStoreConfig($asai, "user");
39
+ return { type: resolved.type, opt: resolved.opt };
40
+ }
41
+ __name(getUserStoreConfig, "getUserStoreConfig");
42
+ function getUserDataServer($asai) {
43
+ return getDataServer($asai, "user");
44
+ }
45
+ __name(getUserDataServer, "getUserDataServer");
46
+ function rowToRecord(row, numericKeys) {
47
+ const out = { ...row };
48
+ for (const key of numericKeys) {
49
+ if (out[key] != null && out[key] !== "") out[key] = Number(out[key]);
50
+ }
51
+ if (out.lockedUntil === "" || out.lockedUntil == null) delete out.lockedUntil;
52
+ if (out.email === "") delete out.email;
53
+ return out;
54
+ }
55
+ __name(rowToRecord, "rowToRecord");
56
+ async function readJsonArray(db, filename) {
57
+ return readJsonBlob(db, filename);
58
+ }
59
+ __name(readJsonArray, "readJsonArray");
60
+ async function writeJsonArray(db, filename, data) {
61
+ await writeJsonBlob(db, filename, data);
62
+ }
63
+ __name(writeJsonArray, "writeJsonArray");
64
+ async function dbListUsers($asai) {
65
+ const db = getUserDataServer($asai);
66
+ const cfg = getUserStoreConfig($asai);
67
+ if (cfg.type === "file") {
68
+ return readJsonArray(db, FILE_USERS);
69
+ }
70
+ const res = await db.sqlDb({ type: "select", table: TABLE_USERS, field: [...USER_FIELDS] });
71
+ return asRows(res).map((r) => rowToRecord(r, ["lv", "failCount", "lockedUntil", "mustChangePassword", "passwordChangedAt", "createdAt", "updatedAt"]));
72
+ }
73
+ __name(dbListUsers, "dbListUsers");
74
+ async function dbFindUser($asai, field, value) {
75
+ const db = getUserDataServer($asai);
76
+ const cfg = getUserStoreConfig($asai);
77
+ if (cfg.type === "file") {
78
+ const users = await readJsonArray(db, FILE_USERS);
79
+ return users.find((u) => u[field] === value) || null;
80
+ }
81
+ const res = await db.sqlDb({
82
+ type: "select",
83
+ table: TABLE_USERS,
84
+ field: [...USER_FIELDS],
85
+ where: [[field, "=", qStr(value, cfg.type)]],
86
+ limit: 1
87
+ });
88
+ const rows = asRows(res);
89
+ return rows.length ? rowToRecord(rows[0], ["lv", "failCount", "lockedUntil", "mustChangePassword", "passwordChangedAt", "createdAt", "updatedAt"]) : null;
90
+ }
91
+ __name(dbFindUser, "dbFindUser");
92
+ async function dbCreateUser($asai, user) {
93
+ const db = getUserDataServer($asai);
94
+ const cfg = getUserStoreConfig($asai);
95
+ if (cfg.type === "file") {
96
+ const users = await readJsonArray(db, FILE_USERS);
97
+ users.push(user);
98
+ await writeJsonArray(db, FILE_USERS, users);
99
+ return user;
100
+ }
101
+ await db.sqlDb({
102
+ type: "insert",
103
+ table: TABLE_USERS,
104
+ field: [...USER_FIELDS],
105
+ value: [
106
+ USER_FIELDS.map((f) => {
107
+ const v = user[f];
108
+ if (typeof v === "number") return qNum(v);
109
+ return qStr(v == null ? "" : String(v), cfg.type);
110
+ })
111
+ ]
112
+ });
113
+ return user;
114
+ }
115
+ __name(dbCreateUser, "dbCreateUser");
116
+ async function dbUpdateUser($asai, us, patch) {
117
+ const existing = await dbFindUser($asai, "us", us);
118
+ if (!existing) return null;
119
+ const merged = { ...existing, ...patch, updatedAt: Date.now() };
120
+ const db = getUserDataServer($asai);
121
+ const cfg = getUserStoreConfig($asai);
122
+ if (cfg.type === "file") {
123
+ const users = await readJsonArray(db, FILE_USERS);
124
+ const idx = users.findIndex((u) => u.us === us);
125
+ if (idx < 0) return null;
126
+ users[idx] = merged;
127
+ await writeJsonArray(db, FILE_USERS, users);
128
+ return merged;
129
+ }
130
+ const sets = [];
131
+ for (const [key, val] of Object.entries(patch)) {
132
+ if (val === void 0) continue;
133
+ if (typeof val === "number") sets.push([key, val]);
134
+ else sets.push([key, val == null ? "" : String(val)]);
135
+ }
136
+ sets.push(["updatedAt", merged.updatedAt]);
137
+ await db.sqlDb({
138
+ type: "update",
139
+ table: TABLE_USERS,
140
+ set: sets,
141
+ where: [["us", "=", qStr(us, cfg.type)]]
142
+ });
143
+ return merged;
144
+ }
145
+ __name(dbUpdateUser, "dbUpdateUser");
146
+ async function dbListSessions($asai) {
147
+ const db = getUserDataServer($asai);
148
+ const cfg = getUserStoreConfig($asai);
149
+ if (cfg.type === "file") {
150
+ return readJsonArray(db, FILE_SESSIONS);
151
+ }
152
+ const res = await db.sqlDb({ type: "select", table: TABLE_SESSIONS, field: [...SESSION_FIELDS] });
153
+ return asRows(res).map((r) => rowToRecord(r, ["createdAt", "expiresAt", "lastActivityAt"]));
154
+ }
155
+ __name(dbListSessions, "dbListSessions");
156
+ async function dbWriteSessions($asai, sessions) {
157
+ const db = getUserDataServer($asai);
158
+ const cfg = getUserStoreConfig($asai);
159
+ if (cfg.type === "file") {
160
+ await writeJsonArray(db, FILE_SESSIONS, sessions);
161
+ return;
162
+ }
163
+ const existing = await dbListSessions($asai);
164
+ for (const s of existing) {
165
+ await db.sqlDb({
166
+ type: "delete",
167
+ table: TABLE_SESSIONS,
168
+ where: [["id", "=", qStr(String(s.id), cfg.type)]]
169
+ });
170
+ }
171
+ for (const s of sessions) {
172
+ await db.sqlDb({
173
+ type: "insert",
174
+ table: TABLE_SESSIONS,
175
+ field: [...SESSION_FIELDS],
176
+ value: [
177
+ SESSION_FIELDS.map((f) => {
178
+ const v = s[f];
179
+ if (typeof v === "number") return qNum(v);
180
+ return qStr(v == null ? "" : String(v), cfg.type);
181
+ })
182
+ ]
183
+ });
184
+ }
185
+ }
186
+ __name(dbWriteSessions, "dbWriteSessions");
187
+ async function dbAddSession($asai, session) {
188
+ const db = getUserDataServer($asai);
189
+ const cfg = getUserStoreConfig($asai);
190
+ if (cfg.type === "file") {
191
+ const sessions = await readJsonArray(db, FILE_SESSIONS);
192
+ sessions.push(session);
193
+ await writeJsonArray(db, FILE_SESSIONS, sessions);
194
+ return;
195
+ }
196
+ await db.sqlDb({
197
+ type: "insert",
198
+ table: TABLE_SESSIONS,
199
+ field: [...SESSION_FIELDS],
200
+ value: [
201
+ SESSION_FIELDS.map((f) => {
202
+ const v = session[f];
203
+ if (typeof v === "number") return qNum(v);
204
+ return qStr(v == null ? "" : String(v), cfg.type);
205
+ })
206
+ ]
207
+ });
208
+ }
209
+ __name(dbAddSession, "dbAddSession");
210
+ async function dbRemoveSession($asai, sessionId) {
211
+ const db = getUserDataServer($asai);
212
+ const cfg = getUserStoreConfig($asai);
213
+ if (cfg.type === "file") {
214
+ const sessions = await readJsonArray(db, FILE_SESSIONS);
215
+ await writeJsonArray(
216
+ db,
217
+ FILE_SESSIONS,
218
+ sessions.filter((s) => s.id !== sessionId)
219
+ );
220
+ return;
221
+ }
222
+ await db.sqlDb({
223
+ type: "delete",
224
+ table: TABLE_SESSIONS,
225
+ where: [["id", "=", qStr(sessionId, cfg.type)]]
226
+ });
227
+ }
228
+ __name(dbRemoveSession, "dbRemoveSession");
229
+ async function dbRemoveSessionsByUser($asai, us) {
230
+ const db = getUserDataServer($asai);
231
+ const cfg = getUserStoreConfig($asai);
232
+ if (cfg.type === "file") {
233
+ const sessions = await readJsonArray(db, FILE_SESSIONS);
234
+ await writeJsonArray(
235
+ db,
236
+ FILE_SESSIONS,
237
+ sessions.filter((s) => s.us !== us)
238
+ );
239
+ return;
240
+ }
241
+ await db.sqlDb({
242
+ type: "delete",
243
+ table: TABLE_SESSIONS,
244
+ where: [["us", "=", qStr(us, cfg.type)]]
245
+ });
246
+ }
247
+ __name(dbRemoveSessionsByUser, "dbRemoveSessionsByUser");
248
+ async function dbDeleteUser($asai, us) {
249
+ const existing = await dbFindUser($asai, "us", us);
250
+ if (!existing) return false;
251
+ const db = getUserDataServer($asai);
252
+ const cfg = getUserStoreConfig($asai);
253
+ await dbRemoveSessionsByUser($asai, us);
254
+ if (cfg.type === "file") {
255
+ const users = await readJsonArray(db, FILE_USERS);
256
+ const next = users.filter((u) => u.us !== us);
257
+ if (next.length === users.length) return false;
258
+ await writeJsonArray(db, FILE_USERS, next);
259
+ return true;
260
+ }
261
+ await db.sqlDb({
262
+ type: "delete",
263
+ table: TABLE_USERS,
264
+ where: [["us", "=", qStr(us, cfg.type)]]
265
+ });
266
+ return true;
267
+ }
268
+ __name(dbDeleteUser, "dbDeleteUser");
269
+
270
+ // src/infra/security-policy.ts
271
+ import { createHash } from "crypto";
272
+
273
+ // src/sysserver/api/login/service/user-level-config.ts
274
+ import fs from "fs/promises";
275
+ import path from "path";
276
+
277
+ // src/sysserver/api/login/service/route-defaults.ts
278
+ var DEFAULT_ROUTE_RIGHTS = [
279
+ { route: "testkdd", title: "testkdd", minLv: 0, public: true },
280
+ { route: "testkdd/testindex", title: "\u529F\u80FD\u603B\u89C8", minLv: 0, public: true },
281
+ { route: "user/login", title: "\u7528\u6237\u767B\u5F55", minLv: 0, public: true },
282
+ { route: "user/reg", title: "\u7528\u6237\u6CE8\u518C", minLv: 0, public: true },
283
+ { route: "user/exit", title: "\u9000\u51FA\u767B\u5F55", minLv: 0, public: true },
284
+ { route: "testkdd/testerrorcodes", title: "\u9519\u8BEF\u7801\u6CE8\u518C\u8868", minLv: 0, public: true },
285
+ { route: "testkdd/testhealth", title: "\u5065\u5EB7\u76D1\u63A7", minLv: 0, public: true },
286
+ { route: "testkdd/testauth", title: "\u8BA4\u8BC1\u5B9E\u9A8C\u5BA4", minLv: 0, public: true },
287
+ { route: "testkdd/testasdb", title: "AsDb \u603B\u89C8", minLv: 0, public: true },
288
+ { route: "testkdd/testasdbfile", title: "AsDb \u6587\u4EF6", minLv: 0, public: true },
289
+ { route: "testkdd/testasdbsqlite", title: "AsDb SQLite", minLv: 0, public: true },
290
+ { route: "testkdd/testmfc", title: "AES MFC", minLv: 0, public: true },
291
+ { route: "testkdd/testws", title: "WebSocket", minLv: 0, public: true },
292
+ { route: "testkdd/testchat", title: "WS \u804A\u5929\u5BA4", minLv: 0, public: true },
293
+ { route: "testkdd/testascss", title: "AsCss", minLv: 0, public: true },
294
+ { route: "testkdd/testpopbox", title: "\u5F39\u7A97\u7EC4\u4EF6", minLv: 0, public: true },
295
+ { route: "testkdd/testaes", title: "AES \u52A0\u89E3\u5BC6", minLv: 0, public: true },
296
+ { route: "testkdd/testdirectives", title: "Vue \u6307\u4EE4", minLv: 0, public: true },
297
+ { route: "testkdd/testjsonp", title: "JSONP", minLv: 0, public: true },
298
+ { route: "testkdd/testlv1", title: "\u6743\u9650\u6F14\u793A\xB7\u64CD\u4F5C\u5458", minLv: 3 },
299
+ { route: "testkdd/testlv2", title: "\u6743\u9650\u6F14\u793A\xB7\u7BA1\u7406\u5458", minLv: 7 },
300
+ { route: "testkdd/testlv3", title: "\u6743\u9650\u6F14\u793A\xB7\u5F00\u53D1\u8005", minLv: 9 },
301
+ { route: "testkdd/testlog", title: "IEC62443 \u65E5\u5FD7", minLv: 3 },
302
+ { route: "user/manage", title: "\u7528\u6237\u7BA1\u7406", minLv: 7 }
303
+ ];
304
+ var LEGACY_RIGHTS_MIN_LV = {
305
+ lv0: 0,
306
+ lv1: 3,
307
+ lv2: 7,
308
+ lv3: 9
309
+ };
310
+ function normalizeRouteRight(item) {
311
+ if (!item || typeof item !== "object") return null;
312
+ const route = String(item.route || "").trim().replace(/^\/+|\/+$/g, "");
313
+ if (!route) return null;
314
+ const minLv = Number(item.minLv ?? 0);
315
+ return {
316
+ route,
317
+ title: item.title ? String(item.title).trim().slice(0, 128) : void 0,
318
+ minLv: Number.isFinite(minLv) ? Math.max(0, Math.floor(minLv)) : 0,
319
+ public: !!item.public || minLv === 0
320
+ };
321
+ }
322
+ __name(normalizeRouteRight, "normalizeRouteRight");
323
+ function mergeRouteRights(existing, incoming) {
324
+ const map = /* @__PURE__ */ new Map();
325
+ for (const r of existing) map.set(r.route, { ...r });
326
+ for (const r of incoming) {
327
+ const prev = map.get(r.route);
328
+ map.set(r.route, prev ? { ...prev, ...r, route: r.route } : r);
329
+ }
330
+ return [...map.values()].sort((a, b) => a.route.localeCompare(b.route));
331
+ }
332
+ __name(mergeRouteRights, "mergeRouteRights");
333
+ function routesFromLegacyRights(rights, titles) {
334
+ const out = [];
335
+ if (!rights || typeof rights !== "object") return out;
336
+ for (const [bucket, routes] of Object.entries(rights)) {
337
+ if (!Array.isArray(routes)) continue;
338
+ const minLv = LEGACY_RIGHTS_MIN_LV[bucket] ?? 0;
339
+ for (const route of routes) {
340
+ const r = String(route || "").trim();
341
+ if (!r) continue;
342
+ out.push({
343
+ route: r,
344
+ title: titles?.[r],
345
+ minLv,
346
+ public: minLv === 0
347
+ });
348
+ }
349
+ }
350
+ return out;
351
+ }
352
+ __name(routesFromLegacyRights, "routesFromLegacyRights");
353
+
354
+ // src/sysserver/api/login/service/user-level-config.ts
355
+ var DEFAULT_LEVELS = [
356
+ { lv: 0, key: "guest", name: "\u666E\u901A\u7528\u6237", nameEn: "Guest" },
357
+ { lv: 3, key: "operator", name: "\u64CD\u4F5C\u5458", nameEn: "Operator" },
358
+ { lv: 5, key: "engineer", name: "\u5DE5\u7A0B\u5E08", nameEn: "Engineer" },
359
+ { lv: 7, key: "admin", name: "\u7BA1\u7406\u5458", nameEn: "Administrator" },
360
+ { lv: 9, key: "developer", name: "\u5F00\u53D1\u8005", nameEn: "Developer" },
361
+ { lv: 99, key: "superadmin", name: "\u8D85\u7EA7\u7BA1\u7406\u5458", nameEn: "Super Administrator" }
362
+ ];
363
+ var DEFAULT_THRESHOLDS = {
364
+ logRead: 3,
365
+ adminRead: 7,
366
+ adminWrite: 9
367
+ };
368
+ var META_FILE = "_meta/user-levels.json";
369
+ function configPath($asai) {
370
+ const dir = String(getUserStoreConfig($asai).opt?.dir || "webdata/webdb/users/");
371
+ return path.resolve(dir, META_FILE);
372
+ }
373
+ __name(configPath, "configPath");
374
+ function fromHostLevels($asai) {
375
+ const raw = $asai?.hostconfig?.security?.userLevels;
376
+ if (!Array.isArray(raw) || !raw.length) return null;
377
+ const levels = [];
378
+ for (const item of raw) {
379
+ if (!item || typeof item !== "object") continue;
380
+ const lv = Number(item.lv);
381
+ const key = String(item.key || "").trim();
382
+ const name = String(item.name || item.label || "").trim();
383
+ if (!Number.isFinite(lv) || !key || !name) continue;
384
+ levels.push({
385
+ lv,
386
+ key,
387
+ name,
388
+ nameEn: item.nameEn ? String(item.nameEn) : void 0
389
+ });
390
+ }
391
+ return levels.length ? levels.sort((a, b) => a.lv - b.lv) : null;
392
+ }
393
+ __name(fromHostLevels, "fromHostLevels");
394
+ function fromHostThresholds($asai) {
395
+ const t = $asai?.hostconfig?.security?.permissionThresholds;
396
+ if (!t || typeof t !== "object") return { ...DEFAULT_THRESHOLDS };
397
+ return {
398
+ logRead: Number(t.logRead ?? DEFAULT_THRESHOLDS.logRead),
399
+ adminRead: Number(t.adminRead ?? DEFAULT_THRESHOLDS.adminRead),
400
+ adminWrite: Number(t.adminWrite ?? DEFAULT_THRESHOLDS.adminWrite)
401
+ };
402
+ }
403
+ __name(fromHostThresholds, "fromHostThresholds");
404
+ function getDefaultRouteRights($asai) {
405
+ const fromHost = $asai?.hostconfig?.security?.routeRights;
406
+ let base = DEFAULT_ROUTE_RIGHTS.map((r) => ({ ...r }));
407
+ if (Array.isArray(fromHost) && fromHost.length) {
408
+ const parsed = [];
409
+ for (const item of fromHost) {
410
+ const row = normalizeRouteRight(item);
411
+ if (row) parsed.push(row);
412
+ }
413
+ if (parsed.length) base = mergeRouteRights(base, parsed);
414
+ }
415
+ const legacy = $asai?.hostconfig?.security?.routerRights;
416
+ if (legacy && typeof legacy === "object") {
417
+ base = mergeRouteRights(base, routesFromLegacyRights(legacy));
418
+ }
419
+ return base;
420
+ }
421
+ __name(getDefaultRouteRights, "getDefaultRouteRights");
422
+ function getDefaultUserLevelConfig($asai) {
423
+ return {
424
+ levels: fromHostLevels($asai) || DEFAULT_LEVELS.map((l) => ({ ...l })),
425
+ thresholds: fromHostThresholds($asai),
426
+ routeRights: getDefaultRouteRights($asai)
427
+ };
428
+ }
429
+ __name(getDefaultUserLevelConfig, "getDefaultUserLevelConfig");
430
+ function normalizeLevel(item) {
431
+ if (!item || typeof item !== "object") return null;
432
+ const lv = Number(item.lv);
433
+ const key = String(item.key || "").trim().slice(0, 32);
434
+ const name = String(item.name || "").trim().slice(0, 64);
435
+ if (!Number.isFinite(lv) || lv < 0 || !key || !name) return null;
436
+ return {
437
+ lv: Math.floor(lv),
438
+ key,
439
+ name,
440
+ nameEn: item.nameEn ? String(item.nameEn).trim().slice(0, 64) : void 0
441
+ };
442
+ }
443
+ __name(normalizeLevel, "normalizeLevel");
444
+ function normalizeUserLevelConfig(input, $asai) {
445
+ const base = getDefaultUserLevelConfig($asai);
446
+ const synced = getUserLevelConfigSync($asai);
447
+ const levelBase = synced.levels?.length ? synced : base;
448
+ const levels = [];
449
+ const seenLv = /* @__PURE__ */ new Set();
450
+ const seenKey = /* @__PURE__ */ new Set();
451
+ if (Array.isArray(input.levels) && input.levels.length) {
452
+ for (const raw of input.levels) {
453
+ const row = normalizeLevel(raw);
454
+ if (!row || seenLv.has(row.lv) || seenKey.has(row.key)) continue;
455
+ seenLv.add(row.lv);
456
+ seenKey.add(row.key);
457
+ levels.push(row);
458
+ }
459
+ } else {
460
+ levels.push(...levelBase.levels.map((l) => ({ ...l })));
461
+ }
462
+ if (!levels.some((l) => l.lv === 0)) {
463
+ levels.unshift({ lv: 0, key: "guest", name: "\u666E\u901A\u7528\u6237", nameEn: "Guest" });
464
+ }
465
+ levels.sort((a, b) => a.lv - b.lv);
466
+ const th = input.thresholds || {};
467
+ const thresholds = {
468
+ logRead: Number.isFinite(Number(th.logRead)) ? Number(th.logRead) : base.thresholds.logRead,
469
+ adminRead: Number.isFinite(Number(th.adminRead)) ? Number(th.adminRead) : base.thresholds.adminRead,
470
+ adminWrite: Number.isFinite(Number(th.adminWrite)) ? Number(th.adminWrite) : base.thresholds.adminWrite
471
+ };
472
+ return {
473
+ levels,
474
+ thresholds,
475
+ routeRights: normalizeRouteRightsList(input.routeRights, base.routeRights || getDefaultRouteRights($asai)),
476
+ updatedAt: input.updatedAt,
477
+ updatedBy: input.updatedBy
478
+ };
479
+ }
480
+ __name(normalizeUserLevelConfig, "normalizeUserLevelConfig");
481
+ function normalizeRouteRightsList(raw, fallback) {
482
+ if (!Array.isArray(raw) || !raw.length) return fallback.map((r) => ({ ...r }));
483
+ const parsed = [];
484
+ const seen = /* @__PURE__ */ new Set();
485
+ for (const item of raw) {
486
+ const row = normalizeRouteRight(item);
487
+ if (!row || seen.has(row.route)) continue;
488
+ seen.add(row.route);
489
+ parsed.push(row);
490
+ }
491
+ return parsed.length ? parsed : fallback.map((r) => ({ ...r }));
492
+ }
493
+ __name(normalizeRouteRightsList, "normalizeRouteRightsList");
494
+ function validateUserLevelConfig(cfg) {
495
+ if (!cfg.levels.length) return "LEVELS_EMPTY";
496
+ const keys = /* @__PURE__ */ new Set();
497
+ const lvs = /* @__PURE__ */ new Set();
498
+ for (const l of cfg.levels) {
499
+ if (keys.has(l.key) || lvs.has(l.lv)) return "LEVELS_DUPLICATE";
500
+ keys.add(l.key);
501
+ lvs.add(l.lv);
502
+ }
503
+ if (!lvs.has(0)) return "LEVELS_GUEST_REQUIRED";
504
+ const { thresholds } = cfg;
505
+ if (thresholds.logRead > thresholds.adminRead) return "THRESHOLD_ORDER";
506
+ if (thresholds.adminRead > thresholds.adminWrite) return "THRESHOLD_ORDER";
507
+ for (const r of cfg.routeRights || []) {
508
+ if (!r.route) return "ROUTE_INVALID";
509
+ if (!Number.isFinite(r.minLv) || r.minLv < 0) return "ROUTE_MINLV_INVALID";
510
+ }
511
+ return null;
512
+ }
513
+ __name(validateUserLevelConfig, "validateUserLevelConfig");
514
+ function getUserLevelConfigSync($asai) {
515
+ return $asai._userLevelConfig || getDefaultUserLevelConfig($asai);
516
+ }
517
+ __name(getUserLevelConfigSync, "getUserLevelConfigSync");
518
+ async function loadUserLevelConfig($asai) {
519
+ const file = configPath($asai);
520
+ try {
521
+ const text = await fs.readFile(file, "utf8");
522
+ const parsed = JSON.parse(text);
523
+ const cfg = normalizeUserLevelConfig(parsed, $asai);
524
+ const err = validateUserLevelConfig(cfg);
525
+ if (err) {
526
+ $asai._userLevelConfig = getDefaultUserLevelConfig($asai);
527
+ } else {
528
+ $asai._userLevelConfig = cfg;
529
+ }
530
+ } catch {
531
+ $asai._userLevelConfig = getDefaultUserLevelConfig($asai);
532
+ }
533
+ return $asai._userLevelConfig;
534
+ }
535
+ __name(loadUserLevelConfig, "loadUserLevelConfig");
536
+ async function saveUserLevelConfig($asai, input, updatedBy) {
537
+ const cfg = normalizeUserLevelConfig(input, $asai);
538
+ const err = validateUserLevelConfig(cfg);
539
+ if (err) throw new Error(err);
540
+ cfg.updatedAt = Date.now();
541
+ cfg.updatedBy = updatedBy;
542
+ const file = configPath($asai);
543
+ await fs.mkdir(path.dirname(file), { recursive: true });
544
+ await fs.writeFile(file, JSON.stringify(cfg, null, 2), "utf8");
545
+ $asai._userLevelConfig = cfg;
546
+ return cfg;
547
+ }
548
+ __name(saveUserLevelConfig, "saveUserLevelConfig");
549
+ function getPermissionThreshold($asai, key) {
550
+ return getUserLevelConfigSync($asai).thresholds[key];
551
+ }
552
+ __name(getPermissionThreshold, "getPermissionThreshold");
553
+ function getLevelDef($asai, lv) {
554
+ return getUserLevelConfigSync($asai).levels.find((l) => l.lv === lv);
555
+ }
556
+ __name(getLevelDef, "getLevelDef");
557
+ function isValidUserLv($asai, lv) {
558
+ return getUserLevelConfigSync($asai).levels.some((l) => l.lv === lv);
559
+ }
560
+ __name(isValidUserLv, "isValidUserLv");
561
+ function getLevelLabel($asai, lv, lang = "zh-CN") {
562
+ const def = getLevelDef($asai, lv);
563
+ if (!def) return `lv${lv}`;
564
+ if (lang.startsWith("en") && def.nameEn) return def.nameEn;
565
+ return def.name;
566
+ }
567
+ __name(getLevelLabel, "getLevelLabel");
568
+ function toPublicUserLevels($asai, lang = "zh-CN") {
569
+ const cfg = getUserLevelConfigSync($asai);
570
+ return {
571
+ levels: cfg.levels.map((l) => ({
572
+ lv: l.lv,
573
+ key: l.key,
574
+ name: getLevelLabel($asai, l.lv, lang),
575
+ nameZh: l.name,
576
+ nameEn: l.nameEn || l.name
577
+ })),
578
+ thresholds: { ...cfg.thresholds },
579
+ routeRights: (cfg.routeRights || getDefaultRouteRights($asai)).map((r) => ({ ...r }))
580
+ };
581
+ }
582
+ __name(toPublicUserLevels, "toPublicUserLevels");
583
+
584
+ // src/infra/security-policy.ts
585
+ var DEFAULT_POLICY = {
586
+ minLength: 8,
587
+ requireUpper: false,
588
+ requireLower: true,
589
+ requireDigit: true,
590
+ requireSpecial: false,
591
+ maxAgeDays: 0,
592
+ historyCount: 0
593
+ };
594
+ var DEFAULT_ROLES = [
595
+ { name: "viewer", minLv: 0, permissions: ["read"] },
596
+ { name: "operator", minLv: 3, permissions: ["read", "operate"] },
597
+ { name: "engineer", minLv: 5, permissions: ["read", "operate", "audit"] },
598
+ { name: "admin", minLv: 7, permissions: ["read", "operate", "audit", "admin"] },
599
+ { name: "developer", minLv: 9, permissions: ["read", "operate", "audit", "admin", "develop"] },
600
+ { name: "superadmin", minLv: 99, permissions: ["read", "operate", "audit", "admin", "develop", "super"] }
601
+ ];
602
+ function getSecurityBlock($asai) {
603
+ return $asai?.hostconfig?.security || {};
604
+ }
605
+ __name(getSecurityBlock, "getSecurityBlock");
606
+ function getLoginBanner($asai) {
607
+ const sec = getSecurityBlock($asai);
608
+ const banner = sec.banner || {};
609
+ return {
610
+ enabled: banner.enabled === 1 || banner.enabled === true,
611
+ text: String(banner.text || "")
612
+ };
613
+ }
614
+ __name(getLoginBanner, "getLoginBanner");
615
+ function getPasswordPolicy($asai) {
616
+ const sec = getSecurityBlock($asai);
617
+ const p = sec.passwordPolicy || {};
618
+ const minFromLegacy = Number(sec.passwordMinLength ?? DEFAULT_POLICY.minLength);
619
+ return {
620
+ minLength: Number(p.minLength ?? minFromLegacy) || DEFAULT_POLICY.minLength,
621
+ requireUpper: p.requireUpper === 1 || p.requireUpper === true,
622
+ requireLower: p.requireLower !== 0 && p.requireLower !== false,
623
+ requireDigit: p.requireDigit !== 0 && p.requireDigit !== false,
624
+ requireSpecial: p.requireSpecial === 1 || p.requireSpecial === true,
625
+ maxAgeDays: Number(p.maxAgeDays ?? 0) || 0,
626
+ historyCount: Number(p.historyCount ?? 0) || 0
627
+ };
628
+ }
629
+ __name(getPasswordPolicy, "getPasswordPolicy");
630
+ function getSessionIdleMinutes($asai) {
631
+ const sec = getSecurityBlock($asai);
632
+ const v = sec.sessionIdleMinutes;
633
+ return v == null || v === 0 ? 0 : Number(v);
634
+ }
635
+ __name(getSessionIdleMinutes, "getSessionIdleMinutes");
636
+ function getRoleDefinitions($asai) {
637
+ const sec = getSecurityBlock($asai);
638
+ const raw = sec.roles;
639
+ if (!Array.isArray(raw) || !raw.length) return DEFAULT_ROLES;
640
+ return raw.map((r) => ({
641
+ name: String(r.name || "role"),
642
+ minLv: Number(r.minLv ?? 0),
643
+ permissions: Array.isArray(r.permissions) ? r.permissions.map(String) : []
644
+ }));
645
+ }
646
+ __name(getRoleDefinitions, "getRoleDefinitions");
647
+ function resolveRoleForLevel($asai, lv) {
648
+ const roles = getRoleDefinitions($asai);
649
+ let best = roles[0];
650
+ for (const r of roles) {
651
+ if (lv >= r.minLv && r.minLv >= (best?.minLv ?? -1)) best = r;
652
+ }
653
+ return best || DEFAULT_ROLES[0];
654
+ }
655
+ __name(resolveRoleForLevel, "resolveRoleForLevel");
656
+ function hashPasswordForHistory(password) {
657
+ return createHash("sha256").update(password).digest("hex");
658
+ }
659
+ __name(hashPasswordForHistory, "hashPasswordForHistory");
660
+ function validatePasswordWithPolicy(password, policy) {
661
+ if (!password || typeof password !== "string") return "PASSWORD_REQUIRED";
662
+ if (password.length < policy.minLength) return "PASSWORD_TOO_SHORT";
663
+ if (policy.requireLower && !/[a-z]/.test(password)) return "PASSWORD_WEAK";
664
+ if (policy.requireUpper && !/[A-Z]/.test(password)) return "PASSWORD_WEAK";
665
+ if (policy.requireDigit && !/[0-9]/.test(password)) return "PASSWORD_WEAK";
666
+ if (policy.requireSpecial && !/[^a-zA-Z0-9]/.test(password)) return "PASSWORD_WEAK";
667
+ return null;
668
+ }
669
+ __name(validatePasswordWithPolicy, "validatePasswordWithPolicy");
670
+ function parsePassHistory(raw) {
671
+ if (!raw) return [];
672
+ if (Array.isArray(raw)) return raw.map(String);
673
+ if (typeof raw === "string" && raw.startsWith("[")) {
674
+ try {
675
+ const arr = JSON.parse(raw);
676
+ return Array.isArray(arr) ? arr.map(String) : [];
677
+ } catch {
678
+ return [];
679
+ }
680
+ }
681
+ return [];
682
+ }
683
+ __name(parsePassHistory, "parsePassHistory");
684
+ function isPasswordInHistory(password, history) {
685
+ const h = hashPasswordForHistory(password);
686
+ return history.includes(h);
687
+ }
688
+ __name(isPasswordInHistory, "isPasswordInHistory");
689
+ function isPasswordExpired(passwordChangedAt, maxAgeDays) {
690
+ if (!maxAgeDays || maxAgeDays <= 0) return false;
691
+ if (!passwordChangedAt) return false;
692
+ return Date.now() - passwordChangedAt > maxAgeDays * 864e5;
693
+ }
694
+ __name(isPasswordExpired, "isPasswordExpired");
695
+ function getPublicSecurityConfig($asai, lang = "zh-CN") {
696
+ const policy = getPasswordPolicy($asai);
697
+ const banner = getLoginBanner($asai);
698
+ return {
699
+ banner,
700
+ passwordPolicy: {
701
+ minLength: policy.minLength,
702
+ requireUpper: policy.requireUpper,
703
+ requireLower: policy.requireLower,
704
+ requireDigit: policy.requireDigit,
705
+ requireSpecial: policy.requireSpecial
706
+ },
707
+ sessionIdleMinutes: getSessionIdleMinutes($asai),
708
+ roles: getRoleDefinitions($asai).map((r) => ({ name: r.name, minLv: r.minLv })),
709
+ userLevels: toPublicUserLevels($asai, lang),
710
+ allowRegister: getSecurityBlock($asai).allowRegister !== 0
711
+ };
712
+ }
713
+ __name(getPublicSecurityConfig, "getPublicSecurityConfig");
714
+
715
+ export {
716
+ dbListUsers,
717
+ dbFindUser,
718
+ dbCreateUser,
719
+ dbUpdateUser,
720
+ dbListSessions,
721
+ dbWriteSessions,
722
+ dbAddSession,
723
+ dbRemoveSession,
724
+ dbRemoveSessionsByUser,
725
+ dbDeleteUser,
726
+ getUserLevelConfigSync,
727
+ loadUserLevelConfig,
728
+ saveUserLevelConfig,
729
+ getPermissionThreshold,
730
+ isValidUserLv,
731
+ getPasswordPolicy,
732
+ resolveRoleForLevel,
733
+ hashPasswordForHistory,
734
+ validatePasswordWithPolicy,
735
+ parsePassHistory,
736
+ isPasswordInHistory,
737
+ isPasswordExpired,
738
+ getPublicSecurityConfig
739
+ };
740
+ //# sourceMappingURL=chunk-EOGZPRML.es.js.map