asaihost-nodejs 0.0.9 → 0.0.10

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 (44) hide show
  1. package/dist/InterventionLog-WNVDQFAH.cjs.map +1 -1
  2. package/dist/LogStore-AP4T2CFD.cjs.map +1 -1
  3. package/dist/asaihost-nodejs.cjs +684 -492
  4. package/dist/asaihost-nodejs.cjs.map +1 -1
  5. package/dist/asaihost-nodejs.es.js +291 -99
  6. package/dist/asaihost-nodejs.es.js.map +1 -1
  7. package/dist/chunk-2BCIIKRG.cjs.map +1 -1
  8. package/dist/{chunk-OTL2B4WS.es.js → chunk-3E4Y5ATG.es.js} +2 -2
  9. package/dist/{chunk-ZK4O4Z2O.cjs → chunk-5EWEKXQI.cjs} +20 -20
  10. package/dist/chunk-5EWEKXQI.cjs.map +1 -0
  11. package/dist/chunk-ARHPHWT5.cjs.map +1 -1
  12. package/dist/{chunk-GXOFLFFW.cjs → chunk-FRPN33BY.cjs} +3 -2
  13. package/dist/chunk-FRPN33BY.cjs.map +1 -0
  14. package/dist/{chunk-WXW5DYNU.cjs → chunk-H2QP5J7X.cjs} +9 -9
  15. package/dist/chunk-H2QP5J7X.cjs.map +1 -0
  16. package/dist/chunk-HR5JKN7Y.cjs.map +1 -1
  17. package/dist/{chunk-BOIRAQXE.es.js → chunk-M3NURHGU.es.js} +2 -1
  18. package/dist/chunk-O6LVLSNB.cjs.map +1 -1
  19. package/dist/chunk-OCEBEEP5.cjs.map +1 -1
  20. package/dist/chunk-SMVZ3ZDJ.cjs.map +1 -1
  21. package/dist/chunk-U2YJH2RW.cjs.map +1 -1
  22. package/dist/{chunk-5WJQD4RZ.es.js → chunk-UKZ3DP37.es.js} +3 -3
  23. package/dist/intervention-hooks-4NAAAZK3.cjs.map +1 -1
  24. package/dist/log-alert-LAJ6VSAG.cjs.map +1 -1
  25. package/dist/log-formats-TUXGMCCU.cjs.map +1 -1
  26. package/dist/{password-4P7IZUDQ.es.js → password-2ZBHQQBF.es.js} +3 -3
  27. package/dist/password-T762KJOI.cjs +20 -0
  28. package/dist/password-T762KJOI.cjs.map +1 -0
  29. package/dist/user-store-6QNAL4CK.cjs +12 -0
  30. package/dist/user-store-6QNAL4CK.cjs.map +1 -0
  31. package/dist/{user-store-FXD2N55G.es.js → user-store-ZI67TXSI.es.js} +3 -3
  32. package/package.json +1 -1
  33. package/dist/chunk-GXOFLFFW.cjs.map +0 -1
  34. package/dist/chunk-WXW5DYNU.cjs.map +0 -1
  35. package/dist/chunk-ZK4O4Z2O.cjs.map +0 -1
  36. package/dist/password-MDYRL6ZD.cjs +0 -20
  37. package/dist/password-MDYRL6ZD.cjs.map +0 -1
  38. package/dist/user-store-55HOLHEM.cjs +0 -12
  39. package/dist/user-store-55HOLHEM.cjs.map +0 -1
  40. /package/dist/{chunk-OTL2B4WS.es.js.map → chunk-3E4Y5ATG.es.js.map} +0 -0
  41. /package/dist/{chunk-BOIRAQXE.es.js.map → chunk-M3NURHGU.es.js.map} +0 -0
  42. /package/dist/{chunk-5WJQD4RZ.es.js.map → chunk-UKZ3DP37.es.js.map} +0 -0
  43. /package/dist/{password-4P7IZUDQ.es.js.map → password-2ZBHQQBF.es.js.map} +0 -0
  44. /package/dist/{user-store-FXD2N55G.es.js.map → user-store-ZI67TXSI.es.js.map} +0 -0
@@ -50,7 +50,7 @@ import {
50
50
  secureCompare,
51
51
  validatePasswordChange,
52
52
  verifyPassword
53
- } from "./chunk-OTL2B4WS.es.js";
53
+ } from "./chunk-3E4Y5ATG.es.js";
54
54
  import {
55
55
  USER_LV,
56
56
  applySecretsToHostConfig,
@@ -62,20 +62,21 @@ import {
62
62
  resolveHttpsCertFromPaths,
63
63
  stripForbiddenSecretsFromHostConfig,
64
64
  validateAsaiSecrets
65
- } from "./chunk-5WJQD4RZ.es.js";
65
+ } from "./chunk-UKZ3DP37.es.js";
66
66
  import {
67
67
  getDefaultResetPassword,
68
68
  getPasswordPolicy,
69
69
  getPermissionThreshold,
70
70
  getPublicSecurityConfig,
71
71
  getUserLevelConfigSync,
72
+ getUserStoreConfig,
72
73
  isPasswordExpired,
73
74
  isValidUserLv,
74
75
  loadUserLevelConfig,
75
76
  resolveRoleForLevel,
76
77
  saveUserLevelConfig,
77
78
  validatePasswordWithPolicy
78
- } from "./chunk-BOIRAQXE.es.js";
79
+ } from "./chunk-M3NURHGU.es.js";
79
80
  import {
80
81
  getDataServerForAsDb
81
82
  } from "./chunk-ZQBHJRAO.es.js";
@@ -1000,6 +1001,11 @@ async function clearStaleWsIfDead($asai, hostdir, us, existingWs) {
1000
1001
  if (map?.[us]) delete map[us];
1001
1002
  return true;
1002
1003
  }
