asaihost-nodejs 0.0.6 → 0.0.9

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.
@@ -399,7 +399,7 @@ var LogBuffer = class _LogBuffer {
399
399
  }
400
400
  };
401
401
  function saveLog($asai, src, logstr) {
402
- return new Promise((resolve5, reject) => {
402
+ return new Promise((resolve6, reject) => {
403
403
  try {
404
404
  const bufferKey = `${$asai.id || "default"}_${src}`;
405
405
  let logBuffer = logBuffers.get(bufferKey);
@@ -408,7 +408,7 @@ function saveLog($asai, src, logstr) {
408
408
  logBuffers.set(bufferKey, logBuffer);
409
409
  }
410
410
  logBuffer.add(logstr);
411
- resolve5();
411
+ resolve6();
412
412
  } catch (err) {
413
413
  console.error(err);
414
414
  reject(err);
@@ -915,7 +915,7 @@ import * as https from "https";
915
915
  import { WebSocketServer } from "ws";
916
916
 
917
917
  // src/ws/auth.ts
918
- import { WebSocket } from "ws";
918
+ import { WebSocket as WebSocket2 } from "ws";
919
919
 
920
920
  // src/utils/userinfo.ts
921
921
  function deUserInfoWithAsai(userinfo, $asai) {
@@ -940,6 +940,79 @@ function deUserInfoWithAsai(userinfo, $asai) {
940
940
  }
941
941
  __name(deUserInfoWithAsai, "deUserInfoWithAsai");
942
942
 
943
+ // src/ws/ws-probe.ts
944
+ import { WebSocket } from "ws";
945
+ function isWsStillActive(ws2) {
946
+ if (!ws2) return false;
947
+ const state = ws2.readyState;
948
+ return state === WebSocket.CONNECTING || state === WebSocket.OPEN;
949
+ }
950
+ __name(isWsStillActive, "isWsStillActive");
951
+ function getWsProbeTimeoutMs($asai) {
952
+ return $asai?.hostconfig?.tm?.d || 3e3;
953
+ }
954
+ __name(getWsProbeTimeoutMs, "getWsProbeTimeoutMs");
955
+ function probeWsAlive($asai, wsclient, timeoutMs) {
956
+ const waitMs = timeoutMs ?? getWsProbeTimeoutMs($asai);
957
+ return new Promise((resolve6) => {
958
+ if (!wsclient || !isWsStillActive(wsclient)) {
959
+ resolve6(false);
960
+ return;
961
+ }
962
+ let settled = false;
963
+ const finish = /* @__PURE__ */ __name((alive) => {
964
+ if (settled) return;
965
+ settled = true;
966
+ clearTimeout(timer);
967
+ try {
968
+ wsclient.off?.("message", onMessage);
969
+ } catch {
970
+ }
971
+ resolve6(alive);
972
+ }, "finish");
973
+ const onMessage = /* @__PURE__ */ __name(() => finish(true), "onMessage");
974
+ const timer = setTimeout(() => finish(false), waitMs);
975
+ try {
976
+ wsclient.once?.("message", onMessage);
977
+ wsclient.send(JSON.stringify({ ty: "publish/web", db: "pong" }));
978
+ } catch {
979
+ finish(false);
980
+ }
981
+ });
982
+ }
983
+ __name(probeWsAlive, "probeWsAlive");
984
+ function terminateWs(wsclient) {
985
+ if (!wsclient) return;
986
+ try {
987
+ wsclient.terminate?.();
988
+ } catch {
989
+ }
990
+ try {
991
+ wsclient.close?.();
992
+ } catch {
993
+ }
994
+ }
995
+ __name(terminateWs, "terminateWs");
996
+ async function clearStaleWsIfDead($asai, hostdir, us, existingWs) {
997
+ if (!us || us === "asai") return true;
998
+ const map = $asai.connectionsws?.[hostdir];
999
+ if (!existingWs) {
1000
+ if (map?.[us]) delete map[us];
1001
+ return true;
1002
+ }
1003
+ if (!isWsStillActive(existingWs)) {
1004
+ terminateWs(existingWs);
1005
+ if (map?.[us] === existingWs) delete map[us];
1006
+ return true;
1007
+ }
1008
+ const alive = await probeWsAlive($asai, existingWs);
1009
+ if (alive) return false;
1010
+ terminateWs(existingWs);
1011
+ if (map?.[us] === existingWs) delete map[us];
1012
+ return true;
1013
+ }
1014
+ __name(clearStaleWsIfDead, "clearStaleWsIfDead");
1015
+
943
1016
  // src/ws/auth.ts
944
1017
  function getWsUserinfo(req, $asai) {
945
1018
  try {
@@ -1006,12 +1079,6 @@ async function removeSessionOnWsClose($asai, us, token) {
1006
1079
  }
1007
1080
  }
1008
1081
  __name(removeSessionOnWsClose, "removeSessionOnWsClose");
1009
- function isWsStillActive(ws2) {
1010
- if (!ws2) return false;
1011
- const state = ws2.readyState;
1012
- return state === WebSocket.CONNECTING || state === WebSocket.OPEN;
1013
- }
1014
- __name(isWsStillActive, "isWsStillActive");
1015
1082
  function rejectIncomingWs(ws2, db) {
1016
1083
  const payload = JSON.stringify({ ty: "publish/web", db: { exit: 1, type: "err", ...db } });
1017
1084
  try {
@@ -1029,7 +1096,7 @@ function rejectIncomingWs(ws2, db) {
1029
1096
  }
1030
1097
  }
1031
1098
  __name(rejectIncomingWs, "rejectIncomingWs");
1032
- function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
1099
+ async function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
1033
1100
  const [us, , tk] = ws2.Userinfo || [];
1034
1101
  if (!us) {
1035
1102
  ws2.close();
@@ -1053,12 +1120,16 @@ function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
1053
1120
  delete $asai.connectionsws[hostdir][us];
1054
1121
  return true;
1055
1122
  }
1056
- rejectIncomingWs(ws2, {
1057
- lang: "logged",
1058
- langpm: `${us}`,
1059
- con: `${us} logged in!`
1060
- });
1061
- return false;
1123
+ const cleared = await clearStaleWsIfDead($asai, hostdir, us, existingWs);
1124
+ if (!cleared) {
1125
+ rejectIncomingWs(ws2, {
1126
+ lang: "logged",
1127
+ langpm: `${us}`,
1128
+ con: `${us} logged in!`
1129
+ });
1130
+ return false;
1131
+ }
1132
+ return true;
1062
1133
  }
1063
1134
  if (us !== "asai") {
1064
1135
  const connections = $asai.connectionsws[hostdir] || {};
@@ -1109,13 +1180,13 @@ function loginWs($asai, hostdir, ws2, req, hostcfg, wsWorkHandler, logWsMessage)
1109
1180
  if (us && token && !isUserOnlineOnWs($asai, us, hostdir)) {
1110
1181
  void removeSessionOnWsClose($asai, us, String(token));
1111
1182
  }
1112
- if (ws2.readyState !== WebSocket.CLOSED && ws2.readyState !== WebSocket.CLOSING) ws2.close();
1183
+ if (ws2.readyState !== WebSocket2.CLOSED && ws2.readyState !== WebSocket2.CLOSING) ws2.close();
1113
1184
  });
1114
1185
  }
1115
1186
  __name(loginWs, "loginWs");
1116
1187
 
1117
1188
  // src/ws/message.ts
1118
- import { WebSocket as WebSocket2 } from "ws";
1189
+ import { WebSocket as WebSocket3 } from "ws";
1119
1190
  function clearWsServerTasks($asai, hostdir) {
1120
1191
  const tasks = $asai.tasksws[hostdir];
1121
1192
  if (tasks && typeof tasks === "object") {
@@ -1140,7 +1211,7 @@ function attachWssHelpers(wss, $asai) {
1140
1211
  const data = resWsMsg(msg, $asai);
1141
1212
  if (type) {
1142
1213
  wss.clients.forEach((client) => {
1143
- if ((type === 2 || client !== curws) && client.readyState === WebSocket2.OPEN) {
1214
+ if ((type === 2 || client !== curws) && client.readyState === WebSocket3.OPEN) {
1144
1215
  client.send(data);
1145
1216
  }
1146
1217
  });
@@ -1151,7 +1222,7 @@ function attachWssHelpers(wss, $asai) {
1151
1222
  wss.broadws = (msg) => {
1152
1223
  const data = resWsMsg(msg, $asai);
1153
1224
  wss.clients.forEach((client) => {
1154
- if (client?.readyState === WebSocket2.OPEN) client.send(data);
1225
+ if (client?.readyState === WebSocket3.OPEN) client.send(data);
1155
1226
  });
1156
1227
  };
1157
1228
  }
@@ -1331,12 +1402,14 @@ function createWSHost($asai, hostdir) {
1331
1402
  ws2.send(wsmsg, () => ws2.close(1013, "Server busy"));
1332
1403
  return;
1333
1404
  }
1334
- const isOpen = !hostcfg.ckws || isOkWs($asai, hostdir, ws2, req, hostcfg);
1335
- if (isOpen) {
1336
- loginWs($asai, hostdir, ws2, req, hostcfg, wsWorkHandler, logWsMessage);
1337
- } else {
1338
- ws2.close();
1339
- }
1405
+ void (async () => {
1406
+ const isOpen = !hostcfg.ckws || await isOkWs($asai, hostdir, ws2, req, hostcfg);
1407
+ if (isOpen) {
1408
+ loginWs($asai, hostdir, ws2, req, hostcfg, wsWorkHandler, logWsMessage);
1409
+ } else {
1410
+ ws2.close();
1411
+ }
1412
+ })();
1340
1413
  });
1341
1414
  $asai.serversws[hostdir] = wss;
1342
1415
  } catch (err) {
@@ -1460,11 +1533,11 @@ function createSkipChecker($asai) {
1460
1533
  }
1461
1534
  if (exact.size === 0 && prefixes.length === 0) return () => false;
1462
1535
  return (url) => {
1463
- const path7 = normalizeRequestPath(url);
1464
- if (!path7) return false;
1465
- if (exact.has(path7)) return true;
1536
+ const path8 = normalizeRequestPath(url);
1537
+ if (!path8) return false;
1538
+ if (exact.has(path8)) return true;
1466
1539
  for (let i = 0; i < prefixes.length; i++) {
1467
- if (path7.startsWith(prefixes[i])) return true;
1540
+ if (path8.startsWith(prefixes[i])) return true;
1468
1541
  }
1469
1542
  return false;
1470
1543
  };
@@ -2382,11 +2455,11 @@ function getSecurityConfig($asai) {
2382
2455
  return $asai?.hostconfig?.security || {};
2383
2456
  }
2384
2457
  __name(getSecurityConfig, "getSecurityConfig");
2385
- function pathMatchesPattern(path7, pattern) {
2458
+ function pathMatchesPattern(path8, pattern) {
2386
2459
  if (pattern.endsWith("*")) {
2387
- return path7.startsWith(pattern.slice(0, -1));
2460
+ return path8.startsWith(pattern.slice(0, -1));
2388
2461
  }
2389
- return path7 === pattern;
2462
+ return path8 === pattern;
2390
2463
  }
2391
2464
  __name(pathMatchesPattern, "pathMatchesPattern");
2392
2465
  function isAuthSkipped($asai, pathname) {
@@ -2535,7 +2608,7 @@ __name(isApiCorsAllowed, "isApiCorsAllowed");
2535
2608
 
2536
2609
  // src/common/server/Api.ts
2537
2610
  function readRequestBody(req, maxBody) {
2538
- return new Promise((resolve5, reject) => {
2611
+ return new Promise((resolve6, reject) => {
2539
2612
  const chunks = [];
2540
2613
  let received = 0;
2541
2614
  let settled = false;
@@ -2543,7 +2616,7 @@ function readRequestBody(req, maxBody) {
2543
2616
  if (settled) return;
2544
2617
  settled = true;
2545
2618
  if (err) reject(err);
2546
- else resolve5(data ?? "");
2619
+ else resolve6(data ?? "");
2547
2620
  }, "finish");
2548
2621
  req.on("data", (chunk) => {
2549
2622
  if (settled) return;
@@ -2673,83 +2746,95 @@ var Api_default = /* @__PURE__ */ __name(($asai, opt = {}) => {
2673
2746
  const isTextPost = req.method === "POST" && req.url.indexOf("binary") === -1;
2674
2747
  const bodyPromise = isTextPost ? readRequestBody(req, maxBody) : null;
2675
2748
  void (async () => {
2676
- let authResult;
2677
- let rawBody = "";
2678
2749
  try {
2679
- [authResult, rawBody] = await Promise.all([
2680
- validateHttpAuthAsync(req, $asai, pathname),
2681
- bodyPromise ?? Promise.resolve("")
2682
- ]);
2683
- } catch (err) {
2684
- releaseSlot();
2685
- if (err?.message === "PAYLOAD_TOO_LARGE") {
2686
- return sendError(res, 413, "Payload Too Large");
2687
- }
2688
- return sendError(res, 400, err?.message || "Bad Request");
2689
- }
2690
- if (!authResult.ok) {
2691
- releaseSlot();
2692
- return sendAuthError(res, authResult);
2693
- }
2694
- let apiFn = $asai.hostserverapi[apiHandlerKey]?.($asai, query);
2695
- if (!apiFn) {
2696
- return sendError(res, 501, "Not Implemented");
2697
- }
2698
- res.setHeader("Content-Type", "application/json; charset=utf-8");
2699
- $asai.$log("API", req.method, urlParse.pathname, query);
2700
- let qrdata;
2701
- let postdata;
2702
- if (req.url.indexOf("binary") === -1) {
2703
- qrdata = query;
2704
- if (req.method === "POST") {
2705
- try {
2706
- const processed = processPostPayload($asai, req, rawBody, pm);
2707
- postdata = processed.postdata;
2708
- pm = processed.pm;
2709
- isAes = processed.isAes;
2710
- } catch {
2711
- return sendError(res, 400, "AES decrypt failed");
2712
- }
2713
- if (apiFn?.post && typeof apiFn?.post === "function") {
2714
- const optTmp = { qrdata, data: postdata, pm };
2715
- if (isAes || $asai.$lib.aesfns?.allaes) {
2716
- optTmp.resAES = $asai.$lib.aesfns?.resAES;
2717
- }
2718
- apiFn.post(req, res, hostdir, optTmp);
2719
- } else {
2720
- res.end(typeof postdata === "string" ? postdata : JSON.stringify(postdata ?? ""));
2721
- }
2722
- } else if (req.method === "GET") {
2723
- if (apiFn?.get && typeof apiFn?.get === "function") {
2724
- const optTmp = { qrdata, data: qrdata, pm };
2725
- if (isAes || $asai.$lib.aesfns?.allaes) {
2726
- optTmp.resAES = $asai.$lib.aesfns?.resAES;
2727
- }
2728
- apiFn.get(req, res, hostdir, optTmp);
2729
- } else {
2730
- res.end(JSON.stringify(qrdata));
2750
+ let authResult;
2751
+ let rawBody = "";
2752
+ try {
2753
+ [authResult, rawBody] = await Promise.all([
2754
+ validateHttpAuthAsync(req, $asai, pathname),
2755
+ bodyPromise ?? Promise.resolve("")
2756
+ ]);
2757
+ } catch (err) {
2758
+ releaseSlot();
2759
+ if (err?.message === "PAYLOAD_TOO_LARGE") {
2760
+ return sendError(res, 413, "Payload Too Large");
2731
2761
  }
2762
+ return sendError(res, 400, err?.message || "Bad Request");
2732
2763
  }
2733
- } else {
2734
- if (Object.keys(query)?.length) {
2735
- qrdata = query;
2764
+ if (!authResult.ok) {
2765
+ releaseSlot();
2766
+ return sendAuthError(res, authResult);
2736
2767
  }
2737
- if (req.method === "POST") {
2738
- $asai.$lib.AsNodeTool.upBinary(req).then((postdata2) => {
2768
+ let apiFn = $asai.hostserverapi[apiHandlerKey]?.($asai, query);
2769
+ if (!apiFn) {
2770
+ return sendError(res, 501, "Not Implemented");
2771
+ }
2772
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
2773
+ $asai.$log("API", req.method, urlParse.pathname, query);
2774
+ let qrdata;
2775
+ let postdata;
2776
+ if (req.url.indexOf("binary") === -1) {
2777
+ qrdata = query;
2778
+ if (req.method === "POST") {
2779
+ try {
2780
+ const processed = processPostPayload($asai, req, rawBody, pm);
2781
+ postdata = processed.postdata;
2782
+ pm = processed.pm;
2783
+ isAes = processed.isAes;
2784
+ } catch {
2785
+ return sendError(res, 400, "AES decrypt failed");
2786
+ }
2739
2787
  if (apiFn?.post && typeof apiFn?.post === "function") {
2740
- apiFn.post(req, res, hostdir, {
2741
- qrdata,
2742
- data: postdata2
2743
- });
2788
+ const optTmp = { qrdata, data: postdata, pm };
2789
+ if (isAes || $asai.$lib.aesfns?.allaes) {
2790
+ optTmp.resAES = $asai.$lib.aesfns?.resAES;
2791
+ }
2792
+ apiFn.post(req, res, hostdir, optTmp);
2744
2793
  } else {
2745
- res.end(postdata2);
2794
+ res.end(typeof postdata === "string" ? postdata : JSON.stringify(postdata ?? ""));
2746
2795
  }
2747
- }).catch((err) => {
2748
- return sendError(res, 400, "Invalid content type");
2749
- });
2796
+ } else if (req.method === "GET") {
2797
+ if (apiFn?.get && typeof apiFn?.get === "function") {
2798
+ const optTmp = { qrdata, data: qrdata, pm };
2799
+ if (isAes || $asai.$lib.aesfns?.allaes) {
2800
+ optTmp.resAES = $asai.$lib.aesfns?.resAES;
2801
+ }
2802
+ apiFn.get(req, res, hostdir, optTmp);
2803
+ } else {
2804
+ res.end(JSON.stringify(qrdata));
2805
+ }
2806
+ }
2807
+ } else {
2808
+ if (Object.keys(query)?.length) {
2809
+ qrdata = query;
2810
+ }
2811
+ if (req.method === "POST") {
2812
+ $asai.$lib.AsNodeTool.upBinary(req).then((postdata2) => {
2813
+ if (apiFn?.post && typeof apiFn?.post === "function") {
2814
+ apiFn.post(req, res, hostdir, {
2815
+ qrdata,
2816
+ data: postdata2
2817
+ });
2818
+ } else {
2819
+ res.end(postdata2);
2820
+ }
2821
+ }).catch((err) => {
2822
+ return sendError(res, 400, "Invalid content type");
2823
+ });
2824
+ }
2825
+ }
2826
+ } catch (err) {
2827
+ releaseSlot();
2828
+ if (!res.headersSent) {
2829
+ return sendError(res, 500, err?.message || "Internal Server Error");
2750
2830
  }
2751
2831
  }
2752
- })();
2832
+ })().catch((err) => {
2833
+ releaseSlot();
2834
+ if (!res.headersSent) {
2835
+ sendError(res, 500, err?.message || "Internal Server Error");
2836
+ }
2837
+ });
2753
2838
  } else {
2754
2839
  $asai.$log("API", req.method, req.url);
2755
2840
  }
@@ -2883,27 +2968,28 @@ var Sys_default = /* @__PURE__ */ __name(($asai, opt = {}) => {
2883
2968
  }
2884
2969
  const urlPath = (req.url || "").split("?")[0];
2885
2970
  void (async () => {
2886
- const authResult = await validateHttpAuthAsync(req, $asai, urlPath);
2887
- if (!authResult.ok) {
2888
- return sendAuthError(res, authResult);
2889
- }
2890
- const handler = $asai.hostserversys[handlerKey]?.($asai);
2891
- if (!handler) {
2892
- return sendError(res, 500, "Handler Initialization Failed");
2893
- }
2894
- if (handler?.show && typeof handler?.show === "function") {
2895
- handler.show(req, res, hostdir);
2896
- } else {
2897
- res.end("postdata");
2971
+ try {
2972
+ const authResult = await validateHttpAuthAsync(req, $asai, urlPath);
2973
+ if (!authResult.ok) {
2974
+ return sendAuthError(res, authResult);
2975
+ }
2976
+ const handler = $asai.hostserversys[handlerKey]?.($asai);
2977
+ if (!handler) {
2978
+ return sendError(res, 500, "Handler Initialization Failed");
2979
+ }
2980
+ if (handler?.show && typeof handler?.show === "function") {
2981
+ handler.show(req, res, hostdir);
2982
+ } else {
2983
+ res.end("postdata");
2984
+ }
2985
+ } catch (err) {
2986
+ return sendError(res, 500, err?.message || "Internal Server Error");
2898
2987
  }
2899
- if (!handler) {
2900
- res.writeHead(404, {
2901
- "Content-Type": "application/json; charset=utf-8"
2902
- });
2903
- res.end(req.url);
2904
- } else {
2988
+ })().catch((err) => {
2989
+ if (!res.headersSent) {
2990
+ sendError(res, 500, err?.message || "Internal Server Error");
2905
2991
  }
2906
- })();
2992
+ });
2907
2993
  }
2908
2994
  __name(sysWork, "sysWork");
2909
2995
  return { sysWork };
@@ -2922,7 +3008,7 @@ var WsClient_default = /* @__PURE__ */ __name(($asai, wsname) => {
2922
3008
  }
2923
3009
  __name(getKeyByData, "getKeyByData");
2924
3010
  function clientwsInit() {
2925
- return new Promise((resolve5, reject) => {
3011
+ return new Promise((resolve6, reject) => {
2926
3012
  $asai.clientws[wsname].socket = new $asai.asaiWs(
2927
3013
  $asai.command.commandconfig.clientws[wsname]
2928
3014
  );
@@ -2952,7 +3038,7 @@ var WsClient_default = /* @__PURE__ */ __name(($asai, wsname) => {
2952
3038
  `${wsname}=${$asai.command.commandconfig.clientws[wsname]} is open.`
2953
3039
  );
2954
3040
  $asai.clientws[wsname].open = 1;
2955
- resolve5();
3041
+ resolve6();
2956
3042
  };
2957
3043
  });
2958
3044
  }
@@ -2982,7 +3068,7 @@ var WsClient_default = /* @__PURE__ */ __name(($asai, wsname) => {
2982
3068
  }
2983
3069
  __name(clientwsOnmessage, "clientwsOnmessage");
2984
3070
  function clientwsApi(messageData) {
2985
- return new Promise((resolve5, reject) => {
3071
+ return new Promise((resolve6, reject) => {
2986
3072
  if ($asai.clientws[wsname].open) {
2987
3073
  try {
2988
3074
  const taskOpt = {};
@@ -2994,7 +3080,7 @@ var WsClient_default = /* @__PURE__ */ __name(($asai, wsname) => {
2994
3080
  getKeyByData(messageData),
2995
3081
  {
2996
3082
  callback: /* @__PURE__ */ __name((res) => {
2997
- resolve5(res);
3083
+ resolve6(res);
2998
3084
  }, "callback"),
2999
3085
  ...messageData,
3000
3086
  ...taskOpt
@@ -4140,8 +4226,14 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
4140
4226
  const policy = getPasswordPolicy($asai);
4141
4227
  const mustChange = !!user.mustChangePassword || isPasswordExpired(user.passwordChangedAt, policy.maxAgeDays);
4142
4228
  const hostcfg = $asai.getHost?.(hostdir) || {};
4143
- if (hostcfg.ckws && isUserOnlineOnWs($asai, us, hostdir)) {
4144
- kickUserWs($asai, us, hostdir);
4229
+ if (hostcfg.ckws) {
4230
+ const existingWs = $asai.connectionsws?.[hostdir]?.[us];
4231
+ if (existingWs) {
4232
+ const cleared = await clearStaleWsIfDead($asai, hostdir, us, existingWs);
4233
+ if (!cleared) {
4234
+ kickUserWs($asai, us, hostdir);
4235
+ }
4236
+ }
4145
4237
  }
4146
4238
  const quotaCheck = await canLogin($asai, us, hostdir);
4147
4239
  if (!quotaCheck.ok) {
@@ -4543,10 +4635,10 @@ var As_default = {
4543
4635
  return absPath;
4544
4636
  },
4545
4637
  makeDir(fs11, filePaths) {
4546
- return new Promise((resolve5, reject) => {
4638
+ return new Promise((resolve6, reject) => {
4547
4639
  try {
4548
4640
  const newPath = this.ensureDir(fs11, filePaths);
4549
- resolve5(newPath);
4641
+ resolve6(newPath);
4550
4642
  } catch (err) {
4551
4643
  reject(err);
4552
4644
  }
@@ -5142,6 +5234,7 @@ var AsDb_default = /* @__PURE__ */ __name(($asai, cfg) => {
5142
5234
  }, "default");
5143
5235
 
5144
5236
  // src/sysserver/lib/common/reqWork.ts
5237
+ import * as path4 from "path";
5145
5238
  var reqWork_default = /* @__PURE__ */ __name(($asai) => {
5146
5239
  function hostDefault2(mod) {
5147
5240
  if (mod && typeof mod === "object" && "default" in mod) {
@@ -5157,6 +5250,19 @@ var reqWork_default = /* @__PURE__ */ __name(($asai) => {
5157
5250
  throw new Error("Missing path");
5158
5251
  }
5159
5252
  const decoded = decodeURIComponent(String(rawPath));
5253
+ if (path4.isAbsolute(decoded)) {
5254
+ return path4.normalize(decoded);
5255
+ }
5256
+ const rel = decoded.replace(/^\.(\/)?/, "");
5257
+ const serverRoot = $asai?.serverRoot;
5258
+ 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)) {
5263
+ return abs;
5264
+ }
5265
+ }
5160
5266
  const roots = $asai?.asaihost?.getHostAllowedRoots?.($asai?.hostconfig) || [];
5161
5267
  if (!roots.length) {
5162
5268
  const fallback = $asai?.serverRoot || process.cwd();
@@ -5214,17 +5320,17 @@ __export(AsFs_exports, {
5214
5320
  writeJson: () => writeJson
5215
5321
  });
5216
5322
  import fs9 from "fs/promises";
5217
- import path4 from "path";
5323
+ import path5 from "path";
5218
5324
  async function ensureFile(filePath) {
5219
- const normalized = path4.normalize(filePath);
5220
- await fs9.mkdir(path4.dirname(normalized), { recursive: true });
5325
+ const normalized = path5.normalize(filePath);
5326
+ await fs9.mkdir(path5.dirname(normalized), { recursive: true });
5221
5327
  const fh = await fs9.open(normalized, "a");
5222
5328
  await fh.close();
5223
5329
  }
5224
5330
  __name(ensureFile, "ensureFile");
5225
5331
  async function pathExists(filePath) {
5226
5332
  try {
5227
- await fs9.access(path4.normalize(filePath));
5333
+ await fs9.access(path5.normalize(filePath));
5228
5334
  return true;
5229
5335
  } catch {
5230
5336
  return false;
@@ -5232,7 +5338,7 @@ async function pathExists(filePath) {
5232
5338
  }
5233
5339
  __name(pathExists, "pathExists");
5234
5340
  async function readJson(filePath) {
5235
- const raw = await fs9.readFile(path4.normalize(filePath), "utf8");
5341
+ const raw = await fs9.readFile(path5.normalize(filePath), "utf8");
5236
5342
  return JSON.parse(raw);
5237
5343
  }
5238
5344
  __name(readJson, "readJson");
@@ -5243,26 +5349,26 @@ function resolvePathUnderBase(baseDir, relPath) {
5243
5349
  if (relPath.includes("\0")) {
5244
5350
  throw new Error("Invalid path");
5245
5351
  }
5246
- const base = path4.resolve(baseDir);
5247
- const target = path4.resolve(base, relPath.replace(/\\/g, "/"));
5248
- const rel = path4.relative(base, target);
5249
- if (rel.startsWith("..") || path4.isAbsolute(rel)) {
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)) {
5250
5356
  throw new Error("Path not allowed");
5251
5357
  }
5252
5358
  return target;
5253
5359
  }
5254
5360
  __name(resolvePathUnderBase, "resolvePathUnderBase");
5255
5361
  async function readLogDir(dir) {
5256
- const resolved = path4.resolve(dir);
5362
+ const resolved = path5.resolve(dir);
5257
5363
  const stats = await fs9.stat(resolved);
5258
5364
  if (!stats.isDirectory()) {
5259
5365
  throw new Error("Not a directory");
5260
5366
  }
5261
- const dirName = path4.basename(resolved);
5367
+ const dirName = path5.basename(resolved);
5262
5368
  const entries = await fs9.readdir(resolved, { withFileTypes: true });
5263
5369
  const children = await Promise.all(
5264
5370
  entries.map(async (entry) => {
5265
- const fullPath = path4.join(resolved, entry.name);
5371
+ const fullPath = path5.join(resolved, entry.name);
5266
5372
  const st = await fs9.stat(fullPath);
5267
5373
  const dot = entry.name.lastIndexOf(".");
5268
5374
  const type = entry.isDirectory() ? "dir" : dot > -1 ? entry.name.substring(dot) : "file";
@@ -5270,7 +5376,7 @@ async function readLogDir(dir) {
5270
5376
  name: entry.name,
5271
5377
  size: st.size,
5272
5378
  time: st.mtime.toISOString(),
5273
- path: path4.normalize(fullPath),
5379
+ path: path5.normalize(fullPath),
5274
5380
  type,
5275
5381
  created: st.birthtime.toISOString(),
5276
5382
  permissions: st.mode.toString(8).slice(-3)
@@ -5281,7 +5387,7 @@ async function readLogDir(dir) {
5281
5387
  name: dirName,
5282
5388
  size: stats.size,
5283
5389
  time: stats.mtime.toISOString(),
5284
- path: path4.normalize(resolved),
5390
+ path: path5.normalize(resolved),
5285
5391
  type: "dir",
5286
5392
  created: stats.birthtime.toISOString(),
5287
5393
  permissions: stats.mode.toString(8).slice(-3),
@@ -5291,7 +5397,7 @@ async function readLogDir(dir) {
5291
5397
  __name(readLogDir, "readLogDir");
5292
5398
  async function removeFile(filePath) {
5293
5399
  try {
5294
- await fs9.unlink(path4.normalize(filePath));
5400
+ await fs9.unlink(path5.normalize(filePath));
5295
5401
  } catch (err) {
5296
5402
  if (err?.code !== "ENOENT") throw err;
5297
5403
  }
@@ -5299,13 +5405,13 @@ async function removeFile(filePath) {
5299
5405
  __name(removeFile, "removeFile");
5300
5406
  var remove = removeFile;
5301
5407
  async function write(filePath, content) {
5302
- const normalized = path4.normalize(filePath);
5303
- await fs9.mkdir(path4.dirname(normalized), { recursive: true });
5408
+ const normalized = path5.normalize(filePath);
5409
+ await fs9.mkdir(path5.dirname(normalized), { recursive: true });
5304
5410
  await fs9.writeFile(normalized, content, "utf8");
5305
5411
  }
5306
5412
  __name(write, "write");
5307
5413
  async function read(filePath) {
5308
- return fs9.readFile(path4.normalize(filePath), "utf8");
5414
+ return fs9.readFile(path5.normalize(filePath), "utf8");
5309
5415
  }
5310
5416
  __name(read, "read");
5311
5417
  async function writeJson(filePath, data) {
@@ -5341,19 +5447,19 @@ __export(common_exports, {
5341
5447
  toSetObject: () => toSetObject,
5342
5448
  toWherePairs: () => toWherePairs
5343
5449
  });
5344
- import * as path5 from "path";
5450
+ import * as path6 from "path";
5345
5451
  function promisifySqlDb(query, sql) {
5346
- return new Promise((resolve5, reject) => {
5452
+ return new Promise((resolve6, reject) => {
5347
5453
  query(sql, (err, result) => {
5348
5454
  if (err) {
5349
5455
  reject(err);
5350
5456
  return;
5351
5457
  }
5352
5458
  if (!result || Array.isArray(result) && result.length === 0) {
5353
- resolve5("");
5459
+ resolve6("");
5354
5460
  return;
5355
5461
  }
5356
- resolve5(result);
5462
+ resolve6(result);
5357
5463
  });
5358
5464
  });
5359
5465
  }
@@ -5406,11 +5512,11 @@ function toInsertRows(sql) {
5406
5512
  }
5407
5513
  __name(toInsertRows, "toInsertRows");
5408
5514
  function isPathInside(baseDir, targetPath) {
5409
- const base = path5.resolve(baseDir);
5410
- const target = path5.resolve(targetPath);
5411
- const relative4 = path5.relative(base, target);
5412
- if (!relative4) return true;
5413
- return !relative4.startsWith("..") && !path5.isAbsolute(relative4);
5515
+ const base = path6.resolve(baseDir);
5516
+ const target = path6.resolve(targetPath);
5517
+ const relative5 = path6.relative(base, target);
5518
+ if (!relative5) return true;
5519
+ return !relative5.startsWith("..") && !path6.isAbsolute(relative5);
5414
5520
  }
5415
5521
  __name(isPathInside, "isPathInside");
5416
5522
  function joinTablePath(table, ...segments) {
@@ -5592,7 +5698,7 @@ var Sql_default = {
5592
5698
 
5593
5699
  // src/sysserver/db/src/file/Index.ts
5594
5700
  import * as fs10 from "fs";
5595
- import * as path6 from "path";
5701
+ import * as path7 from "path";
5596
5702
  function dbDriver(opt = {}) {
5597
5703
  const { Sql, DbCommon } = opt;
5598
5704
  const { isPathInside: isPathInside2, joinTablePath: joinTablePath2 } = DbCommon;
@@ -5606,7 +5712,7 @@ function dbDriver(opt = {}) {
5606
5712
  this.fileDel = (config.del || 0) !== 0;
5607
5713
  this.createDir = config.create || false;
5608
5714
  const root = config.$asai?.serverRoot || getPrimaryServerRoot();
5609
- this.baseDirAbs = path6.resolve(root, config.dir);
5715
+ this.baseDirAbs = path7.resolve(root, config.dir);
5610
5716
  }
5611
5717
  dbLog(...args) {
5612
5718
  (this.config?.$asai?.$log || console.log)("FILE", ...args);
@@ -5619,9 +5725,9 @@ function dbDriver(opt = {}) {
5619
5725
  if (!relPath) {
5620
5726
  return this.baseDirAbs;
5621
5727
  }
5622
- const abs = path6.isAbsolute(relPath) ? path6.normalize(relPath) : path6.resolve(this.baseDirAbs, relPath);
5728
+ const abs = path7.isAbsolute(relPath) ? path7.normalize(relPath) : path7.resolve(this.baseDirAbs, relPath);
5623
5729
  if (!isPathInside2(this.baseDirAbs, abs)) {
5624
- return this.baseDirAbs;
5730
+ throw new Error("Path not allowed");
5625
5731
  }
5626
5732
  return abs;
5627
5733
  }
@@ -5633,11 +5739,11 @@ function dbDriver(opt = {}) {
5633
5739
  if (!relPath) {
5634
5740
  return "";
5635
5741
  }
5636
- return path6.isAbsolute(relPath) ? path6.relative(this.baseDirAbs, relPath) : relPath;
5742
+ return path7.isAbsolute(relPath) ? path7.relative(this.baseDirAbs, relPath) : relPath;
5637
5743
  }
5638
5744
  ensureDir(relPath) {
5639
- const relative4 = this.normalizeRelPath(relPath);
5640
- const dirPath = path6.dirname(relative4);
5745
+ const relative5 = this.normalizeRelPath(relPath);
5746
+ const dirPath = path7.dirname(relative5);
5641
5747
  const absDir = this.resolvePath(dirPath === "." ? "" : dirPath);
5642
5748
  if (this.createDir && !fs10.existsSync(absDir)) {
5643
5749
  this.dbLog("mkdir", "[MKDIR]", absDir);
@@ -5663,9 +5769,9 @@ function dbDriver(opt = {}) {
5663
5769
  this.dbLog("delete", "[RMDIR]", absPath);
5664
5770
  const entries = fs10.readdirSync(absPath, { withFileTypes: true });
5665
5771
  for (const entry of entries) {
5666
- const current = path6.join(absPath, entry.name);
5772
+ const current = path7.join(absPath, entry.name);
5667
5773
  if (entry.isDirectory()) {
5668
- this.deldir(path6.join(relativePath, entry.name));
5774
+ this.deldir(path7.join(relativePath, entry.name));
5669
5775
  } else {
5670
5776
  this.dbLog("delete", "[UNLINK]", current);
5671
5777
  fs10.unlinkSync(current);
@@ -5676,8 +5782,8 @@ function dbDriver(opt = {}) {
5676
5782
  getReadPath(file, sql) {
5677
5783
  const candidates = [];
5678
5784
  if (sql?.filename) {
5679
- candidates.push({ dir: path6.join(file, sql.filename) });
5680
- candidates.push({ dir: path6.join(file + ".delete" + this.fileExt, sql.filename) });
5785
+ candidates.push({ dir: path7.join(file, sql.filename) });
5786
+ candidates.push({ dir: path7.join(file + ".delete" + this.fileExt, sql.filename) });
5681
5787
  } else {
5682
5788
  candidates.push({ dir: file });
5683
5789
  candidates.push({ dir: file + this.fileExt + ".delete" });
@@ -5696,13 +5802,13 @@ function dbDriver(opt = {}) {
5696
5802
  return { path: "", dir: read2.dir };
5697
5803
  }
5698
5804
  if (sql?.filename) {
5699
- return { path: path6.dirname(read2.path), dir: path6.dirname(read2.dir) };
5805
+ return { path: path7.dirname(read2.path), dir: path7.dirname(read2.dir) };
5700
5806
  }
5701
5807
  return { path: read2.path, dir: read2.dir };
5702
5808
  }
5703
5809
  getNewPath(file, sql) {
5704
5810
  if (sql?.filename) {
5705
- return this.getAbsPath(path6.join(file, sql.filename), true);
5811
+ return this.getAbsPath(path7.join(file, sql.filename), true);
5706
5812
  }
5707
5813
  return this.getAbsPath(file, true);
5708
5814
  }
@@ -5795,7 +5901,7 @@ function dbDriver(opt = {}) {
5795
5901
  const isDelete = fileName.endsWith(deleteSuffix);
5796
5902
  const baseName = isDelete ? fileName.slice(0, -deleteSuffix.length) : fileName;
5797
5903
  if (this.fileExt && !baseName.endsWith(this.fileExt)) continue;
5798
- const fullPath = path6.join(absDir, fileName);
5904
+ const fullPath = path7.join(absDir, fileName);
5799
5905
  try {
5800
5906
  const data = fs10.readFileSync(fullPath, "utf8");
5801
5907
  const entryData = { data, file: baseName };
@@ -5824,7 +5930,7 @@ function dbDriver(opt = {}) {
5824
5930
  return String(raw ?? "");
5825
5931
  }
5826
5932
  sqlDb(sql) {
5827
- return new Promise((resolve5, reject) => {
5933
+ return new Promise((resolve6, reject) => {
5828
5934
  try {
5829
5935
  let result;
5830
5936
  const tableDir = this.tableFilePath(sql.table);
@@ -5953,7 +6059,7 @@ function dbDriver(opt = {}) {
5953
6059
  }
5954
6060
  }
5955
6061
  if (result !== void 0) {
5956
- resolve5(result);
6062
+ resolve6(result);
5957
6063
  } else {
5958
6064
  reject("");
5959
6065
  }
@@ -6563,7 +6669,7 @@ var initAsaiHostNodejs2 = {
6563
6669
  createTransportDataHandler,
6564
6670
  wireTransportConnectionLogs
6565
6671
  };
6566
- var PLUGIN_VERSION = true ? "0.0.6" : "0.0.1";
6672
+ var PLUGIN_VERSION = true ? "0.0.9" : "0.0.1";
6567
6673
  var index_default = initAsaiHostNodejs2;
6568
6674
  export {
6569
6675
  Index_default as AsaiCommon,