asaihost-nodejs 0.0.8 → 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.
- package/dist/InterventionLog-WNVDQFAH.cjs.map +1 -1
- package/dist/LogStore-AP4T2CFD.cjs.map +1 -1
- package/dist/asaihost-nodejs.cjs +778 -507
- package/dist/asaihost-nodejs.cjs.map +1 -1
- package/dist/asaihost-nodejs.es.js +391 -120
- package/dist/asaihost-nodejs.es.js.map +1 -1
- package/dist/chunk-2BCIIKRG.cjs.map +1 -1
- package/dist/{chunk-OTL2B4WS.es.js → chunk-3E4Y5ATG.es.js} +2 -2
- package/dist/{chunk-ZK4O4Z2O.cjs → chunk-5EWEKXQI.cjs} +20 -20
- package/dist/chunk-5EWEKXQI.cjs.map +1 -0
- package/dist/chunk-ARHPHWT5.cjs.map +1 -1
- package/dist/{chunk-GXOFLFFW.cjs → chunk-FRPN33BY.cjs} +3 -2
- package/dist/chunk-FRPN33BY.cjs.map +1 -0
- package/dist/{chunk-WXW5DYNU.cjs → chunk-H2QP5J7X.cjs} +9 -9
- package/dist/chunk-H2QP5J7X.cjs.map +1 -0
- package/dist/chunk-HR5JKN7Y.cjs.map +1 -1
- package/dist/{chunk-BOIRAQXE.es.js → chunk-M3NURHGU.es.js} +2 -1
- package/dist/chunk-O6LVLSNB.cjs.map +1 -1
- package/dist/chunk-OCEBEEP5.cjs.map +1 -1
- package/dist/chunk-SMVZ3ZDJ.cjs.map +1 -1
- package/dist/chunk-U2YJH2RW.cjs.map +1 -1
- package/dist/{chunk-5WJQD4RZ.es.js → chunk-UKZ3DP37.es.js} +3 -3
- package/dist/intervention-hooks-4NAAAZK3.cjs.map +1 -1
- package/dist/log-alert-LAJ6VSAG.cjs.map +1 -1
- package/dist/log-formats-TUXGMCCU.cjs.map +1 -1
- package/dist/{password-4P7IZUDQ.es.js → password-2ZBHQQBF.es.js} +3 -3
- package/dist/password-T762KJOI.cjs +20 -0
- package/dist/password-T762KJOI.cjs.map +1 -0
- package/dist/user-store-6QNAL4CK.cjs +12 -0
- package/dist/user-store-6QNAL4CK.cjs.map +1 -0
- package/dist/{user-store-FXD2N55G.es.js → user-store-ZI67TXSI.es.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-GXOFLFFW.cjs.map +0 -1
- package/dist/chunk-WXW5DYNU.cjs.map +0 -1
- package/dist/chunk-ZK4O4Z2O.cjs.map +0 -1
- package/dist/password-MDYRL6ZD.cjs +0 -20
- package/dist/password-MDYRL6ZD.cjs.map +0 -1
- package/dist/user-store-55HOLHEM.cjs +0 -12
- package/dist/user-store-55HOLHEM.cjs.map +0 -1
- /package/dist/{chunk-OTL2B4WS.es.js.map → chunk-3E4Y5ATG.es.js.map} +0 -0
- /package/dist/{chunk-BOIRAQXE.es.js.map → chunk-M3NURHGU.es.js.map} +0 -0
- /package/dist/{chunk-5WJQD4RZ.es.js.map → chunk-UKZ3DP37.es.js.map} +0 -0
- /package/dist/{password-4P7IZUDQ.es.js.map → password-2ZBHQQBF.es.js.map} +0 -0
- /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-
|
|
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-
|
|
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-
|
|
79
|
+
} from "./chunk-M3NURHGU.es.js";
|
|
79
80
|
import {
|
|
80
81
|
getDataServerForAsDb
|
|
81
82
|
} from "./chunk-ZQBHJRAO.es.js";
|
|
@@ -915,7 +916,7 @@ import * as https from "https";
|
|
|
915
916
|
import { WebSocketServer } from "ws";
|
|
916
917
|
|
|
917
918
|
// src/ws/auth.ts
|
|
918
|
-
import { WebSocket } from "ws";
|
|
919
|
+
import { WebSocket as WebSocket2 } from "ws";
|
|
919
920
|
|
|
920
921
|
// src/utils/userinfo.ts
|
|
921
922
|
function deUserInfoWithAsai(userinfo, $asai) {
|
|
@@ -940,6 +941,84 @@ function deUserInfoWithAsai(userinfo, $asai) {
|
|
|
940
941
|
}
|
|
941
942
|
__name(deUserInfoWithAsai, "deUserInfoWithAsai");
|
|
942
943
|
|
|
944
|
+
// src/ws/ws-probe.ts
|
|
945
|
+
import { WebSocket } from "ws";
|
|
946
|
+
function isWsStillActive(ws2) {
|
|
947
|
+
if (!ws2) return false;
|
|
948
|
+
const state = ws2.readyState;
|
|
949
|
+
return state === WebSocket.CONNECTING || state === WebSocket.OPEN;
|
|
950
|
+
}
|
|
951
|
+
__name(isWsStillActive, "isWsStillActive");
|
|
952
|
+
function getWsProbeTimeoutMs($asai) {
|
|
953
|
+
return $asai?.hostconfig?.tm?.d || 3e3;
|
|
954
|
+
}
|
|
955
|
+
__name(getWsProbeTimeoutMs, "getWsProbeTimeoutMs");
|
|
956
|
+
function probeWsAlive($asai, wsclient, timeoutMs) {
|
|
957
|
+
const waitMs = timeoutMs ?? getWsProbeTimeoutMs($asai);
|
|
958
|
+
return new Promise((resolve6) => {
|
|
959
|
+
if (!wsclient || !isWsStillActive(wsclient)) {
|
|
960
|
+
resolve6(false);
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
let settled = false;
|
|
964
|
+
const finish = /* @__PURE__ */ __name((alive) => {
|
|
965
|
+
if (settled) return;
|
|
966
|
+
settled = true;
|
|
967
|
+
clearTimeout(timer);
|
|
968
|
+
try {
|
|
969
|
+
wsclient.off?.("message", onMessage);
|
|
970
|
+
} catch {
|
|
971
|
+
}
|
|
972
|
+
resolve6(alive);
|
|
973
|
+
}, "finish");
|
|
974
|
+
const onMessage = /* @__PURE__ */ __name(() => finish(true), "onMessage");
|
|
975
|
+
const timer = setTimeout(() => finish(false), waitMs);
|
|
976
|
+
try {
|
|
977
|
+
wsclient.once?.("message", onMessage);
|
|
978
|
+
wsclient.send(JSON.stringify({ ty: "publish/web", db: "pong" }));
|
|
979
|
+
} catch {
|
|
980
|
+
finish(false);
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
__name(probeWsAlive, "probeWsAlive");
|
|
985
|
+
function terminateWs(wsclient) {
|
|
986
|
+
if (!wsclient) return;
|
|
987
|
+
try {
|
|
988
|
+
wsclient.terminate?.();
|
|
989
|
+
} catch {
|
|
990
|
+
}
|
|
991
|
+
try {
|
|
992
|
+
wsclient.close?.();
|
|
993
|
+
} catch {
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
__name(terminateWs, "terminateWs");
|
|
997
|
+
async function clearStaleWsIfDead($asai, hostdir, us, existingWs) {
|
|
998
|
+
if (!us || us === "asai") return true;
|
|
999
|
+
const map = $asai.connectionsws?.[hostdir];
|
|
1000
|
+
if (!existingWs) {
|
|
1001
|
+
if (map?.[us]) delete map[us];
|
|
1002
|
+
return true;
|
|
1003
|
+
}
|
|
1004
|
+
if (existingWs._asaiKicked) {
|
|
1005
|
+
terminateWs(existingWs);
|
|
1006
|
+
if (map?.[us] === existingWs) delete map[us];
|
|
1007
|
+
return true;
|
|
1008
|
+
}
|
|
1009
|
+
if (!isWsStillActive(existingWs)) {
|
|
1010
|
+
terminateWs(existingWs);
|
|
1011
|
+
if (map?.[us] === existingWs) delete map[us];
|
|
1012
|
+
return true;
|
|
1013
|
+
}
|
|
1014
|
+
const alive = await probeWsAlive($asai, existingWs);
|
|
1015
|
+
if (alive) return false;
|
|
1016
|
+
terminateWs(existingWs);
|
|
1017
|
+
if (map?.[us] === existingWs) delete map[us];
|
|
1018
|
+
return true;
|
|
1019
|
+
}
|
|
1020
|
+
__name(clearStaleWsIfDead, "clearStaleWsIfDead");
|
|
1021
|
+
|
|
943
1022
|
// src/ws/auth.ts
|
|
944
1023
|
function getWsUserinfo(req, $asai) {
|
|
945
1024
|
try {
|
|
@@ -952,22 +1031,38 @@ function getWsUserinfo(req, $asai) {
|
|
|
952
1031
|
}
|
|
953
1032
|
__name(getWsUserinfo, "getWsUserinfo");
|
|
954
1033
|
function listOnlineWsUsers($asai, hostdir) {
|
|
1034
|
+
return listOnlineWsEntries($asai, hostdir).map((e) => e.us);
|
|
1035
|
+
}
|
|
1036
|
+
__name(listOnlineWsUsers, "listOnlineWsUsers");
|
|
1037
|
+
function listOnlineWsEntries($asai, hostdir) {
|
|
955
1038
|
const users = [];
|
|
1039
|
+
const seen = /* @__PURE__ */ new Set();
|
|
956
1040
|
const map = $asai.connectionsws || {};
|
|
957
1041
|
const dirs = hostdir ? [hostdir] : Object.keys(map);
|
|
958
1042
|
for (const dir of dirs) {
|
|
959
1043
|
for (const [us, ws2] of Object.entries(map[dir] || {})) {
|
|
960
1044
|
if (!us || us === "asai") continue;
|
|
961
1045
|
if (isWsStillActive(ws2)) {
|
|
962
|
-
|
|
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
|
+
}
|
|
963
1058
|
} else if (map[dir]?.[us] === ws2) {
|
|
964
1059
|
delete map[dir][us];
|
|
965
1060
|
}
|
|
966
1061
|
}
|
|
967
1062
|
}
|
|
968
|
-
return
|
|
1063
|
+
return users;
|
|
969
1064
|
}
|
|
970
|
-
__name(
|
|
1065
|
+
__name(listOnlineWsEntries, "listOnlineWsEntries");
|
|
971
1066
|
function isUserOnlineOnWs($asai, us, hostdir) {
|
|
972
1067
|
if (!us || us === "asai") return false;
|
|
973
1068
|
const map = $asai.connectionsws || {};
|
|
@@ -981,19 +1076,73 @@ function isUserOnlineOnWs($asai, us, hostdir) {
|
|
|
981
1076
|
return false;
|
|
982
1077
|
}
|
|
983
1078
|
__name(isUserOnlineOnWs, "isUserOnlineOnWs");
|
|
984
|
-
function kickUserWs($asai, us, hostdir) {
|
|
1079
|
+
async function kickUserWs($asai, us, hostdir) {
|
|
985
1080
|
if (!us || us === "asai") return;
|
|
986
1081
|
const map = $asai.connectionsws || {};
|
|
987
1082
|
const dirs = hostdir ? [hostdir] : Object.keys(map);
|
|
1083
|
+
const waits = [];
|
|
988
1084
|
for (const dir of dirs) {
|
|
989
1085
|
const ws2 = map[dir]?.[us];
|
|
990
1086
|
if (!ws2) continue;
|
|
1087
|
+
if (map[dir]?.[us] === ws2) delete map[dir][us];
|
|
991
1088
|
try {
|
|
992
|
-
ws2.
|
|
1089
|
+
ws2._asaiKicked = Date.now();
|
|
993
1090
|
} catch {
|
|
994
1091
|
}
|
|
995
|
-
|
|
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
|
+
);
|
|
996
1144
|
}
|
|
1145
|
+
if (waits.length) await Promise.all(waits);
|
|
997
1146
|
}
|
|
998
1147
|
__name(kickUserWs, "kickUserWs");
|
|
999
1148
|
async function removeSessionOnWsClose($asai, us, token) {
|
|
@@ -1006,12 +1155,6 @@ async function removeSessionOnWsClose($asai, us, token) {
|
|
|
1006
1155
|
}
|
|
1007
1156
|
}
|
|
1008
1157
|
__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
1158
|
function rejectIncomingWs(ws2, db) {
|
|
1016
1159
|
const payload = JSON.stringify({ ty: "publish/web", db: { exit: 1, type: "err", ...db } });
|
|
1017
1160
|
try {
|
|
@@ -1029,7 +1172,7 @@ function rejectIncomingWs(ws2, db) {
|
|
|
1029
1172
|
}
|
|
1030
1173
|
}
|
|
1031
1174
|
__name(rejectIncomingWs, "rejectIncomingWs");
|
|
1032
|
-
function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
|
|
1175
|
+
async function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
|
|
1033
1176
|
const [us, , tk] = ws2.Userinfo || [];
|
|
1034
1177
|
if (!us) {
|
|
1035
1178
|
ws2.close();
|
|
@@ -1042,7 +1185,8 @@ function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
|
|
|
1042
1185
|
rejectIncomingWs(ws2, {
|
|
1043
1186
|
lang: "pageworking",
|
|
1044
1187
|
langpm: `${us}`,
|
|
1045
|
-
con: `${us} page already running
|
|
1188
|
+
con: `${us} page already running`,
|
|
1189
|
+
online: listOnlineWsEntries($asai, hostdir)
|
|
1046
1190
|
});
|
|
1047
1191
|
return false;
|
|
1048
1192
|
}
|
|
@@ -1053,12 +1197,17 @@ function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
|
|
|
1053
1197
|
delete $asai.connectionsws[hostdir][us];
|
|
1054
1198
|
return true;
|
|
1055
1199
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1200
|
+
const cleared = await clearStaleWsIfDead($asai, hostdir, us, existingWs);
|
|
1201
|
+
if (!cleared) {
|
|
1202
|
+
rejectIncomingWs(ws2, {
|
|
1203
|
+
lang: "logged",
|
|
1204
|
+
langpm: `${us}`,
|
|
1205
|
+
con: `${us} logged in!`,
|
|
1206
|
+
online: listOnlineWsEntries($asai, hostdir)
|
|
1207
|
+
});
|
|
1208
|
+
return false;
|
|
1209
|
+
}
|
|
1210
|
+
return true;
|
|
1062
1211
|
}
|
|
1063
1212
|
if (us !== "asai") {
|
|
1064
1213
|
const connections = $asai.connectionsws[hostdir] || {};
|
|
@@ -1066,19 +1215,22 @@ function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
|
|
|
1066
1215
|
(item) => item.Userinfo?.[2] === tk && item.Userinfo?.[0] !== us
|
|
1067
1216
|
);
|
|
1068
1217
|
const maxOnline = hostcfg.maxonline || 100;
|
|
1218
|
+
const online = listOnlineWsEntries($asai, hostdir);
|
|
1069
1219
|
if (sameTkWs) {
|
|
1070
1220
|
rejectIncomingWs(ws2, {
|
|
1071
1221
|
lang: "loggedsametk",
|
|
1072
1222
|
langpm: `${us}(${sameTkWs.Userinfo[0]})`,
|
|
1073
|
-
con: `${us}(${sameTkWs.Userinfo[0]}) logged in
|
|
1223
|
+
con: `${us}(${sameTkWs.Userinfo[0]}) logged in!`,
|
|
1224
|
+
online
|
|
1074
1225
|
});
|
|
1075
1226
|
return false;
|
|
1076
1227
|
}
|
|
1077
|
-
if (
|
|
1228
|
+
if (online.length >= maxOnline) {
|
|
1078
1229
|
rejectIncomingWs(ws2, {
|
|
1079
1230
|
lang: "maxlogged",
|
|
1080
1231
|
langpm: `${maxOnline}`,
|
|
1081
|
-
con: `max ${maxOnline} users online
|
|
1232
|
+
con: `max ${maxOnline} users online!`,
|
|
1233
|
+
online
|
|
1082
1234
|
});
|
|
1083
1235
|
return false;
|
|
1084
1236
|
}
|
|
@@ -1109,13 +1261,13 @@ function loginWs($asai, hostdir, ws2, req, hostcfg, wsWorkHandler, logWsMessage)
|
|
|
1109
1261
|
if (us && token && !isUserOnlineOnWs($asai, us, hostdir)) {
|
|
1110
1262
|
void removeSessionOnWsClose($asai, us, String(token));
|
|
1111
1263
|
}
|
|
1112
|
-
if (ws2.readyState !==
|
|
1264
|
+
if (ws2.readyState !== WebSocket2.CLOSED && ws2.readyState !== WebSocket2.CLOSING) ws2.close();
|
|
1113
1265
|
});
|
|
1114
1266
|
}
|
|
1115
1267
|
__name(loginWs, "loginWs");
|
|
1116
1268
|
|
|
1117
1269
|
// src/ws/message.ts
|
|
1118
|
-
import { WebSocket as
|
|
1270
|
+
import { WebSocket as WebSocket3 } from "ws";
|
|
1119
1271
|
function clearWsServerTasks($asai, hostdir) {
|
|
1120
1272
|
const tasks = $asai.tasksws[hostdir];
|
|
1121
1273
|
if (tasks && typeof tasks === "object") {
|
|
@@ -1140,7 +1292,7 @@ function attachWssHelpers(wss, $asai) {
|
|
|
1140
1292
|
const data = resWsMsg(msg, $asai);
|
|
1141
1293
|
if (type) {
|
|
1142
1294
|
wss.clients.forEach((client) => {
|
|
1143
|
-
if ((type === 2 || client !== curws) && client.readyState ===
|
|
1295
|
+
if ((type === 2 || client !== curws) && client.readyState === WebSocket3.OPEN) {
|
|
1144
1296
|
client.send(data);
|
|
1145
1297
|
}
|
|
1146
1298
|
});
|
|
@@ -1151,7 +1303,7 @@ function attachWssHelpers(wss, $asai) {
|
|
|
1151
1303
|
wss.broadws = (msg) => {
|
|
1152
1304
|
const data = resWsMsg(msg, $asai);
|
|
1153
1305
|
wss.clients.forEach((client) => {
|
|
1154
|
-
if (client?.readyState ===
|
|
1306
|
+
if (client?.readyState === WebSocket3.OPEN) client.send(data);
|
|
1155
1307
|
});
|
|
1156
1308
|
};
|
|
1157
1309
|
}
|
|
@@ -1331,12 +1483,14 @@ function createWSHost($asai, hostdir) {
|
|
|
1331
1483
|
ws2.send(wsmsg, () => ws2.close(1013, "Server busy"));
|
|
1332
1484
|
return;
|
|
1333
1485
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1486
|
+
void (async () => {
|
|
1487
|
+
const isOpen = !hostcfg.ckws || await isOkWs($asai, hostdir, ws2, req, hostcfg);
|
|
1488
|
+
if (isOpen) {
|
|
1489
|
+
loginWs($asai, hostdir, ws2, req, hostcfg, wsWorkHandler, logWsMessage);
|
|
1490
|
+
} else {
|
|
1491
|
+
ws2.close();
|
|
1492
|
+
}
|
|
1493
|
+
})();
|
|
1340
1494
|
});
|
|
1341
1495
|
$asai.serversws[hostdir] = wss;
|
|
1342
1496
|
} catch (err) {
|
|
@@ -1460,11 +1614,11 @@ function createSkipChecker($asai) {
|
|
|
1460
1614
|
}
|
|
1461
1615
|
if (exact.size === 0 && prefixes.length === 0) return () => false;
|
|
1462
1616
|
return (url) => {
|
|
1463
|
-
const
|
|
1464
|
-
if (!
|
|
1465
|
-
if (exact.has(
|
|
1617
|
+
const path9 = normalizeRequestPath(url);
|
|
1618
|
+
if (!path9) return false;
|
|
1619
|
+
if (exact.has(path9)) return true;
|
|
1466
1620
|
for (let i = 0; i < prefixes.length; i++) {
|
|
1467
|
-
if (
|
|
1621
|
+
if (path9.startsWith(prefixes[i])) return true;
|
|
1468
1622
|
}
|
|
1469
1623
|
return false;
|
|
1470
1624
|
};
|
|
@@ -2351,7 +2505,7 @@ async function attachServerUserLevel(req, $asai) {
|
|
|
2351
2505
|
if (!Array.isArray(userinfo) || !userinfo[0]) return;
|
|
2352
2506
|
const us = String(userinfo[0]);
|
|
2353
2507
|
try {
|
|
2354
|
-
const { createUserStore: createUserStore2 } = await import("./user-store-
|
|
2508
|
+
const { createUserStore: createUserStore2 } = await import("./user-store-ZI67TXSI.es.js");
|
|
2355
2509
|
const user = await createUserStore2($asai).findByUsername(us);
|
|
2356
2510
|
if (user && user.status === "active") {
|
|
2357
2511
|
req._serverUser = { us: user.us, lv: user.lv, status: user.status };
|
|
@@ -2382,11 +2536,11 @@ function getSecurityConfig($asai) {
|
|
|
2382
2536
|
return $asai?.hostconfig?.security || {};
|
|
2383
2537
|
}
|
|
2384
2538
|
__name(getSecurityConfig, "getSecurityConfig");
|
|
2385
|
-
function pathMatchesPattern(
|
|
2539
|
+
function pathMatchesPattern(path9, pattern) {
|
|
2386
2540
|
if (pattern.endsWith("*")) {
|
|
2387
|
-
return
|
|
2541
|
+
return path9.startsWith(pattern.slice(0, -1));
|
|
2388
2542
|
}
|
|
2389
|
-
return
|
|
2543
|
+
return path9 === pattern;
|
|
2390
2544
|
}
|
|
2391
2545
|
__name(pathMatchesPattern, "pathMatchesPattern");
|
|
2392
2546
|
function isAuthSkipped($asai, pathname) {
|
|
@@ -3993,6 +4147,37 @@ async function purgeOfflineSessions($asai, hostdir, store) {
|
|
|
3993
4147
|
__name(purgeOfflineSessions, "purgeOfflineSessions");
|
|
3994
4148
|
|
|
3995
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");
|
|
3996
4181
|
function getSecurity($asai) {
|
|
3997
4182
|
return $asai?.hostconfig?.security || {};
|
|
3998
4183
|
}
|
|
@@ -4153,8 +4338,11 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4153
4338
|
const policy = getPasswordPolicy($asai);
|
|
4154
4339
|
const mustChange = !!user.mustChangePassword || isPasswordExpired(user.passwordChangedAt, policy.maxAgeDays);
|
|
4155
4340
|
const hostcfg = $asai.getHost?.(hostdir) || {};
|
|
4156
|
-
if (hostcfg.ckws
|
|
4157
|
-
|
|
4341
|
+
if (hostcfg.ckws) {
|
|
4342
|
+
const existingWs = $asai.connectionsws?.[hostdir]?.[us];
|
|
4343
|
+
if (existingWs) {
|
|
4344
|
+
await clearStaleWsIfDead($asai, hostdir, us, existingWs);
|
|
4345
|
+
}
|
|
4158
4346
|
}
|
|
4159
4347
|
const quotaCheck = await canLogin($asai, us, hostdir);
|
|
4160
4348
|
if (!quotaCheck.ok) {
|
|
@@ -4200,6 +4388,56 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4200
4388
|
return sendFail(res, err?.message || err, opt);
|
|
4201
4389
|
}
|
|
4202
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
|
+
}
|
|
4203
4441
|
if (url.startsWith("/api/user/login/admin/kick/")) {
|
|
4204
4442
|
if (!await checkAdmin(req, url, getPermissionThreshold($asai, "adminWrite"))) {
|
|
4205
4443
|
return sendErr(res, "AUTH_FORBIDDEN", void 0, opt);
|
|
@@ -4213,7 +4451,7 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4213
4451
|
if (!isSessionUserOnline($asai, target.us, hostdir)) {
|
|
4214
4452
|
return sendErr(res, "SESSION_NOT_ONLINE", void 0, opt);
|
|
4215
4453
|
}
|
|
4216
|
-
kickUserWs($asai, target.us, hostdir);
|
|
4454
|
+
await kickUserWs($asai, target.us, hostdir);
|
|
4217
4455
|
await store.removeSession(sessionId);
|
|
4218
4456
|
const operator = String(req?.Userinfo?.[0] || "");
|
|
4219
4457
|
await logSessionTerminate($asai, {
|
|
@@ -4412,6 +4650,39 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4412
4650
|
await loadUserLevelConfig($asai);
|
|
4413
4651
|
return sendSuccess(res, getPublicSecurityConfig($asai, lang), opt);
|
|
4414
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
|
+
}
|
|
4415
4686
|
if (url.startsWith("/api/user/login/admin/levels/")) {
|
|
4416
4687
|
if (!await checkAdmin(req, url, getPermissionThreshold($asai, "adminRead"))) {
|
|
4417
4688
|
return sendErr(res, "AUTH_FORBIDDEN", void 0, opt);
|
|
@@ -4534,10 +4805,10 @@ var As_default = {
|
|
|
4534
4805
|
return relativePath;
|
|
4535
4806
|
},
|
|
4536
4807
|
//创建多层文件夹 同步
|
|
4537
|
-
ensureDir(
|
|
4808
|
+
ensureDir(fs12, relativePath) {
|
|
4538
4809
|
const absPathDir = this.toDirPath(relativePath);
|
|
4539
4810
|
const absPath = this.toAbsolutePath(relativePath);
|
|
4540
|
-
if (!
|
|
4811
|
+
if (!fs12.existsSync(absPath)) {
|
|
4541
4812
|
let pathTmp = "";
|
|
4542
4813
|
absPathDir.split("/").forEach((el) => {
|
|
4543
4814
|
if (el) {
|
|
@@ -4545,8 +4816,8 @@ var As_default = {
|
|
|
4545
4816
|
pathTmp += "/";
|
|
4546
4817
|
}
|
|
4547
4818
|
pathTmp += el;
|
|
4548
|
-
if (!
|
|
4549
|
-
if (!
|
|
4819
|
+
if (!fs12.existsSync(pathTmp)) {
|
|
4820
|
+
if (!fs12.mkdirSync(pathTmp, { recursive: true })) {
|
|
4550
4821
|
return absPath;
|
|
4551
4822
|
}
|
|
4552
4823
|
}
|
|
@@ -4555,10 +4826,10 @@ var As_default = {
|
|
|
4555
4826
|
}
|
|
4556
4827
|
return absPath;
|
|
4557
4828
|
},
|
|
4558
|
-
makeDir(
|
|
4829
|
+
makeDir(fs12, filePaths) {
|
|
4559
4830
|
return new Promise((resolve6, reject) => {
|
|
4560
4831
|
try {
|
|
4561
|
-
const newPath = this.ensureDir(
|
|
4832
|
+
const newPath = this.ensureDir(fs12, filePaths);
|
|
4562
4833
|
resolve6(newPath);
|
|
4563
4834
|
} catch (err) {
|
|
4564
4835
|
reject(err);
|
|
@@ -5155,7 +5426,7 @@ var AsDb_default = /* @__PURE__ */ __name(($asai, cfg) => {
|
|
|
5155
5426
|
}, "default");
|
|
5156
5427
|
|
|
5157
5428
|
// src/sysserver/lib/common/reqWork.ts
|
|
5158
|
-
import * as
|
|
5429
|
+
import * as path5 from "path";
|
|
5159
5430
|
var reqWork_default = /* @__PURE__ */ __name(($asai) => {
|
|
5160
5431
|
function hostDefault2(mod) {
|
|
5161
5432
|
if (mod && typeof mod === "object" && "default" in mod) {
|
|
@@ -5171,16 +5442,16 @@ var reqWork_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
5171
5442
|
throw new Error("Missing path");
|
|
5172
5443
|
}
|
|
5173
5444
|
const decoded = decodeURIComponent(String(rawPath));
|
|
5174
|
-
if (
|
|
5175
|
-
return
|
|
5445
|
+
if (path5.isAbsolute(decoded)) {
|
|
5446
|
+
return path5.normalize(decoded);
|
|
5176
5447
|
}
|
|
5177
5448
|
const rel = decoded.replace(/^\.(\/)?/, "");
|
|
5178
5449
|
const serverRoot = $asai?.serverRoot;
|
|
5179
5450
|
if (serverRoot && rel) {
|
|
5180
|
-
const absRoot =
|
|
5181
|
-
const abs =
|
|
5182
|
-
const relCheck =
|
|
5183
|
-
if (relCheck === "" || !relCheck.startsWith("..") && !
|
|
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)) {
|
|
5184
5455
|
return abs;
|
|
5185
5456
|
}
|
|
5186
5457
|
}
|
|
@@ -5240,18 +5511,18 @@ __export(AsFs_exports, {
|
|
|
5240
5511
|
write: () => write,
|
|
5241
5512
|
writeJson: () => writeJson
|
|
5242
5513
|
});
|
|
5243
|
-
import
|
|
5244
|
-
import
|
|
5514
|
+
import fs10 from "fs/promises";
|
|
5515
|
+
import path6 from "path";
|
|
5245
5516
|
async function ensureFile(filePath) {
|
|
5246
|
-
const normalized =
|
|
5247
|
-
await
|
|
5248
|
-
const fh = await
|
|
5517
|
+
const normalized = path6.normalize(filePath);
|
|
5518
|
+
await fs10.mkdir(path6.dirname(normalized), { recursive: true });
|
|
5519
|
+
const fh = await fs10.open(normalized, "a");
|
|
5249
5520
|
await fh.close();
|
|
5250
5521
|
}
|
|
5251
5522
|
__name(ensureFile, "ensureFile");
|
|
5252
5523
|
async function pathExists(filePath) {
|
|
5253
5524
|
try {
|
|
5254
|
-
await
|
|
5525
|
+
await fs10.access(path6.normalize(filePath));
|
|
5255
5526
|
return true;
|
|
5256
5527
|
} catch {
|
|
5257
5528
|
return false;
|
|
@@ -5259,7 +5530,7 @@ async function pathExists(filePath) {
|
|
|
5259
5530
|
}
|
|
5260
5531
|
__name(pathExists, "pathExists");
|
|
5261
5532
|
async function readJson(filePath) {
|
|
5262
|
-
const raw = await
|
|
5533
|
+
const raw = await fs10.readFile(path6.normalize(filePath), "utf8");
|
|
5263
5534
|
return JSON.parse(raw);
|
|
5264
5535
|
}
|
|
5265
5536
|
__name(readJson, "readJson");
|
|
@@ -5270,34 +5541,34 @@ function resolvePathUnderBase(baseDir, relPath) {
|
|
|
5270
5541
|
if (relPath.includes("\0")) {
|
|
5271
5542
|
throw new Error("Invalid path");
|
|
5272
5543
|
}
|
|
5273
|
-
const base =
|
|
5274
|
-
const target =
|
|
5275
|
-
const rel =
|
|
5276
|
-
if (rel.startsWith("..") ||
|
|
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)) {
|
|
5277
5548
|
throw new Error("Path not allowed");
|
|
5278
5549
|
}
|
|
5279
5550
|
return target;
|
|
5280
5551
|
}
|
|
5281
5552
|
__name(resolvePathUnderBase, "resolvePathUnderBase");
|
|
5282
5553
|
async function readLogDir(dir) {
|
|
5283
|
-
const resolved =
|
|
5284
|
-
const stats = await
|
|
5554
|
+
const resolved = path6.resolve(dir);
|
|
5555
|
+
const stats = await fs10.stat(resolved);
|
|
5285
5556
|
if (!stats.isDirectory()) {
|
|
5286
5557
|
throw new Error("Not a directory");
|
|
5287
5558
|
}
|
|
5288
|
-
const dirName =
|
|
5289
|
-
const entries = await
|
|
5559
|
+
const dirName = path6.basename(resolved);
|
|
5560
|
+
const entries = await fs10.readdir(resolved, { withFileTypes: true });
|
|
5290
5561
|
const children = await Promise.all(
|
|
5291
5562
|
entries.map(async (entry) => {
|
|
5292
|
-
const fullPath =
|
|
5293
|
-
const st = await
|
|
5563
|
+
const fullPath = path6.join(resolved, entry.name);
|
|
5564
|
+
const st = await fs10.stat(fullPath);
|
|
5294
5565
|
const dot = entry.name.lastIndexOf(".");
|
|
5295
5566
|
const type = entry.isDirectory() ? "dir" : dot > -1 ? entry.name.substring(dot) : "file";
|
|
5296
5567
|
return {
|
|
5297
5568
|
name: entry.name,
|
|
5298
5569
|
size: st.size,
|
|
5299
5570
|
time: st.mtime.toISOString(),
|
|
5300
|
-
path:
|
|
5571
|
+
path: path6.normalize(fullPath),
|
|
5301
5572
|
type,
|
|
5302
5573
|
created: st.birthtime.toISOString(),
|
|
5303
5574
|
permissions: st.mode.toString(8).slice(-3)
|
|
@@ -5308,7 +5579,7 @@ async function readLogDir(dir) {
|
|
|
5308
5579
|
name: dirName,
|
|
5309
5580
|
size: stats.size,
|
|
5310
5581
|
time: stats.mtime.toISOString(),
|
|
5311
|
-
path:
|
|
5582
|
+
path: path6.normalize(resolved),
|
|
5312
5583
|
type: "dir",
|
|
5313
5584
|
created: stats.birthtime.toISOString(),
|
|
5314
5585
|
permissions: stats.mode.toString(8).slice(-3),
|
|
@@ -5318,7 +5589,7 @@ async function readLogDir(dir) {
|
|
|
5318
5589
|
__name(readLogDir, "readLogDir");
|
|
5319
5590
|
async function removeFile(filePath) {
|
|
5320
5591
|
try {
|
|
5321
|
-
await
|
|
5592
|
+
await fs10.unlink(path6.normalize(filePath));
|
|
5322
5593
|
} catch (err) {
|
|
5323
5594
|
if (err?.code !== "ENOENT") throw err;
|
|
5324
5595
|
}
|
|
@@ -5326,13 +5597,13 @@ async function removeFile(filePath) {
|
|
|
5326
5597
|
__name(removeFile, "removeFile");
|
|
5327
5598
|
var remove = removeFile;
|
|
5328
5599
|
async function write(filePath, content) {
|
|
5329
|
-
const normalized =
|
|
5330
|
-
await
|
|
5331
|
-
await
|
|
5600
|
+
const normalized = path6.normalize(filePath);
|
|
5601
|
+
await fs10.mkdir(path6.dirname(normalized), { recursive: true });
|
|
5602
|
+
await fs10.writeFile(normalized, content, "utf8");
|
|
5332
5603
|
}
|
|
5333
5604
|
__name(write, "write");
|
|
5334
5605
|
async function read(filePath) {
|
|
5335
|
-
return
|
|
5606
|
+
return fs10.readFile(path6.normalize(filePath), "utf8");
|
|
5336
5607
|
}
|
|
5337
5608
|
__name(read, "read");
|
|
5338
5609
|
async function writeJson(filePath, data) {
|
|
@@ -5368,7 +5639,7 @@ __export(common_exports, {
|
|
|
5368
5639
|
toSetObject: () => toSetObject,
|
|
5369
5640
|
toWherePairs: () => toWherePairs
|
|
5370
5641
|
});
|
|
5371
|
-
import * as
|
|
5642
|
+
import * as path7 from "path";
|
|
5372
5643
|
function promisifySqlDb(query, sql) {
|
|
5373
5644
|
return new Promise((resolve6, reject) => {
|
|
5374
5645
|
query(sql, (err, result) => {
|
|
@@ -5433,11 +5704,11 @@ function toInsertRows(sql) {
|
|
|
5433
5704
|
}
|
|
5434
5705
|
__name(toInsertRows, "toInsertRows");
|
|
5435
5706
|
function isPathInside(baseDir, targetPath) {
|
|
5436
|
-
const base =
|
|
5437
|
-
const target =
|
|
5438
|
-
const relative5 =
|
|
5707
|
+
const base = path7.resolve(baseDir);
|
|
5708
|
+
const target = path7.resolve(targetPath);
|
|
5709
|
+
const relative5 = path7.relative(base, target);
|
|
5439
5710
|
if (!relative5) return true;
|
|
5440
|
-
return !relative5.startsWith("..") && !
|
|
5711
|
+
return !relative5.startsWith("..") && !path7.isAbsolute(relative5);
|
|
5441
5712
|
}
|
|
5442
5713
|
__name(isPathInside, "isPathInside");
|
|
5443
5714
|
function joinTablePath(table, ...segments) {
|
|
@@ -5618,8 +5889,8 @@ var Sql_default = {
|
|
|
5618
5889
|
};
|
|
5619
5890
|
|
|
5620
5891
|
// src/sysserver/db/src/file/Index.ts
|
|
5621
|
-
import * as
|
|
5622
|
-
import * as
|
|
5892
|
+
import * as fs11 from "fs";
|
|
5893
|
+
import * as path8 from "path";
|
|
5623
5894
|
function dbDriver(opt = {}) {
|
|
5624
5895
|
const { Sql, DbCommon } = opt;
|
|
5625
5896
|
const { isPathInside: isPathInside2, joinTablePath: joinTablePath2 } = DbCommon;
|
|
@@ -5633,20 +5904,20 @@ function dbDriver(opt = {}) {
|
|
|
5633
5904
|
this.fileDel = (config.del || 0) !== 0;
|
|
5634
5905
|
this.createDir = config.create || false;
|
|
5635
5906
|
const root = config.$asai?.serverRoot || getPrimaryServerRoot();
|
|
5636
|
-
this.baseDirAbs =
|
|
5907
|
+
this.baseDirAbs = path8.resolve(root, config.dir);
|
|
5637
5908
|
}
|
|
5638
5909
|
dbLog(...args) {
|
|
5639
5910
|
(this.config?.$asai?.$log || console.log)("FILE", ...args);
|
|
5640
5911
|
}
|
|
5641
5912
|
checkFile(file) {
|
|
5642
|
-
return
|
|
5913
|
+
return fs11.existsSync(this.getAbsPath(file, false));
|
|
5643
5914
|
}
|
|
5644
5915
|
/** 解析相对路径并限制在数据根目录内,防止路径穿越(跨平台) */
|
|
5645
5916
|
resolvePath(relPath) {
|
|
5646
5917
|
if (!relPath) {
|
|
5647
5918
|
return this.baseDirAbs;
|
|
5648
5919
|
}
|
|
5649
|
-
const abs =
|
|
5920
|
+
const abs = path8.isAbsolute(relPath) ? path8.normalize(relPath) : path8.resolve(this.baseDirAbs, relPath);
|
|
5650
5921
|
if (!isPathInside2(this.baseDirAbs, abs)) {
|
|
5651
5922
|
throw new Error("Path not allowed");
|
|
5652
5923
|
}
|
|
@@ -5660,15 +5931,15 @@ function dbDriver(opt = {}) {
|
|
|
5660
5931
|
if (!relPath) {
|
|
5661
5932
|
return "";
|
|
5662
5933
|
}
|
|
5663
|
-
return
|
|
5934
|
+
return path8.isAbsolute(relPath) ? path8.relative(this.baseDirAbs, relPath) : relPath;
|
|
5664
5935
|
}
|
|
5665
5936
|
ensureDir(relPath) {
|
|
5666
5937
|
const relative5 = this.normalizeRelPath(relPath);
|
|
5667
|
-
const dirPath =
|
|
5938
|
+
const dirPath = path8.dirname(relative5);
|
|
5668
5939
|
const absDir = this.resolvePath(dirPath === "." ? "" : dirPath);
|
|
5669
|
-
if (this.createDir && !
|
|
5940
|
+
if (this.createDir && !fs11.existsSync(absDir)) {
|
|
5670
5941
|
this.dbLog("mkdir", "[MKDIR]", absDir);
|
|
5671
|
-
|
|
5942
|
+
fs11.mkdirSync(absDir, { recursive: true });
|
|
5672
5943
|
}
|
|
5673
5944
|
return absDir;
|
|
5674
5945
|
}
|
|
@@ -5684,34 +5955,34 @@ function dbDriver(opt = {}) {
|
|
|
5684
5955
|
}
|
|
5685
5956
|
deldir(relativePath) {
|
|
5686
5957
|
const absPath = this.resolvePath(relativePath);
|
|
5687
|
-
if (!
|
|
5958
|
+
if (!fs11.existsSync(absPath)) {
|
|
5688
5959
|
return;
|
|
5689
5960
|
}
|
|
5690
5961
|
this.dbLog("delete", "[RMDIR]", absPath);
|
|
5691
|
-
const entries =
|
|
5962
|
+
const entries = fs11.readdirSync(absPath, { withFileTypes: true });
|
|
5692
5963
|
for (const entry of entries) {
|
|
5693
|
-
const current =
|
|
5964
|
+
const current = path8.join(absPath, entry.name);
|
|
5694
5965
|
if (entry.isDirectory()) {
|
|
5695
|
-
this.deldir(
|
|
5966
|
+
this.deldir(path8.join(relativePath, entry.name));
|
|
5696
5967
|
} else {
|
|
5697
5968
|
this.dbLog("delete", "[UNLINK]", current);
|
|
5698
|
-
|
|
5969
|
+
fs11.unlinkSync(current);
|
|
5699
5970
|
}
|
|
5700
5971
|
}
|
|
5701
|
-
|
|
5972
|
+
fs11.rmdirSync(absPath);
|
|
5702
5973
|
}
|
|
5703
5974
|
getReadPath(file, sql) {
|
|
5704
5975
|
const candidates = [];
|
|
5705
5976
|
if (sql?.filename) {
|
|
5706
|
-
candidates.push({ dir:
|
|
5707
|
-
candidates.push({ dir:
|
|
5977
|
+
candidates.push({ dir: path8.join(file, sql.filename) });
|
|
5978
|
+
candidates.push({ dir: path8.join(file + ".delete" + this.fileExt, sql.filename) });
|
|
5708
5979
|
} else {
|
|
5709
5980
|
candidates.push({ dir: file });
|
|
5710
5981
|
candidates.push({ dir: file + this.fileExt + ".delete" });
|
|
5711
5982
|
}
|
|
5712
5983
|
for (const candidate of candidates) {
|
|
5713
5984
|
const abs = this.getAbsPath(candidate.dir, false);
|
|
5714
|
-
if (
|
|
5985
|
+
if (fs11.existsSync(abs)) {
|
|
5715
5986
|
return { path: abs, dir: candidate.dir };
|
|
5716
5987
|
}
|
|
5717
5988
|
}
|
|
@@ -5723,13 +5994,13 @@ function dbDriver(opt = {}) {
|
|
|
5723
5994
|
return { path: "", dir: read2.dir };
|
|
5724
5995
|
}
|
|
5725
5996
|
if (sql?.filename) {
|
|
5726
|
-
return { path:
|
|
5997
|
+
return { path: path8.dirname(read2.path), dir: path8.dirname(read2.dir) };
|
|
5727
5998
|
}
|
|
5728
5999
|
return { path: read2.path, dir: read2.dir };
|
|
5729
6000
|
}
|
|
5730
6001
|
getNewPath(file, sql) {
|
|
5731
6002
|
if (sql?.filename) {
|
|
5732
|
-
return this.getAbsPath(
|
|
6003
|
+
return this.getAbsPath(path8.join(file, sql.filename), true);
|
|
5733
6004
|
}
|
|
5734
6005
|
return this.getAbsPath(file, true);
|
|
5735
6006
|
}
|
|
@@ -5737,7 +6008,7 @@ function dbDriver(opt = {}) {
|
|
|
5737
6008
|
try {
|
|
5738
6009
|
const filePath = sql?.type === "insert" ? this.getNewPath(file, sql) : this.getReadPath(file, sql).path || this.getNewPath(file, sql);
|
|
5739
6010
|
this.dbLog("write", "[WRITE]", filePath);
|
|
5740
|
-
|
|
6011
|
+
fs11.writeFileSync(filePath, data, "utf8");
|
|
5741
6012
|
return "ok";
|
|
5742
6013
|
} catch (e) {
|
|
5743
6014
|
this.dbLog("write failed", "[WRITE ERROR]", e);
|
|
@@ -5751,7 +6022,7 @@ function dbDriver(opt = {}) {
|
|
|
5751
6022
|
return { error: "empty" };
|
|
5752
6023
|
}
|
|
5753
6024
|
this.dbLog("read", "[READ]", read2.path);
|
|
5754
|
-
const data =
|
|
6025
|
+
const data = fs11.readFileSync(read2.path, { encoding }) || "";
|
|
5755
6026
|
const fileName = this.fileExt && read2.dir.endsWith(this.fileExt) ? read2.dir : read2.dir + this.fileExt;
|
|
5756
6027
|
return [{ data, file: fileName }];
|
|
5757
6028
|
} catch (e) {
|
|
@@ -5768,12 +6039,12 @@ function dbDriver(opt = {}) {
|
|
|
5768
6039
|
if (this.fileDel && sql?.deltrue) {
|
|
5769
6040
|
const target = del.path + ".delete" + this.fileExt;
|
|
5770
6041
|
this.dbLog("soft delete", "[SOFT DELETE]", `${del.path} -> ${target}`);
|
|
5771
|
-
|
|
6042
|
+
fs11.renameSync(del.path, target);
|
|
5772
6043
|
} else if (sql?.filename) {
|
|
5773
6044
|
this.deldir(del.dir);
|
|
5774
6045
|
} else {
|
|
5775
6046
|
this.dbLog("delete", "[DELETE]", del.path);
|
|
5776
|
-
|
|
6047
|
+
fs11.unlinkSync(del.path);
|
|
5777
6048
|
}
|
|
5778
6049
|
return "ok";
|
|
5779
6050
|
} catch (e) {
|
|
@@ -5792,11 +6063,11 @@ function dbDriver(opt = {}) {
|
|
|
5792
6063
|
const original = del.path.replace(new RegExp(`.delete${extPattern}$`), "");
|
|
5793
6064
|
if (original !== del.path) {
|
|
5794
6065
|
this.dbLog("restore", "[RESTORE]", `${del.path} -> ${original}`);
|
|
5795
|
-
|
|
6066
|
+
fs11.renameSync(del.path, original);
|
|
5796
6067
|
}
|
|
5797
6068
|
} else {
|
|
5798
6069
|
this.dbLog("force delete", "[FORCE DELETE]", del.path);
|
|
5799
|
-
|
|
6070
|
+
fs11.unlinkSync(del.path);
|
|
5800
6071
|
}
|
|
5801
6072
|
return "ok";
|
|
5802
6073
|
} catch (e) {
|
|
@@ -5810,7 +6081,7 @@ function dbDriver(opt = {}) {
|
|
|
5810
6081
|
this.dbLog("list", "[LIST]", absDir);
|
|
5811
6082
|
let entries;
|
|
5812
6083
|
try {
|
|
5813
|
-
entries =
|
|
6084
|
+
entries = fs11.readdirSync(absDir, { withFileTypes: true });
|
|
5814
6085
|
} catch {
|
|
5815
6086
|
return sql?.list ? { open: [], delete: [] } : [];
|
|
5816
6087
|
}
|
|
@@ -5822,9 +6093,9 @@ function dbDriver(opt = {}) {
|
|
|
5822
6093
|
const isDelete = fileName.endsWith(deleteSuffix);
|
|
5823
6094
|
const baseName = isDelete ? fileName.slice(0, -deleteSuffix.length) : fileName;
|
|
5824
6095
|
if (this.fileExt && !baseName.endsWith(this.fileExt)) continue;
|
|
5825
|
-
const fullPath =
|
|
6096
|
+
const fullPath = path8.join(absDir, fileName);
|
|
5826
6097
|
try {
|
|
5827
|
-
const data =
|
|
6098
|
+
const data = fs11.readFileSync(fullPath, "utf8");
|
|
5828
6099
|
const entryData = { data, file: baseName };
|
|
5829
6100
|
if (isDelete) {
|
|
5830
6101
|
if (sql?.list) result.delete.push(entryData);
|
|
@@ -6590,7 +6861,7 @@ var initAsaiHostNodejs2 = {
|
|
|
6590
6861
|
createTransportDataHandler,
|
|
6591
6862
|
wireTransportConnectionLogs
|
|
6592
6863
|
};
|
|
6593
|
-
var PLUGIN_VERSION = true ? "0.0.
|
|
6864
|
+
var PLUGIN_VERSION = true ? "0.0.10" : "0.0.1";
|
|
6594
6865
|
var index_default = initAsaiHostNodejs2;
|
|
6595
6866
|
export {
|
|
6596
6867
|
Index_default as AsaiCommon,
|