1004
+ if (existingWs._asaiKicked) {
1005
+ terminateWs(existingWs);
1006
+ if (map?.[us] === existingWs) delete map[us];
1007
+ return true;
1008
+ }
1003
1009
  if (!isWsStillActive(existingWs)) {
1004
1010
  terminateWs(existingWs);
1005
1011
  if (map?.[us] === existingWs) delete map[us];
@@ -1025,22 +1031,38 @@ function getWsUserinfo(req, $asai) {
1025
1031
  }
1026
1032
  __name(getWsUserinfo, "getWsUserinfo");
1027
1033
  function listOnlineWsUsers($asai, hostdir) {
1034
+ return listOnlineWsEntries($asai, hostdir).map((e) => e.us);
1035
+ }
1036
+ __name(listOnlineWsUsers, "listOnlineWsUsers");
1037
+ function listOnlineWsEntries($asai, hostdir) {
1028
1038
  const users = [];
1039
+ const seen = /* @__PURE__ */ new Set();
1029
1040
  const map = $asai.connectionsws || {};
1030
1041
  const dirs = hostdir ? [hostdir] : Object.keys(map);
1031
1042
  for (const dir of dirs) {
1032
1043
  for (const [us, ws2] of Object.entries(map[dir] || {})) {
1033
1044
  if (!us || us === "asai") continue;
1034
1045
  if (isWsStillActive(ws2)) {
1035
- users.push(us);
1046
+ if (seen.has(us)) continue;
1047
+ seen.add(us);
1048
+ const rawLv = ws2?.Userinfo?.[1];
1049
+ if (rawLv === "" || rawLv == null) {
1050
+ users.push({ us, lv: null });
1051
+ } else {
1052
+ const lvNum = Number(rawLv);
1053
+ users.push({
1054
+ us,
1055
+ lv: Number.isFinite(lvNum) ? lvNum : null
1056
+ });
1057
+ }
1036
1058
  } else if (map[dir]?.[us] === ws2) {
1037
1059
  delete map[dir][us];
1038
1060
  }
1039
1061
  }
1040
1062
  }
1041
- return [...new Set(users)];
1063
+ return users;
1042
1064
  }
1043
- __name(listOnlineWsUsers, "listOnlineWsUsers");
1065
+ __name(listOnlineWsEntries, "listOnlineWsEntries");
1044
1066
  function isUserOnlineOnWs($asai, us, hostdir) {
1045
1067
  if (!us || us === "asai") return false;
1046
1068
  const map = $asai.connectionsws || {};
@@ -1054,19 +1076,73 @@ function isUserOnlineOnWs($asai, us, hostdir) {
1054
1076
  return false;
1055
1077
  }
1056
1078
  __name(isUserOnlineOnWs, "isUserOnlineOnWs");
1057
- function kickUserWs($asai, us, hostdir) {
1079
+ async function kickUserWs($asai, us, hostdir) {
1058
1080
  if (!us || us === "asai") return;
1059
1081
  const map = $asai.connectionsws || {};
1060
1082
  const dirs = hostdir ? [hostdir] : Object.keys(map);
1083
+ const waits = [];
1061
1084
  for (const dir of dirs) {
1062
1085
  const ws2 = map[dir]?.[us];
1063
1086
  if (!ws2) continue;
1087
+ if (map[dir]?.[us] === ws2) delete map[dir][us];
1064
1088
  try {
1065
- ws2.close();
1089
+ ws2._asaiKicked = Date.now();
1066
1090
  } catch {
1067
1091
  }
1068
- if (map[dir]?.[us] === ws2) delete map[dir][us];
1092
+ waits.push(
1093
+ new Promise((resolve6) => {
1094
+ let done = false;
1095
+ const finish = /* @__PURE__ */ __name(() => {
1096
+ if (done) return;
1097
+ done = true;
1098
+ try {
1099
+ ws2.off?.("close", finish);
1100
+ } catch {
1101
+ }
1102
+ resolve6();
1103
+ }, "finish");
1104
+ try {
1105
+ ws2.once?.("close", finish);
1106
+ } catch {
1107
+ }
1108
+ const payload = JSON.stringify({
1109
+ ty: "publish/web",
1110
+ db: {
1111
+ exit: 1,
1112
+ type: "err",
1113
+ lang: "forcedout",
1114
+ langpm: `${us}`,
1115
+ con: `${us} forced out by another login`
1116
+ }
1117
+ });
1118
+ const hardClose = /* @__PURE__ */ __name(() => {
1119
+ try {
1120
+ if (typeof ws2.terminate === "function") ws2.terminate();
1121
+ else ws2.close();
1122
+ } catch {
1123
+ try {
1124
+ ws2.close();
1125
+ } catch {
1126
+ }
1127
+ }
1128
+ }, "hardClose");
1129
+ try {
1130
+ ws2.send(payload, () => {
1131
+ hardClose();
1132
+ finish();
1133
+ });
1134
+ } catch {
1135
+ hardClose();
1136
+ finish();
1137
+ }
1138
+ setTimeout(() => {
1139
+ hardClose();
1140
+ finish();
1141
+ }, 150);
1142
+ })
1143
+ );
1069
1144
  }
1145
+ if (waits.length) await Promise.all(waits);
1070
1146
  }
1071
1147
  __name(kickUserWs, "kickUserWs");
1072
1148
  async function removeSessionOnWsClose($asai, us, token) {
@@ -1109,7 +1185,8 @@ async function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
1109
1185
  rejectIncomingWs(ws2, {
1110
1186
  lang: "pageworking",
1111
1187
  langpm: `${us}`,
1112
- con: `${us} page already running`
1188
+ con: `${us} page already running`,
1189
+ online: listOnlineWsEntries($asai, hostdir)
1113
1190
  });
1114
1191
  return false;
1115
1192
  }
@@ -1125,7 +1202,8 @@ async function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
1125
1202
  rejectIncomingWs(ws2, {
1126
1203
  lang: "logged",
1127
1204
  langpm: `${us}`,
1128
- con: `${us} logged in!`
1205
+ con: `${us} logged in!`,
1206
+ online: listOnlineWsEntries($asai, hostdir)
1129
1207
  });
1130
1208
  return false;
1131
1209
  }
@@ -1137,19 +1215,22 @@ async function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
1137
1215
  (item) => item.Userinfo?.[2] === tk && item.Userinfo?.[0] !== us
1138
1216
  );
1139
1217
  const maxOnline = hostcfg.maxonline || 100;
1218
+ const online = listOnlineWsEntries($asai, hostdir);
1140
1219
  if (sameTkWs) {
1141
1220
  rejectIncomingWs(ws2, {
1142
1221
  lang: "loggedsametk",
1143
1222
  langpm: `${us}(${sameTkWs.Userinfo[0]})`,
1144
- con: `${us}(${sameTkWs.Userinfo[0]}) logged in!`
1223
+ con: `${us}(${sameTkWs.Userinfo[0]}) logged in!`,
1224
+ online
1145
1225
  });
1146
1226
  return false;
1147
1227
  }
1148
- if (Object.keys(connections).length >= maxOnline) {
1228
+ if (online.length >= maxOnline) {
1149
1229
  rejectIncomingWs(ws2, {
1150
1230
  lang: "maxlogged",
1151
1231
  langpm: `${maxOnline}`,
1152
- con: `max ${maxOnline} users online!`
1232
+ con: `max ${maxOnline} users online!`,
1233
+ online
1153
1234
  });
1154
1235
  return false;
1155
1236
  }
@@ -1533,11 +1614,11 @@ function createSkipChecker($asai) {
1533
1614
  }
1534
1615
  if (exact.size === 0 && prefixes.length === 0) return () => false;
1535
1616
  return (url) => {
1536
- const path8 = normalizeRequestPath(url);
1537
- if (!path8) return false;
1538
- if (exact.has(path8)) return true;
1617
+ const path9 = normalizeRequestPath(url);
1618
+ if (!path9) return false;
1619
+ if (exact.has(path9)) return true;
1539
1620
  for (let i = 0; i < prefixes.length; i++) {
1540
- if (path8.startsWith(prefixes[i])) return true;
1621
+ if (path9.startsWith(prefixes[i])) return true;
1541
1622
  }
1542
1623
  return false;
1543
1624
  };
@@ -2424,7 +2505,7 @@ async function attachServerUserLevel(req, $asai) {
2424
2505
  if (!Array.isArray(userinfo) || !userinfo[0]) return;
2425
2506
  const us = String(userinfo[0]);
2426
2507
  try {
2427
- const { createUserStore: createUserStore2 } = await import("./user-store-FXD2N55G.es.js");
2508
+ const { createUserStore: createUserStore2 } = await import("./user-store-ZI67TXSI.es.js");
2428
2509
  const user = await createUserStore2($asai).findByUsername(us);
2429
2510
  if (user && user.status === "active") {
2430
2511
  req._serverUser = { us: user.us, lv: user.lv, status: user.status };
@@ -2455,11 +2536,11 @@ function getSecurityConfig($asai) {
2455
2536
  return $asai?.hostconfig?.security || {};
2456
2537
  }
2457
2538
  __name(getSecurityConfig, "getSecurityConfig");
2458
- function pathMatchesPattern(path8, pattern) {
2539
+ function pathMatchesPattern(path9, pattern) {
2459
2540
  if (pattern.endsWith("*")) {
2460
- return path8.startsWith(pattern.slice(0, -1));
2541
+ return path9.startsWith(pattern.slice(0, -1));
2461
2542
  }
2462
- return path8 === pattern;
2543
+ return path9 === pattern;
2463
2544
  }
2464
2545
  __name(pathMatchesPattern, "pathMatchesPattern");
2465
2546
  function isAuthSkipped($asai, pathname) {
@@ -4066,6 +4147,37 @@ async function purgeOfflineSessions($asai, hostdir, store) {
4066
4147
  __name(purgeOfflineSessions, "purgeOfflineSessions");
4067
4148
 
4068
4149
  // src/sysserver/api/login/index.ts
4150
+ import fs9 from "fs";
4151
+ import path4 from "path";
4152
+ async function resolveUserLv($asai, store, us, wsLv) {
4153
+ if (!us) return null;
4154
+ try {
4155
+ const user = await store.findByUsername(us);
4156
+ if (user && user.lv != null && Number.isFinite(Number(user.lv))) {
4157
+ return Number(user.lv);
4158
+ }
4159
+ } catch {
4160
+ }
4161
+ try {
4162
+ const cfg = getUserStoreConfig($asai);
4163
+ const dir = String(cfg?.opt?.dir || "");
4164
+ if (dir) {
4165
+ const base = path4.isAbsolute(dir) ? dir : path4.join(process.cwd(), dir);
4166
+ const safeUs = us.replace(/[\\/:*?"<>|]/g, "");
4167
+ const file = path4.join(base, "json", `${safeUs}.json`);
4168
+ if (safeUs && fs9.existsSync(file)) {
4169
+ const raw = JSON.parse(fs9.readFileSync(file, "utf8"));
4170
+ const lv = Number(raw?.lv);
4171
+ if (Number.isFinite(lv)) return lv;
4172
+ }
4173
+ }
4174
+ } catch {
4175
+ }
4176
+ if (wsLv === null || wsLv === void 0 || wsLv === "") return null;
4177
+ const n = Number(wsLv);
4178
+ return Number.isFinite(n) ? n : null;
4179
+ }
4180
+ __name(resolveUserLv, "resolveUserLv");
4069
4181
  function getSecurity($asai) {
4070
4182
  return $asai?.hostconfig?.security || {};
4071
4183
  }
@@ -4229,10 +4341,7 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
4229
4341
  if (hostcfg.ckws) {
4230
4342
  const existingWs = $asai.connectionsws?.[hostdir]?.[us];
4231
4343
  if (existingWs) {
4232
- const cleared = await clearStaleWsIfDead($asai, hostdir, us, existingWs);
4233
- if (!cleared) {
4234
- kickUserWs($asai, us, hostdir);
4235
- }
4344
+ await clearStaleWsIfDead($asai, hostdir, us, existingWs);
4236
4345
  }
4237
4346
  }
4238
4347
  const quotaCheck = await canLogin($asai, us, hostdir);
@@ -4279,6 +4388,56 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
4279
4388
  return sendFail(res, err?.message || err, opt);
4280
4389
  }
4281
4390
  }
4391
+ if (url.startsWith("/api/user/login/force/")) {
4392
+ try {
4393
+ const us = sanitizeString(String(req?.Userinfo?.[0] || ""), 64);
4394
+ if (!us) return sendErr(res, "AUTH_MISSING", void 0, opt);
4395
+ const myLv = Number(req?.Userinfo?.[1] ?? 0);
4396
+ const targetUs = sanitizeString(String(body?.targetUs || body?.us || ""), 64);
4397
+ const kicked = [];
4398
+ if (isUserOnlineOnWs($asai, us, hostdir)) {
4399
+ await kickUserWs($asai, us, hostdir);
4400
+ kicked.push(us);
4401
+ }
4402
+ const onlineRaw = listOnlineWsEntries($asai, hostdir).filter((e) => e.us !== us);
4403
+ const online = await Promise.all(
4404
+ onlineRaw.map(async (e) => ({
4405
+ us: e.us,
4406
+ lv: await resolveUserLv($asai, store, e.us, e.lv)
4407
+ }))
4408
+ );
4409
+ const candidates = targetUs ? online.filter((e) => e.us === targetUs) : online;
4410
+ for (const entry of candidates) {
4411
+ const entryLv = entry.lv == null ? NaN : Number(entry.lv);
4412
+ if (!Number.isFinite(entryLv) || myLv < entryLv) {
4413
+ if (targetUs) {
4414
+ return sendErr(res, "AUTH_FORBIDDEN", void 0, opt);
4415
+ }
4416
+ continue;
4417
+ }
4418
+ await kickUserWs($asai, entry.us, hostdir);
4419
+ kicked.push(entry.us);
4420
+ }
4421
+ if (!kicked.length && online.length) {
4422
+ return sendErr(res, "AUTH_FORBIDDEN", void 0, opt);
4423
+ }
4424
+ logSecurityEvent($asai, {
4425
+ type: "AUTH_SUCCESS",
4426
+ path: url,
4427
+ method: "POST",
4428
+ user: us,
4429
+ ip,
4430
+ detail: `force_login_kick:${kicked.join(",") || "none"}`
4431
+ });
4432
+ return sendSuccess(
4433
+ res,
4434
+ { ok: true, us, kicked, online: listOnlineWsEntries($asai, hostdir) },
4435
+ opt
4436
+ );
4437
+ } catch (err) {
4438
+ return sendFail(res, err?.message || err, opt);
4439
+ }
4440
+ }
4282
4441
  if (url.startsWith("/api/user/login/admin/kick/")) {
4283
4442
  if (!await checkAdmin(req, url, getPermissionThreshold($asai, "adminWrite"))) {
4284
4443
  return sendErr(res, "AUTH_FORBIDDEN", void 0, opt);
@@ -4292,7 +4451,7 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
4292
4451
  if (!isSessionUserOnline($asai, target.us, hostdir)) {
4293
4452
  return sendErr(res, "SESSION_NOT_ONLINE", void 0, opt);
4294
4453
  }
4295
- kickUserWs($asai, target.us, hostdir);
4454
+ await kickUserWs($asai, target.us, hostdir);
4296
4455
  await store.removeSession(sessionId);
4297
4456
  const operator = String(req?.Userinfo?.[0] || "");
4298
4457
  await logSessionTerminate($asai, {
@@ -4491,6 +4650,39 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
4491
4650
  await loadUserLevelConfig($asai);
4492
4651
  return sendSuccess(res, getPublicSecurityConfig($asai, lang), opt);
4493
4652
  }
4653
+ if (url.startsWith("/api/user/login/online/")) {
4654
+ try {
4655
+ const us = sanitizeString(String(req?.Userinfo?.[0] || ""), 64);
4656
+ if (!us) return sendErr(res, "AUTH_MISSING", void 0, opt);
4657
+ const rawOnline = listOnlineWsEntries($asai, hostdir);
4658
+ const online = await Promise.all(
4659
+ rawOnline.map(async (e) => ({
4660
+ us: e.us,
4661
+ lv: await resolveUserLv($asai, store, e.us, e.lv)
4662
+ }))
4663
+ );
4664
+ const rawMy = req?.Userinfo?.[1];
4665
+ const wsMyLv = rawMy === null || rawMy === void 0 || rawMy === "" ? null : Number(rawMy);
4666
+ const myLvOut = await resolveUserLv(
4667
+ $asai,
4668
+ store,
4669
+ us,
4670
+ Number.isFinite(wsMyLv) ? wsMyLv : null
4671
+ );
4672
+ return sendSuccess(
4673
+ res,
4674
+ {
4675
+ us,
4676
+ lv: myLvOut,
4677
+ online,
4678
+ maxonline: $asai.getHost?.(hostdir)?.maxonline ?? 100
4679
+ },
4680
+ opt
4681
+ );
4682
+ } catch (err) {
4683
+ return sendFail(res, err?.message || err, opt);
4684
+ }
4685
+ }
4494
4686
  if (url.startsWith("/api/user/login/admin/levels/")) {
4495
4687
  if (!await checkAdmin(req, url, getPermissionThreshold($asai, "adminRead"))) {
4496
4688
  return sendErr(res, "AUTH_FORBIDDEN", void 0, opt);
@@ -4613,10 +4805,10 @@ var As_default = {
4613
4805
  return relativePath;
4614
4806
  },
4615
4807
  //创建多层文件夹 同步
4616
- ensureDir(fs11, relativePath) {
4808
+ ensureDir(fs12, relativePath) {
4617
4809
  const absPathDir = this.toDirPath(relativePath);
4618
4810
  const absPath = this.toAbsolutePath(relativePath);
4619
- if (!fs11.existsSync(absPath)) {
4811
+ if (!fs12.existsSync(absPath)) {
4620
4812
  let pathTmp = "";
4621
4813
  absPathDir.split("/").forEach((el) => {
4622
4814
  if (el) {
@@ -4624,8 +4816,8 @@ var As_default = {
4624
4816
  pathTmp += "/";
4625
4817
  }
4626
4818
  pathTmp += el;
4627
- if (!fs11.existsSync(pathTmp)) {
4628
- if (!fs11.mkdirSync(pathTmp, { recursive: true })) {
4819
+ if (!fs12.existsSync(pathTmp)) {
4820
+ if (!fs12.mkdirSync(pathTmp, { recursive: true })) {
4629
4821
  return absPath;
4630
4822
  }
4631
4823
  }
@@ -4634,10 +4826,10 @@ var As_default = {
4634
4826
  }
4635
4827
  return absPath;
4636
4828
  },
4637
- makeDir(fs11, filePaths) {
4829
+ makeDir(fs12, filePaths) {
4638
4830
  return new Promise((resolve6, reject) => {
4639
4831
  try {
4640
- const newPath = this.ensureDir(fs11, filePaths);
4832
+ const newPath = this.ensureDir(fs12, filePaths);
4641
4833
  resolve6(newPath);
4642
4834
  } catch (err) {
4643
4835
  reject(err);
@@ -5234,7 +5426,7 @@ var AsDb_default = /* @__PURE__ */ __name(($asai, cfg) => {
5234
5426
  }, "default");
5235
5427
 
5236
5428
  // src/sysserver/lib/common/reqWork.ts
5237
- import * as path4 from "path";
5429
+ import * as path5 from "path";
5238
5430
  var reqWork_default = /* @__PURE__ */ __name(($asai) => {
5239
5431
  function hostDefault2(mod) {
5240
5432
  if (mod && typeof mod === "object" && "default" in mod) {
@@ -5250,16 +5442,16 @@ var reqWork_default = /* @__PURE__ */ __name(($asai) => {
5250
5442
  throw new Error("Missing path");
5251
5443
  }
5252
5444
  const decoded = decodeURIComponent(String(rawPath));
5253
- if (path4.isAbsolute(decoded)) {
5254
- return path4.normalize(decoded);
5445
+ if (path5.isAbsolute(decoded)) {
5446
+ return path5.normalize(decoded);
5255
5447
  }
5256
5448
  const rel = decoded.replace(/^\.(\/)?/, "");
5257
5449
  const serverRoot = $asai?.serverRoot;
5258
5450
  if (serverRoot && rel) {
5259
- const absRoot = path4.resolve(serverRoot);
5260
- const abs = path4.resolve(absRoot, rel);
5261
- const relCheck = path4.relative(absRoot, abs);
5262
- if (relCheck === "" || !relCheck.startsWith("..") && !path4.isAbsolute(relCheck)) {
5451
+ const absRoot = path5.resolve(serverRoot);
5452
+ const abs = path5.resolve(absRoot, rel);
5453
+ const relCheck = path5.relative(absRoot, abs);
5454
+ if (relCheck === "" || !relCheck.startsWith("..") && !path5.isAbsolute(relCheck)) {
5263
5455
  return abs;
5264
5456
  }
5265
5457
  }
@@ -5319,18 +5511,18 @@ __export(AsFs_exports, {
5319
5511
  write: () => write,
5320
5512
  writeJson: () => writeJson
5321
5513
  });
5322
- import fs9 from "fs/promises";
5323
- import path5 from "path";
5514
+ import fs10 from "fs/promises";
5515
+ import path6 from "path";
5324
5516
  async function ensureFile(filePath) {
5325
- const normalized = path5.normalize(filePath);
5326
- await fs9.mkdir(path5.dirname(normalized), { recursive: true });
5327
- const fh = await fs9.open(normalized, "a");
5517
+ const normalized = path6.normalize(filePath);
5518
+ await fs10.mkdir(path6.dirname(normalized), { recursive: true });
5519
+ const fh = await fs10.open(normalized, "a");
5328
5520
  await fh.close();
5329
5521
  }
5330
5522
  __name(ensureFile, "ensureFile");
5331
5523
  async function pathExists(filePath) {
5332
5524
  try {
5333
- await fs9.access(path5.normalize(filePath));
5525
+ await fs10.access(path6.normalize(filePath));
5334
5526
  return true;
5335
5527
  } catch {
5336
5528
  return false;
@@ -5338,7 +5530,7 @@ async function pathExists(filePath) {
5338
5530
  }
5339
5531
  __name(pathExists, "pathExists");
5340
5532
  async function readJson(filePath) {
5341
- const raw = await fs9.readFile(path5.normalize(filePath), "utf8");
5533
+ const raw = await fs10.readFile(path6.normalize(filePath), "utf8");
5342
5534
  return JSON.parse(raw);
5343
5535
  }
5344
5536
  __name(readJson, "readJson");
@@ -5349,34 +5541,34 @@ function resolvePathUnderBase(baseDir, relPath) {
5349
5541
  if (relPath.includes("\0")) {
5350
5542
  throw new Error("Invalid path");
5351
5543
  }
5352
- const base = path5.resolve(baseDir);
5353
- const target = path5.resolve(base, relPath.replace(/\\/g, "/"));
5354
- const rel = path5.relative(base, target);
5355
- if (rel.startsWith("..") || path5.isAbsolute(rel)) {
5544
+ const base = path6.resolve(baseDir);
5545
+ const target = path6.resolve(base, relPath.replace(/\\/g, "/"));
5546
+ const rel = path6.relative(base, target);
5547
+ if (rel.startsWith("..") || path6.isAbsolute(rel)) {
5356
5548
  throw new Error("Path not allowed");
5357
5549
  }
5358
5550
  return target;
5359
5551
  }
5360
5552
  __name(resolvePathUnderBase, "resolvePathUnderBase");
5361
5553
  async function readLogDir(dir) {
5362
- const resolved = path5.resolve(dir);
5363
- const stats = await fs9.stat(resolved);
5554
+ const resolved = path6.resolve(dir);
5555
+ const stats = await fs10.stat(resolved);
5364
5556
  if (!stats.isDirectory()) {
5365
5557
  throw new Error("Not a directory");
5366
5558
  }
5367
- const dirName = path5.basename(resolved);
5368
- const entries = await fs9.readdir(resolved, { withFileTypes: true });
5559
+ const dirName = path6.basename(resolved);
5560
+ const entries = await fs10.readdir(resolved, { withFileTypes: true });
5369
5561
  const children = await Promise.all(
5370
5562
  entries.map(async (entry) => {
5371
- const fullPath = path5.join(resolved, entry.name);
5372
- const st = await fs9.stat(fullPath);
5563
+ const fullPath = path6.join(resolved, entry.name);
5564
+ const st = await fs10.stat(fullPath);
5373
5565
  const dot = entry.name.lastIndexOf(".");
5374
5566
  const type = entry.isDirectory() ? "dir" : dot > -1 ? entry.name.substring(dot) : "file";
5375
5567
  return {
5376
5568
  name: entry.name,
5377
5569
  size: st.size,
5378
5570
  time: st.mtime.toISOString(),
5379
- path: path5.normalize(fullPath),
5571
+ path: path6.normalize(fullPath),
5380
5572
  type,
5381
5573
  created: st.birthtime.toISOString(),
5382
5574
  permissions: st.mode.toString(8).slice(-3)
@@ -5387,7 +5579,7 @@ async function readLogDir(dir) {
5387
5579
  name: dirName,
5388
5580
  size: stats.size,
5389
5581
  time: stats.mtime.toISOString(),
5390
- path: path5.normalize(resolved),
5582
+ path: path6.normalize(resolved),
5391
5583
  type: "dir",
5392
5584
  created: stats.birthtime.toISOString(),
5393
5585
  permissions: stats.mode.toString(8).slice(-3),
@@ -5397,7 +5589,7 @@ async function readLogDir(dir) {
5397
5589
  __name(readLogDir, "readLogDir");
5398
5590
  async function removeFile(filePath) {
5399
5591
  try {
5400
- await fs9.unlink(path5.normalize(filePath));
5592
+ await fs10.unlink(path6.normalize(filePath));
5401
5593
  } catch (err) {
5402
5594
  if (err?.code !== "ENOENT") throw err;
5403
5595
  }
@@ -5405,13 +5597,13 @@ async function removeFile(filePath) {
5405
5597
  __name(removeFile, "removeFile");
5406
5598
  var remove = removeFile;
5407
5599
  async function write(filePath, content) {
5408
- const normalized = path5.normalize(filePath);
5409
- await fs9.mkdir(path5.dirname(normalized), { recursive: true });
5410
- await fs9.writeFile(normalized, content, "utf8");
5600
+ const normalized = path6.normalize(filePath);
5601
+ await fs10.mkdir(path6.dirname(normalized), { recursive: true });
5602
+ await fs10.writeFile(normalized, content, "utf8");
5411
5603
  }
5412
5604
  __name(write, "write");
5413
5605
  async function read(filePath) {
5414
- return fs9.readFile(path5.normalize(filePath), "utf8");
5606
+ return fs10.readFile(path6.normalize(filePath), "utf8");
5415
5607
  }
5416
5608
  __name(read, "read");
5417
5609
  async function writeJson(filePath, data) {
@@ -5447,7 +5639,7 @@ __export(common_exports, {
5447
5639
  toSetObject: () => toSetObject,
5448
5640
  toWherePairs: () => toWherePairs
5449
5641
  });
5450
- import * as path6 from "path";
5642
+ import * as path7 from "path";
5451
5643
  function promisifySqlDb(query, sql) {
5452
5644
  return new Promise((resolve6, reject) => {
5453
5645
  query(sql, (err, result) => {
@@ -5512,11 +5704,11 @@ function toInsertRows(sql) {
5512
5704
  }
5513
5705
  __name(toInsertRows, "toInsertRows");
5514
5706
  function isPathInside(baseDir, targetPath) {
5515
- const base = path6.resolve(baseDir);
5516
- const target = path6.resolve(targetPath);
5517
- const relative5 = path6.relative(base, target);
5707
+ const base = path7.resolve(baseDir);
5708
+ const target = path7.resolve(targetPath);
5709
+ const relative5 = path7.relative(base, target);
5518
5710
  if (!relative5) return true;
5519
- return !relative5.startsWith("..") && !path6.isAbsolute(relative5);
5711
+ return !relative5.startsWith("..") && !path7.isAbsolute(relative5);
5520
5712
  }
5521
5713
  __name(isPathInside, "isPathInside");
5522
5714
  function joinTablePath(table, ...segments) {
@@ -5697,8 +5889,8 @@ var Sql_default = {
5697
5889
  };
5698
5890
 
5699
5891
  // src/sysserver/db/src/file/Index.ts
5700
- import * as fs10 from "fs";
5701
- import * as path7 from "path";
5892
+ import * as fs11 from "fs";
5893
+ import * as path8 from "path";
5702
5894
  function dbDriver(opt = {}) {
5703
5895
  const { Sql, DbCommon } = opt;
5704
5896
  const { isPathInside: isPathInside2, joinTablePath: joinTablePath2 } = DbCommon;
@@ -5712,20 +5904,20 @@ function dbDriver(opt = {}) {
5712
5904
  this.fileDel = (config.del || 0) !== 0;
5713
5905
  this.createDir = config.create || false;
5714
5906
  const root = config.$asai?.serverRoot || getPrimaryServerRoot();
5715
- this.baseDirAbs = path7.resolve(root, config.dir);
5907
+ this.baseDirAbs = path8.resolve(root, config.dir);
5716
5908
  }
5717
5909
  dbLog(...args) {
5718
5910
  (this.config?.$asai?.$log || console.log)("FILE", ...args);
5719
5911
  }
5720
5912
  checkFile(file) {
5721
- return fs10.existsSync(this.getAbsPath(file, false));
5913
+ return fs11.existsSync(this.getAbsPath(file, false));
5722
5914
  }
5723
5915
  /** 解析相对路径并限制在数据根目录内,防止路径穿越(跨平台) */
5724
5916
  resolvePath(relPath) {
5725
5917
  if (!relPath) {
5726
5918
  return this.baseDirAbs;
5727
5919
  }
5728
- const abs = path7.isAbsolute(relPath) ? path7.normalize(relPath) : path7.resolve(this.baseDirAbs, relPath);
5920
+ const abs = path8.isAbsolute(relPath) ? path8.normalize(relPath) : path8.resolve(this.baseDirAbs, relPath);
5729
5921
  if (!isPathInside2(this.baseDirAbs, abs)) {
5730
5922
  throw new Error("Path not allowed");
5731
5923
  }
@@ -5739,15 +5931,15 @@ function dbDriver(opt = {}) {
5739
5931
  if (!relPath) {
5740
5932
  return "";
5741
5933
  }
5742
- return path7.isAbsolute(relPath) ? path7.relative(this.baseDirAbs, relPath) : relPath;
5934
+ return path8.isAbsolute(relPath) ? path8.relative(this.baseDirAbs, relPath) : relPath;
5743
5935
  }
5744
5936
  ensureDir(relPath) {
5745
5937
  const relative5 = this.normalizeRelPath(relPath);
5746
- const dirPath = path7.dirname(relative5);
5938
+ const dirPath = path8.dirname(relative5);
5747
5939
  const absDir = this.resolvePath(dirPath === "." ? "" : dirPath);
5748
- if (this.createDir && !fs10.existsSync(absDir)) {
5940
+ if (this.createDir && !fs11.existsSync(absDir)) {
5749
5941
  this.dbLog("mkdir", "[MKDIR]", absDir);
5750
- fs10.mkdirSync(absDir, { recursive: true });
5942
+ fs11.mkdirSync(absDir, { recursive: true });
5751
5943
  }
5752
5944
  return absDir;
5753
5945
  }
@@ -5763,34 +5955,34 @@ function dbDriver(opt = {}) {
5763
5955
  }
5764
5956
  deldir(relativePath) {
5765
5957
  const absPath = this.resolvePath(relativePath);
5766
- if (!fs10.existsSync(absPath)) {
5958
+ if (!fs11.existsSync(absPath)) {
5767
5959
  return;
5768
5960
  }
5769
5961
  this.dbLog("delete", "[RMDIR]", absPath);
5770
- const entries = fs10.readdirSync(absPath, { withFileTypes: true });
5962
+ const entries = fs11.readdirSync(absPath, { withFileTypes: true });
5771
5963
  for (const entry of entries) {
5772
- const current = path7.join(absPath, entry.name);
5964
+ const current = path8.join(absPath, entry.name);
5773
5965
  if (entry.isDirectory()) {
5774
- this.deldir(path7.join(relativePath, entry.name));
5966
+ this.deldir(path8.join(relativePath, entry.name));
5775
5967
  } else {
5776
5968
  this.dbLog("delete", "[UNLINK]", current);
5777
- fs10.unlinkSync(current);
5969
+ fs11.unlinkSync(current);
5778
5970
  }
5779
5971
  }
5780
- fs10.rmdirSync(absPath);
5972
+ fs11.rmdirSync(absPath);
5781
5973
  }
5782
5974
  getReadPath(file, sql) {
5783
5975
  const candidates = [];
5784
5976
  if (sql?.filename) {
5785
- candidates.push({ dir: path7.join(file, sql.filename) });
5786
- candidates.push({ dir: path7.join(file + ".delete" + this.fileExt, sql.filename) });
5977
+ candidates.push({ dir: path8.join(file, sql.filename) });
5978
+ candidates.push({ dir: path8.join(file + ".delete" + this.fileExt, sql.filename) });
5787
5979
  } else {
5788
5980
  candidates.push({ dir: file });
5789
5981
  candidates.push({ dir: file + this.fileExt + ".delete" });
5790
5982
  }
5791
5983
  for (const candidate of candidates) {
5792
5984
  const abs = this.getAbsPath(candidate.dir, false);
5793
- if (fs10.existsSync(abs)) {
5985
+ if (fs11.existsSync(abs)) {
5794
5986
  return { path: abs, dir: candidate.dir };
5795
5987
  }
5796
5988
  }
@@ -5802,13 +5994,13 @@ function dbDriver(opt = {}) {
5802
5994
  return { path: "", dir: read2.dir };
5803
5995
  }
5804
5996
  if (sql?.filename) {
5805
- return { path: path7.dirname(read2.path), dir: path7.dirname(read2.dir) };
5997
+ return { path: path8.dirname(read2.path), dir: path8.dirname(read2.dir) };
5806
5998
  }
5807
5999
  return { path: read2.path, dir: read2.dir };
5808
6000
  }
5809
6001
  getNewPath(file, sql) {
5810
6002
  if (sql?.filename) {
5811
- return this.getAbsPath(path7.join(file, sql.filename), true);
6003
+ return this.getAbsPath(path8.join(file, sql.filename), true);
5812
6004
  }
5813
6005
  return this.getAbsPath(file, true);
5814
6006
  }
@@ -5816,7 +6008,7 @@ function dbDriver(opt = {}) {
5816
6008
  try {
5817
6009
  const filePath = sql?.type === "insert" ? this.getNewPath(file, sql) : this.getReadPath(file, sql).path || this.getNewPath(file, sql);
5818
6010
  this.dbLog("write", "[WRITE]", filePath);
5819
- fs10.writeFileSync(filePath, data, "utf8");
6011
+ fs11.writeFileSync(filePath, data, "utf8");
5820
6012
  return "ok";
5821
6013
  } catch (e) {
5822
6014
  this.dbLog("write failed", "[WRITE ERROR]", e);
@@ -5830,7 +6022,7 @@ function dbDriver(opt = {}) {
5830
6022
  return { error: "empty" };
5831
6023
  }
5832
6024
  this.dbLog("read", "[READ]", read2.path);
5833
- const data = fs10.readFileSync(read2.path, { encoding }) || "";
6025
+ const data = fs11.readFileSync(read2.path, { encoding }) || "";
5834
6026
  const fileName = this.fileExt && read2.dir.endsWith(this.fileExt) ? read2.dir : read2.dir + this.fileExt;
5835
6027
  return [{ data, file: fileName }];
5836
6028
  } catch (e) {
@@ -5847,12 +6039,12 @@ function dbDriver(opt = {}) {
5847
6039
  if (this.fileDel && sql?.deltrue) {
5848
6040
  const target = del.path + ".delete" + this.fileExt;
5849
6041
  this.dbLog("soft delete", "[SOFT DELETE]", `${del.path} -> ${target}`);
5850
- fs10.renameSync(del.path, target);
6042
+ fs11.renameSync(del.path, target);
5851
6043
  } else if (sql?.filename) {
5852
6044
  this.deldir(del.dir);
5853
6045
  } else {
5854
6046
  this.dbLog("delete", "[DELETE]", del.path);
5855
- fs10.unlinkSync(del.path);
6047
+ fs11.unlinkSync(del.path);
5856
6048
  }
5857
6049
  return "ok";
5858
6050
  } catch (e) {
@@ -5871,11 +6063,11 @@ function dbDriver(opt = {}) {
5871
6063
  const original = del.path.replace(new RegExp(`.delete${extPattern}$`), "");
5872
6064
  if (original !== del.path) {
5873
6065
  this.dbLog("restore", "[RESTORE]", `${del.path} -> ${original}`);
5874
- fs10.renameSync(del.path, original);
6066
+ fs11.renameSync(del.path, original);
5875
6067
  }
5876
6068
  } else {
5877
6069
  this.dbLog("force delete", "[FORCE DELETE]", del.path);
5878
- fs10.unlinkSync(del.path);
6070
+ fs11.unlinkSync(del.path);
5879
6071
  }
5880
6072
  return "ok";
5881
6073
  } catch (e) {
@@ -5889,7 +6081,7 @@ function dbDriver(opt = {}) {
5889
6081
  this.dbLog("list", "[LIST]", absDir);
5890
6082
  let entries;
5891
6083
  try {
5892
- entries = fs10.readdirSync(absDir, { withFileTypes: true });
6084
+ entries = fs11.readdirSync(absDir, { withFileTypes: true });
5893
6085
  } catch {
5894
6086
  return sql?.list ? { open: [], delete: [] } : [];
5895
6087
  }
@@ -5901,9 +6093,9 @@ function dbDriver(opt = {}) {
5901
6093
  const isDelete = fileName.endsWith(deleteSuffix);
5902
6094
  const baseName = isDelete ? fileName.slice(0, -deleteSuffix.length) : fileName;
5903
6095
  if (this.fileExt && !baseName.endsWith(this.fileExt)) continue;
5904
- const fullPath = path7.join(absDir, fileName);
6096
+ const fullPath = path8.join(absDir, fileName);
5905
6097
  try {
5906
- const data = fs10.readFileSync(fullPath, "utf8");
6098
+ const data = fs11.readFileSync(fullPath, "utf8");
5907
6099
  const entryData = { data, file: baseName };
5908
6100
  if (isDelete) {
5909
6101
  if (sql?.list) result.delete.push(entryData);
@@ -6669,7 +6861,7 @@ var initAsaiHostNodejs2 = {
6669
6861
  createTransportDataHandler,
6670
6862
  wireTransportConnectionLogs
6671
6863
  };
6672
- var PLUGIN_VERSION = true ? "0.0.9" : "0.0.1";
6864
+ var PLUGIN_VERSION = true ? "0.0.10" : "0.0.1";
6673
6865
  var index_default = initAsaiHostNodejs2;
6674
6866
  export {
6675
6867
  Index_default as AsaiCommon,