asaihost-nodejs 0.0.1 → 0.0.2
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/README.md +76 -25
- package/dist/InterventionLog-CRP6YJVM.cjs +30 -0
- package/dist/{InterventionLog-25EHE4QR.cjs.map → InterventionLog-CRP6YJVM.cjs.map} +1 -1
- package/dist/{InterventionLog-R6CCPU2B.es.js → InterventionLog-PZT2CCN3.es.js} +5 -5
- package/dist/LogStore-7MTUQA4G.cjs +54 -0
- package/dist/{LogStore-M2AUBNGO.cjs.map → LogStore-7MTUQA4G.cjs.map} +1 -1
- package/dist/{LogStore-ECXX34WC.es.js → LogStore-Q35YFNAY.es.js} +3 -3
- package/dist/asaihost-nodejs.cjs +1296 -1410
- package/dist/asaihost-nodejs.cjs.map +1 -1
- package/dist/asaihost-nodejs.d.cts +154 -17
- package/dist/asaihost-nodejs.d.ts +154 -17
- package/dist/asaihost-nodejs.es.js +911 -1025
- package/dist/asaihost-nodejs.es.js.map +1 -1
- package/dist/{chunk-HHPGUJ75.cjs → chunk-2BCIIKRG.cjs} +13 -6
- package/dist/chunk-2BCIIKRG.cjs.map +1 -0
- package/dist/{chunk-G4WVDUBH.cjs → chunk-4E7Z5K5U.cjs} +7 -7
- package/dist/{chunk-G4WVDUBH.cjs.map → chunk-4E7Z5K5U.cjs.map} +1 -1
- package/dist/{chunk-DROOHKB3.es.js → chunk-57UQJKSL.es.js} +2 -2
- package/dist/{chunk-EOGZPRML.es.js → chunk-A5CDJGJI.es.js} +224 -39
- package/dist/chunk-A5CDJGJI.es.js.map +1 -0
- package/dist/{chunk-E7V3EBDB.es.js → chunk-A6VJPLDD.es.js} +5 -3
- package/dist/chunk-A6VJPLDD.es.js.map +1 -0
- package/dist/{chunk-X7VHW2TD.cjs → chunk-H3K33HBA.cjs} +249 -64
- package/dist/chunk-H3K33HBA.cjs.map +1 -0
- package/dist/{chunk-KOFCBB3N.cjs → chunk-K2QPSQEV.cjs} +9 -9
- package/dist/{chunk-KOFCBB3N.cjs.map → chunk-K2QPSQEV.cjs.map} +1 -1
- package/dist/{chunk-LOMSUPPU.es.js → chunk-LTEP4XB2.es.js} +6 -6
- package/dist/{chunk-QWVR2HNT.cjs → chunk-LXJMGLKW.cjs} +21 -19
- package/dist/chunk-LXJMGLKW.cjs.map +1 -0
- package/dist/{chunk-4WJDC4M4.es.js → chunk-NLZNIGNF.es.js} +2 -2
- package/dist/chunk-NLZNIGNF.es.js.map +1 -0
- package/dist/{chunk-W7DVKGX5.cjs → chunk-OCEBEEP5.cjs} +2 -2
- package/dist/{chunk-W7DVKGX5.cjs.map → chunk-OCEBEEP5.cjs.map} +1 -1
- package/dist/{chunk-NLBFIRDB.cjs → chunk-RIRKIYET.cjs} +12 -12
- package/dist/{chunk-NLBFIRDB.cjs.map → chunk-RIRKIYET.cjs.map} +1 -1
- package/dist/{chunk-XBIN4C2V.es.js → chunk-SB23ESX2.es.js} +2 -2
- package/dist/{chunk-VLMM735Q.cjs → chunk-TKJAHTPO.cjs} +26 -26
- package/dist/{chunk-VLMM735Q.cjs.map → chunk-TKJAHTPO.cjs.map} +1 -1
- package/dist/{chunk-MCMADJQR.es.js → chunk-VUZO7DCA.es.js} +4 -4
- package/dist/chunk-VUZO7DCA.es.js.map +1 -0
- package/dist/{chunk-KUZQ36IJ.es.js → chunk-ZQBHJRAO.es.js} +10 -3
- package/dist/chunk-ZQBHJRAO.es.js.map +1 -0
- package/dist/intervention-hooks-KF3SNB5Q.cjs +21 -0
- package/dist/{intervention-hooks-LE6X4C6N.cjs.map → intervention-hooks-KF3SNB5Q.cjs.map} +1 -1
- package/dist/{intervention-hooks-FI4EHOL5.es.js → intervention-hooks-TCGIC4IV.es.js} +6 -6
- package/dist/log-formats-TUXGMCCU.cjs +31 -0
- package/dist/{log-formats-FKFMQKNO.cjs.map → log-formats-TUXGMCCU.cjs.map} +1 -1
- package/dist/{log-formats-I5E32H5F.es.js → log-formats-WW3TDUAK.es.js} +2 -2
- package/dist/{password-63OY27RX.es.js → password-4BABWAG4.es.js} +4 -4
- package/dist/password-PJHJRGPE.cjs +20 -0
- package/dist/{password-NZ5EGDWG.cjs.map → password-PJHJRGPE.cjs.map} +1 -1
- package/dist/user-store-6J2PSEIN.cjs +12 -0
- package/dist/{user-store-SX4N6PQ4.cjs.map → user-store-6J2PSEIN.cjs.map} +1 -1
- package/dist/user-store-UC5URX3C.es.js +12 -0
- package/package.json +2 -13
- package/dist/InterventionLog-25EHE4QR.cjs +0 -30
- package/dist/LogStore-M2AUBNGO.cjs +0 -54
- package/dist/chunk-4WJDC4M4.es.js.map +0 -1
- package/dist/chunk-E7V3EBDB.es.js.map +0 -1
- package/dist/chunk-EOGZPRML.es.js.map +0 -1
- package/dist/chunk-HHPGUJ75.cjs.map +0 -1
- package/dist/chunk-KUZQ36IJ.es.js.map +0 -1
- package/dist/chunk-MCMADJQR.es.js.map +0 -1
- package/dist/chunk-QWVR2HNT.cjs.map +0 -1
- package/dist/chunk-X7VHW2TD.cjs.map +0 -1
- package/dist/intervention-hooks-LE6X4C6N.cjs +0 -21
- package/dist/log-formats-FKFMQKNO.cjs +0 -31
- package/dist/password-NZ5EGDWG.cjs +0 -20
- package/dist/user-store-J7LWEXSW.es.js +0 -12
- package/dist/user-store-SX4N6PQ4.cjs +0 -12
- /package/dist/{InterventionLog-R6CCPU2B.es.js.map → InterventionLog-PZT2CCN3.es.js.map} +0 -0
- /package/dist/{LogStore-ECXX34WC.es.js.map → LogStore-Q35YFNAY.es.js.map} +0 -0
- /package/dist/{chunk-DROOHKB3.es.js.map → chunk-57UQJKSL.es.js.map} +0 -0
- /package/dist/{chunk-LOMSUPPU.es.js.map → chunk-LTEP4XB2.es.js.map} +0 -0
- /package/dist/{chunk-XBIN4C2V.es.js.map → chunk-SB23ESX2.es.js.map} +0 -0
- /package/dist/{intervention-hooks-FI4EHOL5.es.js.map → intervention-hooks-TCGIC4IV.es.js.map} +0 -0
- /package/dist/{log-formats-I5E32H5F.es.js.map → log-formats-WW3TDUAK.es.js.map} +0 -0
- /package/dist/{password-63OY27RX.es.js.map → password-4BABWAG4.es.js.map} +0 -0
- /package/dist/{user-store-J7LWEXSW.es.js.map → user-store-UC5URX3C.es.js.map} +0 -0
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
logSecurityConfigIntervention,
|
|
5
5
|
logSessionTerminate,
|
|
6
6
|
logUserRbacChange
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-SB23ESX2.es.js";
|
|
8
8
|
import {
|
|
9
9
|
InterventionLog_exports
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VUZO7DCA.es.js";
|
|
11
11
|
import {
|
|
12
12
|
LogStore_exports,
|
|
13
13
|
archiveInterventionYear,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
queryUnifiedLogs,
|
|
29
29
|
readWebLogFromStore,
|
|
30
30
|
searchUnifiedLogs
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-LTEP4XB2.es.js";
|
|
32
32
|
import {
|
|
33
33
|
W3C_ACCESS_FIELDS,
|
|
34
34
|
W3C_SERVER_FIELDS,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
formatW3CHeader,
|
|
39
39
|
formatW3CServerRecord,
|
|
40
40
|
parseCLF
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-NLZNIGNF.es.js";
|
|
42
42
|
import {
|
|
43
43
|
getNowTime
|
|
44
44
|
} from "./chunk-UTKVJE7N.es.js";
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
secureCompare,
|
|
49
49
|
validatePasswordChange,
|
|
50
50
|
verifyPassword
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-57UQJKSL.es.js";
|
|
52
52
|
import {
|
|
53
53
|
USER_LV,
|
|
54
54
|
applySecretsToHostConfig,
|
|
@@ -60,8 +60,9 @@ import {
|
|
|
60
60
|
resolveHttpsCertFromPaths,
|
|
61
61
|
stripForbiddenSecretsFromHostConfig,
|
|
62
62
|
validateAsaiSecrets
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-A6VJPLDD.es.js";
|
|
64
64
|
import {
|
|
65
|
+
getDefaultResetPassword,
|
|
65
66
|
getPasswordPolicy,
|
|
66
67
|
getPermissionThreshold,
|
|
67
68
|
getPublicSecurityConfig,
|
|
@@ -70,11 +71,12 @@ import {
|
|
|
70
71
|
isValidUserLv,
|
|
71
72
|
loadUserLevelConfig,
|
|
72
73
|
resolveRoleForLevel,
|
|
73
|
-
saveUserLevelConfig
|
|
74
|
-
|
|
74
|
+
saveUserLevelConfig,
|
|
75
|
+
validatePasswordWithPolicy
|
|
76
|
+
} from "./chunk-A5CDJGJI.es.js";
|
|
75
77
|
import {
|
|
76
78
|
getDataServerForAsDb
|
|
77
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-ZQBHJRAO.es.js";
|
|
78
80
|
import {
|
|
79
81
|
__export,
|
|
80
82
|
__name
|
|
@@ -210,7 +212,7 @@ ${reason.name}: ${reason.message}`;
|
|
|
210
212
|
if (restartTimes.length > MAX_RESTARTS) {
|
|
211
213
|
try {
|
|
212
214
|
$asai.$log("NO_RESTART", "Process crashed too frequently, gracefully shutdown and wait for daemon restart.");
|
|
213
|
-
void import("./intervention-hooks-
|
|
215
|
+
void import("./intervention-hooks-TCGIC4IV.es.js").then(
|
|
214
216
|
({ logProcessGuardEvent }) => logProcessGuardEvent($asai, {
|
|
215
217
|
action: "crash_loop_shutdown",
|
|
216
218
|
detail: `${restartTimes.length} crashes in ${RESTART_WINDOW}ms window`,
|
|
@@ -252,7 +254,7 @@ ${reason.name}: ${reason.message}`;
|
|
|
252
254
|
}
|
|
253
255
|
__name(initProcess, "initProcess");
|
|
254
256
|
function gracefulShutdown($asai) {
|
|
255
|
-
void import("./intervention-hooks-
|
|
257
|
+
void import("./intervention-hooks-TCGIC4IV.es.js").then(
|
|
256
258
|
({ logProcessGuardEvent }) => logProcessGuardEvent($asai, {
|
|
257
259
|
action: "graceful_shutdown",
|
|
258
260
|
detail: "SIGTERM/SIGINT or crash guard triggered",
|
|
@@ -395,7 +397,7 @@ var LogBuffer = class _LogBuffer {
|
|
|
395
397
|
}
|
|
396
398
|
};
|
|
397
399
|
function saveLog($asai, src, logstr) {
|
|
398
|
-
return new Promise((
|
|
400
|
+
return new Promise((resolve6, reject) => {
|
|
399
401
|
try {
|
|
400
402
|
const bufferKey = `${$asai.id || "default"}_${src}`;
|
|
401
403
|
let logBuffer = logBuffers.get(bufferKey);
|
|
@@ -404,7 +406,7 @@ function saveLog($asai, src, logstr) {
|
|
|
404
406
|
logBuffers.set(bufferKey, logBuffer);
|
|
405
407
|
}
|
|
406
408
|
logBuffer.add(logstr);
|
|
407
|
-
|
|
409
|
+
resolve6();
|
|
408
410
|
} catch (err) {
|
|
409
411
|
console.error(err);
|
|
410
412
|
reject(err);
|
|
@@ -424,7 +426,7 @@ async function ensureW3CHeader($asai, filePath, dateCur) {
|
|
|
424
426
|
} catch {
|
|
425
427
|
}
|
|
426
428
|
const header = formatW3CHeader({
|
|
427
|
-
software: $asai.hostconfig.website?.title || "
|
|
429
|
+
software: $asai.hostconfig.website?.title || "AsaiHostNodejs",
|
|
428
430
|
version: $asai.hostconfig.website?.ver || "1.0.0.0",
|
|
429
431
|
fields: [...W3C_SERVER_FIELDS],
|
|
430
432
|
date: dateCur
|
|
@@ -507,7 +509,7 @@ async function ensureW3CAccessHeader($asai, filePath, dateCur) {
|
|
|
507
509
|
} catch {
|
|
508
510
|
}
|
|
509
511
|
const header = formatW3CHeader({
|
|
510
|
-
software: $asai.hostconfig.website?.title || "
|
|
512
|
+
software: $asai.hostconfig.website?.title || "AsaiHostNodejs",
|
|
511
513
|
version: $asai.hostconfig.website?.ver || "1.0.0.0",
|
|
512
514
|
fields: [...W3C_ACCESS_FIELDS],
|
|
513
515
|
date: dateCur
|
|
@@ -843,7 +845,7 @@ function verifyConfigManifest(serverRoot) {
|
|
|
843
845
|
__name(verifyConfigManifest, "verifyConfigManifest");
|
|
844
846
|
|
|
845
847
|
// src/core/init.ts
|
|
846
|
-
function
|
|
848
|
+
function initAsaiHostNodejs($asai, _opt) {
|
|
847
849
|
if (!$asai.hostconfig?.ip) {
|
|
848
850
|
$asai.hostconfig.ip = getIp();
|
|
849
851
|
}
|
|
@@ -870,7 +872,7 @@ function initAsaiHost($asai, _opt) {
|
|
|
870
872
|
type: "CONFIG_WARN",
|
|
871
873
|
detail: "Using dev-only password; set ASAI_HOST_PASSWORD for production (IEC 62443-4-2 CR 1.7)"
|
|
872
874
|
});
|
|
873
|
-
void import("./intervention-hooks-
|
|
875
|
+
void import("./intervention-hooks-TCGIC4IV.es.js").then(
|
|
874
876
|
({ logSecurityConfigIntervention: logSecurityConfigIntervention2 }) => logSecurityConfigIntervention2($asai, {
|
|
875
877
|
section: "password",
|
|
876
878
|
detail: "dev-default-password detected at startup"
|
|
@@ -890,13 +892,13 @@ function initAsaiHost($asai, _opt) {
|
|
|
890
892
|
if (!ntp.ok) logSecurityEvent($asai, { type: "CONFIG_WARN", detail: `NTP: ${ntp.detail}` });
|
|
891
893
|
});
|
|
892
894
|
}
|
|
893
|
-
__name(
|
|
895
|
+
__name(initAsaiHostNodejs, "initAsaiHostNodejs");
|
|
894
896
|
function scheduleLogComplianceCheck($asai) {
|
|
895
897
|
const hours = Number($asai?.hostconfig?.logger?.complianceIntervalHours) || 6;
|
|
896
898
|
if (hours <= 0) return;
|
|
897
899
|
const ms = hours * 3600 * 1e3;
|
|
898
900
|
setInterval(() => {
|
|
899
|
-
void import("./LogStore-
|
|
901
|
+
void import("./LogStore-Q35YFNAY.es.js").then(
|
|
900
902
|
({ getComplianceReport: getComplianceReport2 }) => getComplianceReport2($asai, "prod")
|
|
901
903
|
).catch(() => void 0);
|
|
902
904
|
}, ms);
|
|
@@ -947,16 +949,61 @@ function getWsUserinfo(req, $asai) {
|
|
|
947
949
|
}
|
|
948
950
|
}
|
|
949
951
|
__name(getWsUserinfo, "getWsUserinfo");
|
|
952
|
+
function listOnlineWsUsers($asai, hostdir) {
|
|
953
|
+
const users = [];
|
|
954
|
+
const map = $asai.connectionsws || {};
|
|
955
|
+
const dirs = hostdir ? [hostdir] : Object.keys(map);
|
|
956
|
+
for (const dir of dirs) {
|
|
957
|
+
for (const [us, ws2] of Object.entries(map[dir] || {})) {
|
|
958
|
+
if (!us || us === "asai") continue;
|
|
959
|
+
if (isWsStillActive(ws2)) {
|
|
960
|
+
users.push(us);
|
|
961
|
+
} else if (map[dir]?.[us] === ws2) {
|
|
962
|
+
delete map[dir][us];
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return [...new Set(users)];
|
|
967
|
+
}
|
|
968
|
+
__name(listOnlineWsUsers, "listOnlineWsUsers");
|
|
950
969
|
function isUserOnlineOnWs($asai, us, hostdir) {
|
|
951
970
|
if (!us || us === "asai") return false;
|
|
952
971
|
const map = $asai.connectionsws || {};
|
|
953
972
|
const dirs = hostdir ? [hostdir] : Object.keys(map);
|
|
954
973
|
for (const dir of dirs) {
|
|
955
|
-
|
|
974
|
+
const ws2 = map[dir]?.[us];
|
|
975
|
+
if (!ws2) continue;
|
|
976
|
+
if (isWsStillActive(ws2)) return true;
|
|
977
|
+
if (map[dir]?.[us] === ws2) delete map[dir][us];
|
|
956
978
|
}
|
|
957
979
|
return false;
|
|
958
980
|
}
|
|
959
981
|
__name(isUserOnlineOnWs, "isUserOnlineOnWs");
|
|
982
|
+
function kickUserWs($asai, us, hostdir) {
|
|
983
|
+
if (!us || us === "asai") return;
|
|
984
|
+
const map = $asai.connectionsws || {};
|
|
985
|
+
const dirs = hostdir ? [hostdir] : Object.keys(map);
|
|
986
|
+
for (const dir of dirs) {
|
|
987
|
+
const ws2 = map[dir]?.[us];
|
|
988
|
+
if (!ws2) continue;
|
|
989
|
+
try {
|
|
990
|
+
ws2.close();
|
|
991
|
+
} catch {
|
|
992
|
+
}
|
|
993
|
+
if (map[dir]?.[us] === ws2) delete map[dir][us];
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
__name(kickUserWs, "kickUserWs");
|
|
997
|
+
async function removeSessionOnWsClose($asai, us, token) {
|
|
998
|
+
try {
|
|
999
|
+
const store = createUserStore($asai);
|
|
1000
|
+
const sessions = await store.getSessions();
|
|
1001
|
+
const match = sessions.find((s) => s.us === us && s.pub === token);
|
|
1002
|
+
if (match) await store.removeSession(match.id);
|
|
1003
|
+
} catch {
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
__name(removeSessionOnWsClose, "removeSessionOnWsClose");
|
|
960
1007
|
function isWsStillActive(ws2) {
|
|
961
1008
|
if (!ws2) return false;
|
|
962
1009
|
const state = ws2.readyState;
|
|
@@ -1039,8 +1086,11 @@ function isOkWs($asai, hostdir, ws2, _req, hostcfg) {
|
|
|
1039
1086
|
__name(isOkWs, "isOkWs");
|
|
1040
1087
|
function loginWs($asai, hostdir, ws2, req, hostcfg, wsWorkHandler, logWsMessage) {
|
|
1041
1088
|
const [us] = ws2.Userinfo || [];
|
|
1042
|
-
if (
|
|
1043
|
-
|
|
1089
|
+
if (!$asai.connectionsws[hostdir]) {
|
|
1090
|
+
$asai.connectionsws[hostdir] = {};
|
|
1091
|
+
}
|
|
1092
|
+
if (us && us !== "asai") {
|
|
1093
|
+
if (hostcfg.ckws && !ws2.tasksws) ws2.tasksws = {};
|
|
1044
1094
|
$asai.connectionsws[hostdir][us] = ws2;
|
|
1045
1095
|
}
|
|
1046
1096
|
ws2.on("message", (msg) => {
|
|
@@ -1048,12 +1098,14 @@ function loginWs($asai, hostdir, ws2, req, hostcfg, wsWorkHandler, logWsMessage)
|
|
|
1048
1098
|
wsWorkHandler?.(msg, ws2, hostdir, req);
|
|
1049
1099
|
});
|
|
1050
1100
|
ws2.on("close", () => {
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1101
|
+
const token = ws2.Userinfo?.[2];
|
|
1102
|
+
if (hostcfg.ckws && ws2.tasksws) {
|
|
1103
|
+
Object.values(ws2.tasksws).forEach((el) => el && clearInterval(el));
|
|
1104
|
+
ws2.tasksws = void 0;
|
|
1105
|
+
}
|
|
1106
|
+
if (us && $asai.connectionsws[hostdir]?.[us] === ws2) delete $asai.connectionsws[hostdir][us];
|
|
1107
|
+
if (us && token && !isUserOnlineOnWs($asai, us, hostdir)) {
|
|
1108
|
+
void removeSessionOnWsClose($asai, us, String(token));
|
|
1057
1109
|
}
|
|
1058
1110
|
if (ws2.readyState !== WebSocket.CLOSED && ws2.readyState !== WebSocket.CLOSING) ws2.close();
|
|
1059
1111
|
});
|
|
@@ -1406,11 +1458,11 @@ function createSkipChecker($asai) {
|
|
|
1406
1458
|
}
|
|
1407
1459
|
if (exact.size === 0 && prefixes.length === 0) return () => false;
|
|
1408
1460
|
return (url) => {
|
|
1409
|
-
const
|
|
1410
|
-
if (!
|
|
1411
|
-
if (exact.has(
|
|
1461
|
+
const path7 = normalizeRequestPath(url);
|
|
1462
|
+
if (!path7) return false;
|
|
1463
|
+
if (exact.has(path7)) return true;
|
|
1412
1464
|
for (let i = 0; i < prefixes.length; i++) {
|
|
1413
|
-
if (
|
|
1465
|
+
if (path7.startsWith(prefixes[i])) return true;
|
|
1414
1466
|
}
|
|
1415
1467
|
return false;
|
|
1416
1468
|
};
|
|
@@ -1855,7 +1907,7 @@ function releasePort(port, opts) {
|
|
|
1855
1907
|
const occupants = findListeningPids(port).filter((p) => p !== exclude && p !== OWN_PID);
|
|
1856
1908
|
if (!occupants.length) return true;
|
|
1857
1909
|
const label = opts?.label ? `[${opts.label}] ` : "";
|
|
1858
|
-
console.warn(`[
|
|
1910
|
+
console.warn(`[AsaiHostNodejs] ${label}\u7AEF\u53E3 ${port} \u88AB\u5360\u7528\uFF0C\u6B63\u5728\u7ED3\u675F\u8FDB\u7A0B: ${occupants.join(", ")}`);
|
|
1859
1911
|
for (const pid of occupants) {
|
|
1860
1912
|
killPid(pid);
|
|
1861
1913
|
}
|
|
@@ -2066,18 +2118,18 @@ function asaiGuard($asai, failedHostdir = "asai") {
|
|
|
2066
2118
|
const sameProcessOwner = findSameProcessPortOwner($asai, port, failedHostdir);
|
|
2067
2119
|
if (sameProcessOwner) {
|
|
2068
2120
|
console.error(
|
|
2069
|
-
`[
|
|
2121
|
+
`[AsaiHostNodejs] \u7AEF\u53E3 ${port} \u5DF2\u88AB\u540C\u8FDB\u7A0B\u7AD9\u70B9 [${sameProcessOwner}] \u5360\u7528\uFF0C[${failedHostdir}] \u505C\u6B62\u91CD\u8BD5\uFF08\u8BF7\u68C0\u67E5 asaihost.json \u662F\u5426\u91CD\u590D\u914D\u7F6E port\uFF09`
|
|
2070
2122
|
);
|
|
2071
2123
|
return;
|
|
2072
2124
|
}
|
|
2073
2125
|
const released = releasePort(port, { excludePid: process.pid, label: failedHostdir });
|
|
2074
2126
|
$asai.hostconfig.asaiport = conflictPorts.filter((p) => p !== port);
|
|
2075
2127
|
if (released) {
|
|
2076
|
-
console.warn(`[
|
|
2128
|
+
console.warn(`[AsaiHostNodejs] \u7AEF\u53E3 ${port} \u5DF2\u91CA\u653E\uFF0C\u6B63\u5728\u91CD\u65B0\u542F\u52A8 [${failedHostdir}]`);
|
|
2077
2129
|
scheduleHttpRetry($asai, failedHostdir);
|
|
2078
2130
|
return;
|
|
2079
2131
|
}
|
|
2080
|
-
console.error(`[
|
|
2132
|
+
console.error(`[AsaiHostNodejs] \u7AEF\u53E3 ${port} \u65E0\u6CD5\u91CA\u653E\uFF0C\u542F\u52A8\u5931\u8D25\uFF08netstat -ano | findstr :${port}\uFF09`);
|
|
2081
2133
|
process.exit(1);
|
|
2082
2134
|
}
|
|
2083
2135
|
__name(asaiGuard, "asaiGuard");
|
|
@@ -2184,7 +2236,7 @@ function startProxyServer($asai, proxyhostdir) {
|
|
|
2184
2236
|
label: proxyhostdir
|
|
2185
2237
|
});
|
|
2186
2238
|
if (released) {
|
|
2187
|
-
console.warn(`[
|
|
2239
|
+
console.warn(`[AsaiHostNodejs] \u4EE3\u7406\u7AEF\u53E3 ${proxycfg.proxyport} \u5DF2\u91CA\u653E\uFF0C\u6B63\u5728\u91CD\u65B0\u542F\u52A8 [${proxyhostdir}]`);
|
|
2188
2240
|
setTimeout(() => startProxyServer($asai, proxyhostdir), $asai.hostconfig.tm?.d || 2e3);
|
|
2189
2241
|
}
|
|
2190
2242
|
} else {
|
|
@@ -2223,8 +2275,8 @@ function src_default($asai, opt) {
|
|
|
2223
2275
|
workHandlers.set("apiWork", opt.apiWork);
|
|
2224
2276
|
workHandlers.set("wsWork", opt.wsWork);
|
|
2225
2277
|
workHandlers.set("jsonpWork", opt.jsonpWork);
|
|
2226
|
-
function
|
|
2227
|
-
|
|
2278
|
+
function StartAsaiHostNodejs() {
|
|
2279
|
+
initAsaiHostNodejs($asai, opt);
|
|
2228
2280
|
console.log(`\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B\u203B`);
|
|
2229
2281
|
releaseConfiguredPorts($asai);
|
|
2230
2282
|
if ($asai.hostconfig?.hosts) {
|
|
@@ -2246,8 +2298,8 @@ function src_default($asai, opt) {
|
|
|
2246
2298
|
});
|
|
2247
2299
|
}
|
|
2248
2300
|
}
|
|
2249
|
-
__name(
|
|
2250
|
-
return {
|
|
2301
|
+
__name(StartAsaiHostNodejs, "StartAsaiHostNodejs");
|
|
2302
|
+
return { StartAsaiHostNodejs, asaiWs: ws };
|
|
2251
2303
|
}
|
|
2252
2304
|
__name(src_default, "default");
|
|
2253
2305
|
|
|
@@ -2319,7 +2371,7 @@ async function attachServerUserLevel(req, $asai) {
|
|
|
2319
2371
|
if (!Array.isArray(userinfo) || !userinfo[0]) return;
|
|
2320
2372
|
const us = String(userinfo[0]);
|
|
2321
2373
|
try {
|
|
2322
|
-
const { createUserStore: createUserStore2 } = await import("./user-store-
|
|
2374
|
+
const { createUserStore: createUserStore2 } = await import("./user-store-UC5URX3C.es.js");
|
|
2323
2375
|
const user = await createUserStore2($asai).findByUsername(us);
|
|
2324
2376
|
if (user && user.status === "active") {
|
|
2325
2377
|
req._serverUser = { us: user.us, lv: user.lv, status: user.status };
|
|
@@ -2350,11 +2402,11 @@ function getSecurityConfig($asai) {
|
|
|
2350
2402
|
return $asai?.hostconfig?.security || {};
|
|
2351
2403
|
}
|
|
2352
2404
|
__name(getSecurityConfig, "getSecurityConfig");
|
|
2353
|
-
function pathMatchesPattern(
|
|
2405
|
+
function pathMatchesPattern(path7, pattern) {
|
|
2354
2406
|
if (pattern.endsWith("*")) {
|
|
2355
|
-
return
|
|
2407
|
+
return path7.startsWith(pattern.slice(0, -1));
|
|
2356
2408
|
}
|
|
2357
|
-
return
|
|
2409
|
+
return path7 === pattern;
|
|
2358
2410
|
}
|
|
2359
2411
|
__name(pathMatchesPattern, "pathMatchesPattern");
|
|
2360
2412
|
function isAuthSkipped($asai, pathname) {
|
|
@@ -2502,6 +2554,54 @@ function isApiCorsAllowed(origin, hostcfg, hostHeader) {
|
|
|
2502
2554
|
__name(isApiCorsAllowed, "isApiCorsAllowed");
|
|
2503
2555
|
|
|
2504
2556
|
// src/common/server/Api.ts
|
|
2557
|
+
function readRequestBody(req, maxBody) {
|
|
2558
|
+
return new Promise((resolve6, reject) => {
|
|
2559
|
+
const chunks = [];
|
|
2560
|
+
let received = 0;
|
|
2561
|
+
let settled = false;
|
|
2562
|
+
const finish = /* @__PURE__ */ __name((err, data) => {
|
|
2563
|
+
if (settled) return;
|
|
2564
|
+
settled = true;
|
|
2565
|
+
if (err) reject(err);
|
|
2566
|
+
else resolve6(data ?? "");
|
|
2567
|
+
}, "finish");
|
|
2568
|
+
req.on("data", (chunk) => {
|
|
2569
|
+
if (settled) return;
|
|
2570
|
+
received += chunk.length;
|
|
2571
|
+
if (received > maxBody) {
|
|
2572
|
+
req.destroy();
|
|
2573
|
+
finish(new Error("PAYLOAD_TOO_LARGE"));
|
|
2574
|
+
return;
|
|
2575
|
+
}
|
|
2576
|
+
chunks.push(chunk);
|
|
2577
|
+
});
|
|
2578
|
+
req.on("end", () => finish(null, Buffer.concat(chunks).toString("utf8")));
|
|
2579
|
+
req.on("error", (err) => finish(err));
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
__name(readRequestBody, "readRequestBody");
|
|
2583
|
+
function processPostPayload($asai, req, postdata, pm) {
|
|
2584
|
+
let isAes = false;
|
|
2585
|
+
let out = postdata;
|
|
2586
|
+
if ($asai.$lib.aesfns && out?.startsWith?.('{"aes":')) {
|
|
2587
|
+
isAes = true;
|
|
2588
|
+
const aesData = JSON.parse(out);
|
|
2589
|
+
out = $asai.$lib.aesfns.reqAES({ data: aesData });
|
|
2590
|
+
if (aesData?.aes === 2 && typeof out === "string") {
|
|
2591
|
+
try {
|
|
2592
|
+
out = JSON.parse(out);
|
|
2593
|
+
} catch {
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
const plainJson = typeof out === "string" && out.trimStart().startsWith("{");
|
|
2598
|
+
if (req?.Userinfo?.[2] && !plainJson) {
|
|
2599
|
+
pm = req.Userinfo[2];
|
|
2600
|
+
out = $asai.$lib.AsCode.asdecode(out, pm);
|
|
2601
|
+
}
|
|
2602
|
+
return { postdata: out, pm, isAes };
|
|
2603
|
+
}
|
|
2604
|
+
__name(processPostPayload, "processPostPayload");
|
|
2505
2605
|
var Api_default = /* @__PURE__ */ __name(($asai, opt = {}) => {
|
|
2506
2606
|
opt?.fn?.($asai);
|
|
2507
2607
|
function getHeader(req, key) {
|
|
@@ -2581,8 +2681,24 @@ var Api_default = /* @__PURE__ */ __name(($asai, opt = {}) => {
|
|
|
2581
2681
|
const releaseSlot = /* @__PURE__ */ __name(() => releaseApiSlot(hostdir, clientIp), "releaseSlot");
|
|
2582
2682
|
res.once("finish", releaseSlot);
|
|
2583
2683
|
res.once("close", releaseSlot);
|
|
2684
|
+
const maxBody = $asai.hostconfig?.maxbody || 100 * 1024 * 1024;
|
|
2685
|
+
const isTextPost = req.method === "POST" && req.url.indexOf("binary") === -1;
|
|
2686
|
+
const bodyPromise = isTextPost ? readRequestBody(req, maxBody) : null;
|
|
2584
2687
|
void (async () => {
|
|
2585
|
-
|
|
2688
|
+
let authResult;
|
|
2689
|
+
let rawBody = "";
|
|
2690
|
+
try {
|
|
2691
|
+
[authResult, rawBody] = await Promise.all([
|
|
2692
|
+
validateHttpAuthAsync(req, $asai, pathname),
|
|
2693
|
+
bodyPromise ?? Promise.resolve("")
|
|
2694
|
+
]);
|
|
2695
|
+
} catch (err) {
|
|
2696
|
+
releaseSlot();
|
|
2697
|
+
if (err?.message === "PAYLOAD_TOO_LARGE") {
|
|
2698
|
+
return sendError(res, 413, "Payload Too Large");
|
|
2699
|
+
}
|
|
2700
|
+
return sendError(res, 400, err?.message || "Bad Request");
|
|
2701
|
+
}
|
|
2586
2702
|
if (!authResult.ok) {
|
|
2587
2703
|
releaseSlot();
|
|
2588
2704
|
return sendAuthError(res, authResult);
|
|
@@ -2596,52 +2712,25 @@ var Api_default = /* @__PURE__ */ __name(($asai, opt = {}) => {
|
|
|
2596
2712
|
let qrdata;
|
|
2597
2713
|
let postdata;
|
|
2598
2714
|
if (req.url.indexOf("binary") === -1) {
|
|
2599
|
-
postdata = "";
|
|
2600
2715
|
qrdata = query;
|
|
2601
2716
|
if (req.method === "POST") {
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
}
|
|
2615
|
-
chunks.push(chunk);
|
|
2616
|
-
});
|
|
2617
|
-
req.on("end", () => {
|
|
2618
|
-
if (aborted) return;
|
|
2619
|
-
postdata = Buffer.concat(chunks).toString();
|
|
2620
|
-
isAes = $asai.$lib.aesfns && postdata?.startsWith?.('{"aes":');
|
|
2621
|
-
if (isAes) {
|
|
2622
|
-
const aesData = JSON.parse(postdata);
|
|
2623
|
-
postdata = $asai.$lib.aesfns.reqAES({ data: aesData });
|
|
2624
|
-
if (aesData?.aes === 2) {
|
|
2625
|
-
try {
|
|
2626
|
-
postdata = JSON.parse(postdata);
|
|
2627
|
-
} catch (err) {
|
|
2628
|
-
}
|
|
2629
|
-
}
|
|
2630
|
-
}
|
|
2631
|
-
if (req?.Userinfo && req?.Userinfo[2]) {
|
|
2632
|
-
pm = req.Userinfo[2];
|
|
2633
|
-
postdata = $asai.$lib.AsCode.asdecode(postdata, pm);
|
|
2634
|
-
}
|
|
2635
|
-
if (apiFn?.post && typeof apiFn?.post === "function") {
|
|
2636
|
-
const optTmp = { qrdata, data: postdata, pm };
|
|
2637
|
-
if (isAes || $asai.$lib.aesfns?.allaes) {
|
|
2638
|
-
optTmp.resAES = $asai.$lib.aesfns?.resAES;
|
|
2639
|
-
}
|
|
2640
|
-
apiFn.post(req, res, hostdir, optTmp);
|
|
2641
|
-
} else {
|
|
2642
|
-
res.end(postdata);
|
|
2717
|
+
try {
|
|
2718
|
+
const processed = processPostPayload($asai, req, rawBody, pm);
|
|
2719
|
+
postdata = processed.postdata;
|
|
2720
|
+
pm = processed.pm;
|
|
2721
|
+
isAes = processed.isAes;
|
|
2722
|
+
} catch {
|
|
2723
|
+
return sendError(res, 400, "AES decrypt failed");
|
|
2724
|
+
}
|
|
2725
|
+
if (apiFn?.post && typeof apiFn?.post === "function") {
|
|
2726
|
+
const optTmp = { qrdata, data: postdata, pm };
|
|
2727
|
+
if (isAes || $asai.$lib.aesfns?.allaes) {
|
|
2728
|
+
optTmp.resAES = $asai.$lib.aesfns?.resAES;
|
|
2643
2729
|
}
|
|
2644
|
-
|
|
2730
|
+
apiFn.post(req, res, hostdir, optTmp);
|
|
2731
|
+
} else {
|
|
2732
|
+
res.end(typeof postdata === "string" ? postdata : JSON.stringify(postdata ?? ""));
|
|
2733
|
+
}
|
|
2645
2734
|
} else if (req.method === "GET") {
|
|
2646
2735
|
if (apiFn?.get && typeof apiFn?.get === "function") {
|
|
2647
2736
|
const optTmp = { qrdata, data: qrdata, pm };
|
|
@@ -2845,7 +2934,7 @@ var WsClient_default = /* @__PURE__ */ __name(($asai, wsname) => {
|
|
|
2845
2934
|
}
|
|
2846
2935
|
__name(getKeyByData, "getKeyByData");
|
|
2847
2936
|
function clientwsInit() {
|
|
2848
|
-
return new Promise((
|
|
2937
|
+
return new Promise((resolve6, reject) => {
|
|
2849
2938
|
$asai.clientws[wsname].socket = new $asai.asaiWs(
|
|
2850
2939
|
$asai.command.commandconfig.clientws[wsname]
|
|
2851
2940
|
);
|
|
@@ -2875,7 +2964,7 @@ var WsClient_default = /* @__PURE__ */ __name(($asai, wsname) => {
|
|
|
2875
2964
|
`${wsname}=${$asai.command.commandconfig.clientws[wsname]} is open.`
|
|
2876
2965
|
);
|
|
2877
2966
|
$asai.clientws[wsname].open = 1;
|
|
2878
|
-
|
|
2967
|
+
resolve6();
|
|
2879
2968
|
};
|
|
2880
2969
|
});
|
|
2881
2970
|
}
|
|
@@ -2905,7 +2994,7 @@ var WsClient_default = /* @__PURE__ */ __name(($asai, wsname) => {
|
|
|
2905
2994
|
}
|
|
2906
2995
|
__name(clientwsOnmessage, "clientwsOnmessage");
|
|
2907
2996
|
function clientwsApi(messageData) {
|
|
2908
|
-
return new Promise((
|
|
2997
|
+
return new Promise((resolve6, reject) => {
|
|
2909
2998
|
if ($asai.clientws[wsname].open) {
|
|
2910
2999
|
try {
|
|
2911
3000
|
const taskOpt = {};
|
|
@@ -2917,7 +3006,7 @@ var WsClient_default = /* @__PURE__ */ __name(($asai, wsname) => {
|
|
|
2917
3006
|
getKeyByData(messageData),
|
|
2918
3007
|
{
|
|
2919
3008
|
callback: /* @__PURE__ */ __name((res) => {
|
|
2920
|
-
|
|
3009
|
+
resolve6(res);
|
|
2921
3010
|
}, "callback"),
|
|
2922
3011
|
...messageData,
|
|
2923
3012
|
...taskOpt
|
|
@@ -3434,7 +3523,7 @@ var unifiedlog_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
3434
3523
|
if (url.startsWith("/api/asailog/store/formats/")) {
|
|
3435
3524
|
if (!await ensureLogReadAccess(req, $asai, getPermissionThreshold($asai, "logRead"))) return sendFail(res, "Forbidden", opt);
|
|
3436
3525
|
try {
|
|
3437
|
-
const { getLogFormatTaxonomy } = await import("./log-formats-
|
|
3526
|
+
const { getLogFormatTaxonomy } = await import("./log-formats-WW3TDUAK.es.js");
|
|
3438
3527
|
return sendSuccess(res, getLogFormatTaxonomy($asai), opt);
|
|
3439
3528
|
} catch (err) {
|
|
3440
3529
|
return sendFail(res, err?.message || err, opt);
|
|
@@ -3798,7 +3887,7 @@ __name(buildSessionRecord, "buildSessionRecord");
|
|
|
3798
3887
|
var API_ADMIN_PREFIXES = ["/api/user/login/admin/"];
|
|
3799
3888
|
function getMinLevelForApiPath(pathname, $asai) {
|
|
3800
3889
|
if (API_ADMIN_PREFIXES.some((p) => pathname.startsWith(p))) {
|
|
3801
|
-
if (pathname.includes("/admin/update") || pathname.includes("/admin/kick") || pathname.includes("/admin/delete")) {
|
|
3890
|
+
if (pathname.includes("/admin/update") || pathname.includes("/admin/kick") || pathname.includes("/admin/delete") || pathname.includes("/admin/resetpass")) {
|
|
3802
3891
|
return $asai ? getPermissionThreshold($asai, "adminWrite") : USER_LV.DEVELOPER;
|
|
3803
3892
|
}
|
|
3804
3893
|
return $asai ? getPermissionThreshold($asai, "adminRead") : USER_LV.ADMIN;
|
|
@@ -3811,19 +3900,98 @@ function hasLevel(userLevel, required) {
|
|
|
3811
3900
|
}
|
|
3812
3901
|
__name(hasLevel, "hasLevel");
|
|
3813
3902
|
|
|
3814
|
-
// src/sysserver/api/login/
|
|
3815
|
-
function
|
|
3816
|
-
|
|
3817
|
-
|
|
3903
|
+
// src/sysserver/api/login/service/request-body.ts
|
|
3904
|
+
function isAesEnvelope(body) {
|
|
3905
|
+
return typeof body.data === "string" && typeof body.iv === "string" && typeof body.auth === "string" && body.aes != null && body.us == null && body.pass == null && body.password == null;
|
|
3906
|
+
}
|
|
3907
|
+
__name(isAesEnvelope, "isAesEnvelope");
|
|
3908
|
+
function unwrapJsonString(value) {
|
|
3909
|
+
if (typeof value !== "string") return value;
|
|
3910
|
+
const text = value.trim();
|
|
3911
|
+
if (!text.startsWith("{") && !text.startsWith("[")) return value;
|
|
3912
|
+
try {
|
|
3913
|
+
return JSON.parse(text);
|
|
3914
|
+
} catch {
|
|
3915
|
+
return value;
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
__name(unwrapJsonString, "unwrapJsonString");
|
|
3919
|
+
function parseAuthBody(opt) {
|
|
3920
|
+
let raw = opt?.data;
|
|
3921
|
+
if (raw == null || raw === "") return {};
|
|
3922
|
+
if (typeof raw === "string") {
|
|
3923
|
+
const text = raw.trim();
|
|
3924
|
+
if (!text) return {};
|
|
3818
3925
|
try {
|
|
3819
|
-
|
|
3926
|
+
raw = JSON.parse(text);
|
|
3820
3927
|
} catch {
|
|
3821
|
-
|
|
3928
|
+
if (text.includes("=") && !text.startsWith("{") && !text.startsWith("[")) {
|
|
3929
|
+
raw = Object.fromEntries(new URLSearchParams(text));
|
|
3930
|
+
} else {
|
|
3931
|
+
return {};
|
|
3932
|
+
}
|
|
3822
3933
|
}
|
|
3934
|
+
raw = unwrapJsonString(raw);
|
|
3823
3935
|
}
|
|
3824
|
-
|
|
3936
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
3937
|
+
const body = raw;
|
|
3938
|
+
if (isAesEnvelope(body)) return {};
|
|
3939
|
+
return body;
|
|
3940
|
+
}
|
|
3941
|
+
__name(parseAuthBody, "parseAuthBody");
|
|
3942
|
+
function pickAuthField(body, ...keys) {
|
|
3943
|
+
for (const key of keys) {
|
|
3944
|
+
const val = body[key];
|
|
3945
|
+
if (val != null && String(val).length > 0) return String(val);
|
|
3946
|
+
}
|
|
3947
|
+
return "";
|
|
3825
3948
|
}
|
|
3826
|
-
__name(
|
|
3949
|
+
__name(pickAuthField, "pickAuthField");
|
|
3950
|
+
|
|
3951
|
+
// src/sysserver/api/login/service/online-sessions.ts
|
|
3952
|
+
function resolveOnlineSessions($asai, hostdir, allSessions) {
|
|
3953
|
+
const onlineUsers = new Set(listOnlineWsUsers($asai, hostdir));
|
|
3954
|
+
if (!onlineUsers.size) return [];
|
|
3955
|
+
const byUser = /* @__PURE__ */ new Map();
|
|
3956
|
+
for (const s of allSessions) {
|
|
3957
|
+
if (!onlineUsers.has(s.us)) continue;
|
|
3958
|
+
const prev = byUser.get(s.us);
|
|
3959
|
+
const sTime = s.lastActivityAt ?? s.createdAt;
|
|
3960
|
+
const prevTime = prev ? prev.lastActivityAt ?? prev.createdAt : 0;
|
|
3961
|
+
if (!prev || sTime > prevTime) byUser.set(s.us, s);
|
|
3962
|
+
}
|
|
3963
|
+
return [...byUser.values()].sort((a, b) => b.createdAt - a.createdAt);
|
|
3964
|
+
}
|
|
3965
|
+
__name(resolveOnlineSessions, "resolveOnlineSessions");
|
|
3966
|
+
function isSessionUserOnline($asai, us, hostdir) {
|
|
3967
|
+
if (!us) return false;
|
|
3968
|
+
return isUserOnlineOnWs($asai, us, hostdir);
|
|
3969
|
+
}
|
|
3970
|
+
__name(isSessionUserOnline, "isSessionUserOnline");
|
|
3971
|
+
async function countOnlineSessions($asai, hostdir, store) {
|
|
3972
|
+
const all = await store.getSessions();
|
|
3973
|
+
return resolveOnlineSessions($asai, hostdir, all).length;
|
|
3974
|
+
}
|
|
3975
|
+
__name(countOnlineSessions, "countOnlineSessions");
|
|
3976
|
+
async function countOnlineSessionsByUser($asai, hostdir, store, us) {
|
|
3977
|
+
const all = await store.getSessions();
|
|
3978
|
+
return resolveOnlineSessions($asai, hostdir, all).filter((s) => s.us === us).length;
|
|
3979
|
+
}
|
|
3980
|
+
__name(countOnlineSessionsByUser, "countOnlineSessionsByUser");
|
|
3981
|
+
async function purgeOfflineSessions($asai, hostdir, store) {
|
|
3982
|
+
const onlineUsers = new Set(listOnlineWsUsers($asai, hostdir));
|
|
3983
|
+
const all = await store.getSessions();
|
|
3984
|
+
let purged = 0;
|
|
3985
|
+
for (const s of all) {
|
|
3986
|
+
if (onlineUsers.has(s.us)) continue;
|
|
3987
|
+
await store.removeSession(s.id);
|
|
3988
|
+
purged += 1;
|
|
3989
|
+
}
|
|
3990
|
+
return purged;
|
|
3991
|
+
}
|
|
3992
|
+
__name(purgeOfflineSessions, "purgeOfflineSessions");
|
|
3993
|
+
|
|
3994
|
+
// src/sysserver/api/login/index.ts
|
|
3827
3995
|
function getSecurity($asai) {
|
|
3828
3996
|
return $asai?.hostconfig?.security || {};
|
|
3829
3997
|
}
|
|
@@ -3833,16 +4001,16 @@ function getQuota2($asai) {
|
|
|
3833
4001
|
return sec.quota || {};
|
|
3834
4002
|
}
|
|
3835
4003
|
__name(getQuota2, "getQuota");
|
|
3836
|
-
async function canLogin($asai, us) {
|
|
4004
|
+
async function canLogin($asai, us, hostdir) {
|
|
3837
4005
|
const quota = getQuota2($asai);
|
|
3838
4006
|
if (!quota.enabled) return { ok: true };
|
|
3839
4007
|
const store = createUserStore($asai);
|
|
3840
|
-
const total = await store
|
|
4008
|
+
const total = await countOnlineSessions($asai, hostdir, store);
|
|
3841
4009
|
const maxLogin = quota.maxLoginSessions ?? $asai?.hostconfig?.hosts?.default?.maxonline ?? 100;
|
|
3842
4010
|
if (total >= maxLogin) {
|
|
3843
4011
|
return { ok: false, message: "LOGIN_QUOTA_FULL" };
|
|
3844
4012
|
}
|
|
3845
|
-
const perUser = await store
|
|
4013
|
+
const perUser = await countOnlineSessionsByUser($asai, hostdir, store, us);
|
|
3846
4014
|
const maxPerUser = quota.maxSessionsPerUser ?? 3;
|
|
3847
4015
|
if (perUser >= maxPerUser) {
|
|
3848
4016
|
return { ok: false, message: "USER_SESSION_LIMIT" };
|
|
@@ -3882,13 +4050,13 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
3882
4050
|
async function post(req, res, hostdir, opt) {
|
|
3883
4051
|
const url = req.url || "";
|
|
3884
4052
|
const ip = getClientIp(req);
|
|
3885
|
-
const body =
|
|
4053
|
+
const body = parseAuthBody(opt);
|
|
3886
4054
|
if (url.startsWith("/api/user/login/changepass/")) {
|
|
3887
4055
|
await ensureReady($asai);
|
|
3888
4056
|
try {
|
|
3889
|
-
const us = sanitizeString(body
|
|
3890
|
-
const pass = sanitizeString(body
|
|
3891
|
-
const newPass = sanitizeString(body
|
|
4057
|
+
const us = sanitizeString(pickAuthField(body, "us", "username"), 64);
|
|
4058
|
+
const pass = sanitizeString(pickAuthField(body, "pass", "password", "pwd"), 128);
|
|
4059
|
+
const newPass = sanitizeString(pickAuthField(body, "newPass", "newPassword", "newpass"), 128);
|
|
3892
4060
|
if (!us || !pass || !newPass) return sendErr(res, "AUTH_CREDENTIALS_REQUIRED", void 0, opt);
|
|
3893
4061
|
const user = await store.findByUsername(us);
|
|
3894
4062
|
if (!user || !verifyPassword(pass, user.passHash, user.passSalt)) {
|
|
@@ -3922,9 +4090,11 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
3922
4090
|
return sendErr(res, "AUTH_REG_DISABLED", void 0, opt);
|
|
3923
4091
|
}
|
|
3924
4092
|
try {
|
|
3925
|
-
const us = sanitizeString(body
|
|
3926
|
-
const pass = sanitizeString(body
|
|
3927
|
-
const
|
|
4093
|
+
const us = sanitizeString(pickAuthField(body, "us", "username"), 64);
|
|
4094
|
+
const pass = sanitizeString(pickAuthField(body, "pass", "password", "pwd"), 128);
|
|
4095
|
+
const emailRaw = pickAuthField(body, "email");
|
|
4096
|
+
const email = emailRaw ? sanitizeString(emailRaw, 128) : void 0;
|
|
4097
|
+
if (!us || !pass) return sendErr(res, "AUTH_CREDENTIALS_REQUIRED", void 0, opt);
|
|
3928
4098
|
const policyErr = validatePasswordChange(pass, $asai);
|
|
3929
4099
|
if (policyErr === "PASSWORD_TOO_SHORT") {
|
|
3930
4100
|
return sendErr(res, policyErr, [String(getPasswordPolicy($asai).minLength)], opt);
|
|
@@ -3953,8 +4123,8 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
3953
4123
|
if (url.startsWith("/api/user/login/auth/")) {
|
|
3954
4124
|
await ensureReady($asai);
|
|
3955
4125
|
try {
|
|
3956
|
-
const us = sanitizeString(body
|
|
3957
|
-
const pass = sanitizeString(body
|
|
4126
|
+
const us = sanitizeString(pickAuthField(body, "us", "username"), 64);
|
|
4127
|
+
const pass = sanitizeString(pickAuthField(body, "pass", "password", "pwd"), 128);
|
|
3958
4128
|
if (!us || !pass) return sendErr(res, "AUTH_CREDENTIALS_REQUIRED", void 0, opt);
|
|
3959
4129
|
const user = await store.findByUsername(us);
|
|
3960
4130
|
if (!user || !verifyPassword(pass, user.passHash, user.passSalt)) {
|
|
@@ -3983,17 +4153,9 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
3983
4153
|
const mustChange = !!user.mustChangePassword || isPasswordExpired(user.passwordChangedAt, policy.maxAgeDays);
|
|
3984
4154
|
const hostcfg = $asai.getHost?.(hostdir) || {};
|
|
3985
4155
|
if (hostcfg.ckws && isUserOnlineOnWs($asai, us, hostdir)) {
|
|
3986
|
-
|
|
3987
|
-
type: "AUTH_FAIL",
|
|
3988
|
-
path: url,
|
|
3989
|
-
method: "POST",
|
|
3990
|
-
user: us,
|
|
3991
|
-
ip,
|
|
3992
|
-
detail: "user_already_online"
|
|
3993
|
-
});
|
|
3994
|
-
return sendErr(res, "USER_ALREADY_LOGGED", void 0, opt);
|
|
4156
|
+
kickUserWs($asai, us, hostdir);
|
|
3995
4157
|
}
|
|
3996
|
-
const quotaCheck = await canLogin($asai, us);
|
|
4158
|
+
const quotaCheck = await canLogin($asai, us, hostdir);
|
|
3997
4159
|
if (!quotaCheck.ok) {
|
|
3998
4160
|
logSecurityEvent($asai, { type: "RATE_LIMIT", path: url, method: "POST", user: us, ip, detail: quotaCheck.message });
|
|
3999
4161
|
return sendErr(res, quotaCheck.message || "LOGIN_QUOTA_FULL", void 0, opt);
|
|
@@ -4037,12 +4199,17 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4037
4199
|
if (!sessionId) return sendErr(res, "AUTH_CREDENTIALS_REQUIRED", void 0, opt);
|
|
4038
4200
|
const sessions = await store.getSessions();
|
|
4039
4201
|
const target = sessions.find((s) => s.id === sessionId);
|
|
4202
|
+
if (!target) return sendErr(res, "AUTH_SESSION_NOT_FOUND", void 0, opt);
|
|
4203
|
+
if (!isSessionUserOnline($asai, target.us, hostdir)) {
|
|
4204
|
+
return sendErr(res, "SESSION_NOT_ONLINE", void 0, opt);
|
|
4205
|
+
}
|
|
4206
|
+
kickUserWs($asai, target.us, hostdir);
|
|
4040
4207
|
await store.removeSession(sessionId);
|
|
4041
4208
|
const operator = String(req?.Userinfo?.[0] || "");
|
|
4042
4209
|
await logSessionTerminate($asai, {
|
|
4043
4210
|
operator,
|
|
4044
4211
|
sessionId,
|
|
4045
|
-
targetUs: target
|
|
4212
|
+
targetUs: target.us,
|
|
4046
4213
|
ip
|
|
4047
4214
|
});
|
|
4048
4215
|
return sendSuccess(res, { ok: true }, opt);
|
|
@@ -4074,10 +4241,16 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4074
4241
|
const levels = Array.isArray(body.levels) ? body.levels : void 0;
|
|
4075
4242
|
const thresholds = body.thresholds && typeof body.thresholds === "object" ? body.thresholds : void 0;
|
|
4076
4243
|
const routeRights = Array.isArray(body.routeRights) ? body.routeRights : void 0;
|
|
4244
|
+
const defaultResetPassword = typeof body.defaultResetPassword === "string" ? body.defaultResetPassword : void 0;
|
|
4077
4245
|
const operator = String(req?.Userinfo?.[0] || "");
|
|
4078
4246
|
const cfg = await saveUserLevelConfig(
|
|
4079
4247
|
$asai,
|
|
4080
|
-
{
|
|
4248
|
+
{
|
|
4249
|
+
levels,
|
|
4250
|
+
thresholds,
|
|
4251
|
+
routeRights,
|
|
4252
|
+
defaultResetPassword
|
|
4253
|
+
},
|
|
4081
4254
|
operator
|
|
4082
4255
|
);
|
|
4083
4256
|
await logSecurityConfigIntervention($asai, {
|
|
@@ -4091,6 +4264,57 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4091
4264
|
return sendFail(res, err?.message || err, opt);
|
|
4092
4265
|
}
|
|
4093
4266
|
}
|
|
4267
|
+
if (url.startsWith("/api/user/login/admin/resetpass/")) {
|
|
4268
|
+
await ensureReady($asai);
|
|
4269
|
+
if (!await checkAdmin(req, url, getPermissionThreshold($asai, "adminWrite"))) {
|
|
4270
|
+
logSecurityEvent($asai, { type: "ACCESS_DENIED", path: url, method: "POST", ip });
|
|
4271
|
+
return sendErr(res, "AUTH_FORBIDDEN", void 0, opt);
|
|
4272
|
+
}
|
|
4273
|
+
try {
|
|
4274
|
+
const targetUs = sanitizeString(body.us, 64);
|
|
4275
|
+
if (!targetUs) return sendErr(res, "AUTH_USER_NOT_FOUND", void 0, opt);
|
|
4276
|
+
let newPass = sanitizeString(pickAuthField(body, "newPass", "newPassword", "newpass"), 128);
|
|
4277
|
+
if (!newPass) newPass = getDefaultResetPassword($asai);
|
|
4278
|
+
const policy = getPasswordPolicy($asai);
|
|
4279
|
+
const policyErr = validatePasswordWithPolicy(newPass, policy);
|
|
4280
|
+
if (policyErr === "PASSWORD_TOO_SHORT") {
|
|
4281
|
+
return sendErr(res, policyErr, [String(policy.minLength)], opt);
|
|
4282
|
+
}
|
|
4283
|
+
if (policyErr) return sendErr(res, policyErr, void 0, opt);
|
|
4284
|
+
const user = await store.findByUsername(targetUs);
|
|
4285
|
+
if (!user) return sendErr(res, "AUTH_USER_NOT_FOUND", void 0, opt);
|
|
4286
|
+
const { passHash, passSalt } = hashPassword(newPass);
|
|
4287
|
+
const updated = await store.updateUser(targetUs, {
|
|
4288
|
+
passHash,
|
|
4289
|
+
passSalt,
|
|
4290
|
+
mustChangePassword: 1,
|
|
4291
|
+
passwordChangedAt: Date.now(),
|
|
4292
|
+
failCount: 0,
|
|
4293
|
+
status: "active",
|
|
4294
|
+
lockedUntil: void 0,
|
|
4295
|
+
passHistory: "[]"
|
|
4296
|
+
});
|
|
4297
|
+
if (!updated) return sendErr(res, "AUTH_USER_NOT_FOUND", void 0, opt);
|
|
4298
|
+
const operator = String(req?.Userinfo?.[0] || "");
|
|
4299
|
+
await logUserRbacChange($asai, {
|
|
4300
|
+
operator,
|
|
4301
|
+
targetUs,
|
|
4302
|
+
patch: { resetPassword: true, mustChangePassword: 1 },
|
|
4303
|
+
ip
|
|
4304
|
+
});
|
|
4305
|
+
logSecurityEvent($asai, {
|
|
4306
|
+
type: "AUTH_FAIL",
|
|
4307
|
+
path: url,
|
|
4308
|
+
method: "POST",
|
|
4309
|
+
user: targetUs,
|
|
4310
|
+
ip,
|
|
4311
|
+
detail: "admin_password_reset"
|
|
4312
|
+
});
|
|
4313
|
+
return sendSuccess(res, { user: publicUser($asai, updated) }, opt);
|
|
4314
|
+
} catch (err) {
|
|
4315
|
+
return sendFail(res, err?.message || err, opt);
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4094
4318
|
if (url.startsWith("/api/user/login/admin/update/")) {
|
|
4095
4319
|
await ensureReady($asai);
|
|
4096
4320
|
if (!await checkAdmin(req, url)) {
|
|
@@ -4136,11 +4360,12 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4136
4360
|
try {
|
|
4137
4361
|
const user = await store.findByUsername(String(us));
|
|
4138
4362
|
if (!user) return sendErr(res, "AUTH_USER_NOT_FOUND", void 0, opt);
|
|
4139
|
-
if (body
|
|
4140
|
-
const newPass = sanitizeString(body
|
|
4363
|
+
if (pickAuthField(body, "newPass", "newPassword", "newpass")) {
|
|
4364
|
+
const newPass = sanitizeString(pickAuthField(body, "newPass", "newPassword", "newpass"), 128);
|
|
4141
4365
|
const policy = getPasswordPolicy($asai);
|
|
4142
|
-
|
|
4143
|
-
|
|
4366
|
+
const oldPass = pickAuthField(body, "pass", "password", "pwd");
|
|
4367
|
+
if (oldPass) {
|
|
4368
|
+
if (!verifyPassword(oldPass, user.passHash, user.passSalt)) {
|
|
4144
4369
|
return sendErr(res, "AUTH_INVALID_PASSWORD", void 0, opt);
|
|
4145
4370
|
}
|
|
4146
4371
|
} else if (!user.mustChangePassword) {
|
|
@@ -4152,7 +4377,7 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4152
4377
|
}
|
|
4153
4378
|
if (policyErr) return sendErr(res, policyErr, void 0, opt);
|
|
4154
4379
|
const { passHash, passSalt } = hashPassword(newPass);
|
|
4155
|
-
const passHistory = nextPassHistory(
|
|
4380
|
+
const passHistory = nextPassHistory(oldPass || newPass, user.passHistory, policy.historyCount);
|
|
4156
4381
|
await store.updateUser(String(us), {
|
|
4157
4382
|
passHash,
|
|
4158
4383
|
passSalt,
|
|
@@ -4174,6 +4399,7 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4174
4399
|
const ip = getClientIp(req);
|
|
4175
4400
|
if (url.startsWith("/api/user/login/config/")) {
|
|
4176
4401
|
const lang = String(req?.headers?.["accept-language"] || opt?.headers?.["accept-language"] || "zh-CN").split(",")[0];
|
|
4402
|
+
await loadUserLevelConfig($asai);
|
|
4177
4403
|
return sendSuccess(res, getPublicSecurityConfig($asai, lang), opt);
|
|
4178
4404
|
}
|
|
4179
4405
|
if (url.startsWith("/api/user/login/admin/levels/")) {
|
|
@@ -4187,7 +4413,8 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4187
4413
|
if (!await checkAdmin(req, url)) {
|
|
4188
4414
|
return sendErr(res, "AUTH_FORBIDDEN", void 0, opt);
|
|
4189
4415
|
}
|
|
4190
|
-
const
|
|
4416
|
+
const allSessions = await store.getSessions();
|
|
4417
|
+
const sessions = resolveOnlineSessions($asai, hostdir, allSessions);
|
|
4191
4418
|
return sendSuccess(
|
|
4192
4419
|
res,
|
|
4193
4420
|
{
|
|
@@ -4197,7 +4424,8 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4197
4424
|
ip: s.ip,
|
|
4198
4425
|
createdAt: s.createdAt,
|
|
4199
4426
|
expiresAt: s.expiresAt,
|
|
4200
|
-
lastActivityAt: s.lastActivityAt
|
|
4427
|
+
lastActivityAt: s.lastActivityAt,
|
|
4428
|
+
online: true
|
|
4201
4429
|
}))
|
|
4202
4430
|
},
|
|
4203
4431
|
opt
|
|
@@ -4215,12 +4443,13 @@ var login_default = /* @__PURE__ */ __name(($asai) => {
|
|
|
4215
4443
|
if (!await checkAdmin(req, url)) {
|
|
4216
4444
|
return sendErr(res, "AUTH_FORBIDDEN", void 0, opt);
|
|
4217
4445
|
}
|
|
4218
|
-
|
|
4446
|
+
await purgeOfflineSessions($asai, hostdir, store);
|
|
4447
|
+
const onlineCount = await countOnlineSessions($asai, hostdir, store);
|
|
4219
4448
|
const quota = getQuota2($asai);
|
|
4220
4449
|
return sendSuccess(
|
|
4221
4450
|
res,
|
|
4222
4451
|
{
|
|
4223
|
-
sessions:
|
|
4452
|
+
sessions: onlineCount,
|
|
4224
4453
|
maxLoginSessions: quota.maxLoginSessions ?? 100,
|
|
4225
4454
|
quotaEnabled: !!quota.enabled,
|
|
4226
4455
|
http: $asai.connectionshttp,
|
|
@@ -4295,10 +4524,10 @@ var As_default = {
|
|
|
4295
4524
|
return relativePath;
|
|
4296
4525
|
},
|
|
4297
4526
|
//创建多层文件夹 同步
|
|
4298
|
-
ensureDir(
|
|
4527
|
+
ensureDir(fs12, relativePath) {
|
|
4299
4528
|
const absPathDir = this.toDirPath(relativePath);
|
|
4300
4529
|
const absPath = this.toAbsolutePath(relativePath);
|
|
4301
|
-
if (!
|
|
4530
|
+
if (!fs12.existsSync(absPath)) {
|
|
4302
4531
|
let pathTmp = "";
|
|
4303
4532
|
absPathDir.split("/").forEach((el) => {
|
|
4304
4533
|
if (el) {
|
|
@@ -4306,8 +4535,8 @@ var As_default = {
|
|
|
4306
4535
|
pathTmp += "/";
|
|
4307
4536
|
}
|
|
4308
4537
|
pathTmp += el;
|
|
4309
|
-
if (!
|
|
4310
|
-
if (!
|
|
4538
|
+
if (!fs12.existsSync(pathTmp)) {
|
|
4539
|
+
if (!fs12.mkdirSync(pathTmp, { recursive: true })) {
|
|
4311
4540
|
return absPath;
|
|
4312
4541
|
}
|
|
4313
4542
|
}
|
|
@@ -4316,11 +4545,11 @@ var As_default = {
|
|
|
4316
4545
|
}
|
|
4317
4546
|
return absPath;
|
|
4318
4547
|
},
|
|
4319
|
-
makeDir(
|
|
4320
|
-
return new Promise((
|
|
4548
|
+
makeDir(fs12, filePaths) {
|
|
4549
|
+
return new Promise((resolve6, reject) => {
|
|
4321
4550
|
try {
|
|
4322
|
-
const newPath = this.ensureDir(
|
|
4323
|
-
|
|
4551
|
+
const newPath = this.ensureDir(fs12, filePaths);
|
|
4552
|
+
resolve6(newPath);
|
|
4324
4553
|
} catch (err) {
|
|
4325
4554
|
reject(err);
|
|
4326
4555
|
}
|
|
@@ -4543,7 +4772,10 @@ var AsAES_default = /* @__PURE__ */ __name(($asai, cfg = null) => {
|
|
|
4543
4772
|
return fn(reqData);
|
|
4544
4773
|
}
|
|
4545
4774
|
} catch (error) {
|
|
4546
|
-
|
|
4775
|
+
if (validateFields(reqData, ["data", "iv", "auth"])) {
|
|
4776
|
+
throw error;
|
|
4777
|
+
}
|
|
4778
|
+
return fn(reqData);
|
|
4547
4779
|
}
|
|
4548
4780
|
}
|
|
4549
4781
|
__name(reqAES, "reqAES");
|
|
@@ -4627,6 +4859,48 @@ var AsDb_default = /* @__PURE__ */ __name(($asai, cfg) => {
|
|
|
4627
4859
|
return limittmp;
|
|
4628
4860
|
}
|
|
4629
4861
|
__name(reqlimit, "reqlimit");
|
|
4862
|
+
function decodePathSegment(seg) {
|
|
4863
|
+
try {
|
|
4864
|
+
return decodeURIComponent(seg);
|
|
4865
|
+
} catch {
|
|
4866
|
+
return seg;
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
__name(decodePathSegment, "decodePathSegment");
|
|
4870
|
+
function bareFileName(name) {
|
|
4871
|
+
const base = String(name).replace(/\.json$/i, "");
|
|
4872
|
+
const slash = base.lastIndexOf("/");
|
|
4873
|
+
return slash > -1 ? base.substring(slash + 1) : base;
|
|
4874
|
+
}
|
|
4875
|
+
__name(bareFileName, "bareFileName");
|
|
4876
|
+
function parseFileBody(raw) {
|
|
4877
|
+
let body = raw;
|
|
4878
|
+
if (typeof body === "string") {
|
|
4879
|
+
try {
|
|
4880
|
+
body = JSON.parse(body);
|
|
4881
|
+
} catch {
|
|
4882
|
+
return { content: body };
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4885
|
+
if (body && typeof body === "object" && !Array.isArray(body)) {
|
|
4886
|
+
return {
|
|
4887
|
+
name: body.name != null ? String(body.name) : void 0,
|
|
4888
|
+
content: body.content !== void 0 ? body.content : body
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
4891
|
+
return { content: body };
|
|
4892
|
+
}
|
|
4893
|
+
__name(parseFileBody, "parseFileBody");
|
|
4894
|
+
function fileContentString(content) {
|
|
4895
|
+
if (content == null) return "";
|
|
4896
|
+
if (typeof content === "string") return content;
|
|
4897
|
+
return JSON.stringify(content);
|
|
4898
|
+
}
|
|
4899
|
+
__name(fileContentString, "fileContentString");
|
|
4900
|
+
function fileKeyFromSegment(seg, bodyName) {
|
|
4901
|
+
return bareFileName(decodePathSegment(seg || bodyName || ""));
|
|
4902
|
+
}
|
|
4903
|
+
__name(fileKeyFromSegment, "fileKeyFromSegment");
|
|
4630
4904
|
function getSqlParamsByUrl(req, opt) {
|
|
4631
4905
|
let tmpSqlParams;
|
|
4632
4906
|
const reqArr = req?.url?.split("?")[0]?.split("/") || [];
|
|
@@ -4640,8 +4914,10 @@ var AsDb_default = /* @__PURE__ */ __name(($asai, cfg) => {
|
|
|
4640
4914
|
if (reqArr[4] === "insert") {
|
|
4641
4915
|
tmpSqlParams.type = reqArr[4];
|
|
4642
4916
|
if (cfg?.type === "file") {
|
|
4917
|
+
const { name: bodyName, content } = parseFileBody(opt.data);
|
|
4918
|
+
const fileKey = fileKeyFromSegment(reqArr[5], bodyName);
|
|
4643
4919
|
tmpSqlParams.field = cfg.field.data;
|
|
4644
|
-
tmpSqlParams.value = [[
|
|
4920
|
+
tmpSqlParams.value = [[fileKey, fileContentString(content)]];
|
|
4645
4921
|
} else {
|
|
4646
4922
|
tmpSqlParams.field = [];
|
|
4647
4923
|
tmpSqlParams.value = [];
|
|
@@ -4661,9 +4937,12 @@ var AsDb_default = /* @__PURE__ */ __name(($asai, cfg) => {
|
|
|
4661
4937
|
}
|
|
4662
4938
|
} else if (reqArr[4] === "update") {
|
|
4663
4939
|
tmpSqlParams.type = reqArr[4];
|
|
4664
|
-
|
|
4940
|
+
const rowKey = cfg?.type === "file" ? fileKeyFromSegment(reqArr[5]) : decodePathSegment(reqArr[5]);
|
|
4941
|
+
tmpSqlParams.where = [[cfg.field.key, "=", reqwherestr(rowKey)]];
|
|
4665
4942
|
if (cfg?.type === "file") {
|
|
4666
|
-
|
|
4943
|
+
const { content } = parseFileBody(opt.data);
|
|
4944
|
+
const contentField = cfg.field.data?.[1] || "content";
|
|
4945
|
+
tmpSqlParams.set = [[contentField, fileContentString(content)]];
|
|
4667
4946
|
} else {
|
|
4668
4947
|
tmpSqlParams.set = [];
|
|
4669
4948
|
const dbData = typeof opt.data === "string" ? JSON.parse(opt.data) : opt.data;
|
|
@@ -4679,13 +4958,15 @@ var AsDb_default = /* @__PURE__ */ __name(($asai, cfg) => {
|
|
|
4679
4958
|
}
|
|
4680
4959
|
} else if (reqArr[4] === "delete") {
|
|
4681
4960
|
tmpSqlParams.type = reqArr[4];
|
|
4682
|
-
|
|
4961
|
+
const rowKey = cfg?.type === "file" ? fileKeyFromSegment(reqArr[5]) : decodePathSegment(reqArr[5]);
|
|
4962
|
+
tmpSqlParams.where = [[cfg.field.key, "=", reqwherestr(rowKey)]];
|
|
4683
4963
|
} else {
|
|
4684
4964
|
if (cfg?.as > 1) {
|
|
4685
4965
|
tmpSqlParams.as = +cfg.as - 1;
|
|
4686
4966
|
}
|
|
4687
4967
|
tmpSqlParams.field = cfg.field.data;
|
|
4688
|
-
|
|
4968
|
+
const rowKey = cfg?.type === "file" ? fileKeyFromSegment(reqArr[5]) : decodePathSegment(reqArr[5]);
|
|
4969
|
+
tmpSqlParams.where = [[cfg.field.key, "=", reqwherestr(rowKey)]];
|
|
4689
4970
|
}
|
|
4690
4971
|
} else {
|
|
4691
4972
|
tmpSqlParams.as = cfg?.as;
|
|
@@ -4701,19 +4982,50 @@ var AsDb_default = /* @__PURE__ */ __name(($asai, cfg) => {
|
|
|
4701
4982
|
if (req?.url?.includes("/selectlist/")) {
|
|
4702
4983
|
const list = Array.isArray(resdb) ? resdb : resdb?.data;
|
|
4703
4984
|
if (Array.isArray(list)) {
|
|
4704
|
-
const
|
|
4985
|
+
const names = list.map((el) => {
|
|
4986
|
+
if (typeof el === "string") {
|
|
4987
|
+
const dot2 = el.lastIndexOf(".");
|
|
4988
|
+
return dot2 > -1 ? el.substring(0, dot2) : el;
|
|
4989
|
+
}
|
|
4705
4990
|
const rawName = el.name ?? el.file ?? "";
|
|
4706
4991
|
let tmpName = String(rawName).substring(String(rawName).lastIndexOf("/") + 1);
|
|
4707
4992
|
const dot = tmpName.lastIndexOf(".");
|
|
4708
4993
|
return dot > -1 ? tmpName.substring(0, dot) : tmpName;
|
|
4709
4994
|
});
|
|
4710
|
-
|
|
4711
|
-
resdb
|
|
4995
|
+
const children = names.map((name) => ({ name, path: name }));
|
|
4996
|
+
if (Array.isArray(resdb)) return { children };
|
|
4997
|
+
resdb.data = children;
|
|
4998
|
+
return resdb;
|
|
4712
4999
|
}
|
|
4713
5000
|
}
|
|
4714
5001
|
return resdb;
|
|
4715
5002
|
}
|
|
4716
5003
|
__name(dbFresh, "dbFresh");
|
|
5004
|
+
function normalizeSelectResult(params, resdb) {
|
|
5005
|
+
let data = resdb;
|
|
5006
|
+
if (params?.where?.length && Array.isArray(data) && data.length === 1) {
|
|
5007
|
+
data = data[0];
|
|
5008
|
+
}
|
|
5009
|
+
if (cfg?.type === "file" && data && typeof data === "object" && !Array.isArray(data)) {
|
|
5010
|
+
const nameKey = cfg.field?.data?.[0] || "name";
|
|
5011
|
+
const contentKey = cfg.field?.data?.[1] || "content";
|
|
5012
|
+
if (data[nameKey] != null) {
|
|
5013
|
+
data[nameKey] = bareFileName(String(data[nameKey]));
|
|
5014
|
+
}
|
|
5015
|
+
const raw = data[contentKey];
|
|
5016
|
+
if (typeof raw === "string") {
|
|
5017
|
+
const text = raw.trim();
|
|
5018
|
+
if (text.startsWith("{") || text.startsWith("[")) {
|
|
5019
|
+
try {
|
|
5020
|
+
data[contentKey] = JSON.parse(text);
|
|
5021
|
+
} catch {
|
|
5022
|
+
}
|
|
5023
|
+
}
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
return data;
|
|
5027
|
+
}
|
|
5028
|
+
__name(normalizeSelectResult, "normalizeSelectResult");
|
|
4717
5029
|
function dbCodePm(req, resdb) {
|
|
4718
5030
|
if (req?.Userinfo && req?.Userinfo[2]) {
|
|
4719
5031
|
resdb.data = $asai.$lib.AsCode.asencode(JSON.stringify(resdb.data), req.Userinfo[2]);
|
|
@@ -4744,7 +5056,7 @@ var AsDb_default = /* @__PURE__ */ __name(($asai, cfg) => {
|
|
|
4744
5056
|
if (params && typeof DataServer.sqlDb === "function") {
|
|
4745
5057
|
try {
|
|
4746
5058
|
DataServer.sqlDb(params).then((resdb) => {
|
|
4747
|
-
let resData = dbFresh(req, resdb);
|
|
5059
|
+
let resData = normalizeSelectResult(params, dbFresh(req, resdb));
|
|
4748
5060
|
if (params?.as === 2 && params?.field?.length > 1) {
|
|
4749
5061
|
let limitArr = [];
|
|
4750
5062
|
if (params.limit?.split(",")?.length < 2) {
|
|
@@ -5055,19 +5367,31 @@ var lib_default = {
|
|
|
5055
5367
|
};
|
|
5056
5368
|
|
|
5057
5369
|
// src/sysserver/db/src/common.ts
|
|
5370
|
+
var common_exports = {};
|
|
5371
|
+
__export(common_exports, {
|
|
5372
|
+
fieldHasId: () => fieldHasId,
|
|
5373
|
+
isPathInside: () => isPathInside,
|
|
5374
|
+
isTableMissingError: () => isTableMissingError,
|
|
5375
|
+
joinTablePath: () => joinTablePath,
|
|
5376
|
+
promisifySqlDb: () => promisifySqlDb,
|
|
5377
|
+
toFieldArray: () => toFieldArray,
|
|
5378
|
+
toInsertRows: () => toInsertRows,
|
|
5379
|
+
toSetObject: () => toSetObject,
|
|
5380
|
+
toWherePairs: () => toWherePairs
|
|
5381
|
+
});
|
|
5058
5382
|
import * as path5 from "path";
|
|
5059
5383
|
function promisifySqlDb(query, sql) {
|
|
5060
|
-
return new Promise((
|
|
5384
|
+
return new Promise((resolve6, reject) => {
|
|
5061
5385
|
query(sql, (err, result) => {
|
|
5062
5386
|
if (err) {
|
|
5063
5387
|
reject(err);
|
|
5064
5388
|
return;
|
|
5065
5389
|
}
|
|
5066
5390
|
if (!result || Array.isArray(result) && result.length === 0) {
|
|
5067
|
-
|
|
5391
|
+
resolve6("");
|
|
5068
5392
|
return;
|
|
5069
5393
|
}
|
|
5070
|
-
|
|
5394
|
+
resolve6(result);
|
|
5071
5395
|
});
|
|
5072
5396
|
});
|
|
5073
5397
|
}
|
|
@@ -5077,6 +5401,48 @@ function toFieldArray(field) {
|
|
|
5077
5401
|
return typeof field === "string" ? field.split(",") : field;
|
|
5078
5402
|
}
|
|
5079
5403
|
__name(toFieldArray, "toFieldArray");
|
|
5404
|
+
function toWherePairs(where) {
|
|
5405
|
+
if (!where || !Array.isArray(where)) return [];
|
|
5406
|
+
const pairs = [];
|
|
5407
|
+
for (const item of where) {
|
|
5408
|
+
if (typeof item === "string") continue;
|
|
5409
|
+
if (Array.isArray(item) && item.length >= 3) {
|
|
5410
|
+
pairs.push([item[0], item[2]]);
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
return pairs;
|
|
5414
|
+
}
|
|
5415
|
+
__name(toWherePairs, "toWherePairs");
|
|
5416
|
+
function toSetObject(set) {
|
|
5417
|
+
if (!set) return {};
|
|
5418
|
+
if (typeof set === "string") return {};
|
|
5419
|
+
const obj = {};
|
|
5420
|
+
for (const item of set) {
|
|
5421
|
+
if (typeof item === "string") continue;
|
|
5422
|
+
if (Array.isArray(item)) {
|
|
5423
|
+
if (item.length >= 3 && typeof item[1] === "string") {
|
|
5424
|
+
obj[item[0]] = item[2];
|
|
5425
|
+
} else if (item.length >= 2) {
|
|
5426
|
+
obj[item[0]] = item[1];
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
return obj;
|
|
5431
|
+
}
|
|
5432
|
+
__name(toSetObject, "toSetObject");
|
|
5433
|
+
function toInsertRows(sql) {
|
|
5434
|
+
const fields = toFieldArray(sql.field);
|
|
5435
|
+
const values = sql.value;
|
|
5436
|
+
if (!fields.length || !Array.isArray(values)) return [];
|
|
5437
|
+
return values.map((row) => {
|
|
5438
|
+
const rowObj = {};
|
|
5439
|
+
fields.forEach((field, index) => {
|
|
5440
|
+
rowObj[field] = row[index];
|
|
5441
|
+
});
|
|
5442
|
+
return rowObj;
|
|
5443
|
+
});
|
|
5444
|
+
}
|
|
5445
|
+
__name(toInsertRows, "toInsertRows");
|
|
5080
5446
|
function isPathInside(baseDir, targetPath) {
|
|
5081
5447
|
const base = path5.resolve(baseDir);
|
|
5082
5448
|
const target = path5.resolve(targetPath);
|
|
@@ -5262,936 +5628,426 @@ var Sql_default = {
|
|
|
5262
5628
|
}
|
|
5263
5629
|
};
|
|
5264
5630
|
|
|
5265
|
-
// src/sysserver/db/src/
|
|
5266
|
-
import mysql from "mysql";
|
|
5267
|
-
var DbMySQL = class {
|
|
5268
|
-
static {
|
|
5269
|
-
__name(this, "DbMySQL");
|
|
5270
|
-
}
|
|
5271
|
-
constructor(config) {
|
|
5272
|
-
this.config = config;
|
|
5273
|
-
this.pool = mysql.createPool(config);
|
|
5274
|
-
}
|
|
5275
|
-
getErr(sql, err) {
|
|
5276
|
-
if (this.config.create && isTableMissingError(err)) {
|
|
5277
|
-
this.createTable(sql);
|
|
5278
|
-
}
|
|
5279
|
-
return err?.message || null;
|
|
5280
|
-
}
|
|
5281
|
-
getResult(rows) {
|
|
5282
|
-
return rows ?? null;
|
|
5283
|
-
}
|
|
5284
|
-
query(sql, callback) {
|
|
5285
|
-
this.pool.getConnection((error, connection) => {
|
|
5286
|
-
if (error) {
|
|
5287
|
-
callback(this.getErr(sql, error), this.getResult(null));
|
|
5288
|
-
return;
|
|
5289
|
-
}
|
|
5290
|
-
connection.query(Sql_default.makeSql(sql), (err, rows) => {
|
|
5291
|
-
try {
|
|
5292
|
-
callback(this.getErr(sql, err), this.getResult(rows));
|
|
5293
|
-
} finally {
|
|
5294
|
-
connection.release();
|
|
5295
|
-
}
|
|
5296
|
-
});
|
|
5297
|
-
});
|
|
5298
|
-
}
|
|
5299
|
-
sqlDb(sql) {
|
|
5300
|
-
return promisifySqlDb(this.query.bind(this), sql);
|
|
5301
|
-
}
|
|
5302
|
-
/** 关闭连接池,释放所有连接 */
|
|
5303
|
-
close(callback) {
|
|
5304
|
-
this.pool.end(callback);
|
|
5305
|
-
}
|
|
5306
|
-
/** 表不存在时自动创建数据库与表(需 config.create 为 true) */
|
|
5307
|
-
createTable(sql) {
|
|
5308
|
-
if (!this.config.create) return;
|
|
5309
|
-
const conn = mysql.createConnection({
|
|
5310
|
-
host: this.config.host,
|
|
5311
|
-
user: this.config.user,
|
|
5312
|
-
password: this.config.password
|
|
5313
|
-
});
|
|
5314
|
-
conn.connect((err) => {
|
|
5315
|
-
if (err) {
|
|
5316
|
-
conn.end();
|
|
5317
|
-
return;
|
|
5318
|
-
}
|
|
5319
|
-
conn.query(`CREATE DATABASE IF NOT EXISTS ${this.config.database};`, (dbErr) => {
|
|
5320
|
-
if (dbErr) {
|
|
5321
|
-
conn.end();
|
|
5322
|
-
return;
|
|
5323
|
-
}
|
|
5324
|
-
this.pool = mysql.createPool(this.config);
|
|
5325
|
-
this.pool.getConnection((poolErr, connection) => {
|
|
5326
|
-
if (poolErr || !connection) {
|
|
5327
|
-
conn.end();
|
|
5328
|
-
return;
|
|
5329
|
-
}
|
|
5330
|
-
connection.query(Sql_default.createSql(sql, "mysql"), () => {
|
|
5331
|
-
connection.release();
|
|
5332
|
-
conn.end();
|
|
5333
|
-
});
|
|
5334
|
-
});
|
|
5335
|
-
});
|
|
5336
|
-
});
|
|
5337
|
-
}
|
|
5338
|
-
};
|
|
5339
|
-
var Index_default2 = DbMySQL;
|
|
5340
|
-
|
|
5341
|
-
// src/sysserver/db/src/sqlite/Index.ts
|
|
5631
|
+
// src/sysserver/db/src/file/Index.ts
|
|
5342
5632
|
import * as fs11 from "fs";
|
|
5343
5633
|
import * as path6 from "path";
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
}
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
const absolutePath = path6.resolve(config.database);
|
|
5383
|
-
const dir = path6.dirname(absolutePath);
|
|
5384
|
-
if (!fs11.existsSync(dir)) {
|
|
5385
|
-
fs11.mkdirSync(dir, { recursive: true });
|
|
5386
|
-
dbLog("MKDIR", dir);
|
|
5387
|
-
}
|
|
5388
|
-
return new Promise((resolve7, reject) => {
|
|
5389
|
-
const db = new sqlite3.Database(absolutePath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, async (err) => {
|
|
5390
|
-
if (err) {
|
|
5391
|
-
dbLog("open failed", "[ERROR] Failed to open database:", absolutePath, err.message);
|
|
5392
|
-
reject(err);
|
|
5393
|
-
return;
|
|
5394
|
-
}
|
|
5395
|
-
try {
|
|
5396
|
-
await configureSqliteConnection(db, config.busyTimeout ?? DEFAULT_BUSY_TIMEOUT_MS);
|
|
5397
|
-
dbLog("OPEN", absolutePath);
|
|
5398
|
-
resolve7(new _PooledConnection(db, config));
|
|
5399
|
-
} catch (pragmaErr) {
|
|
5400
|
-
db.close(() => {
|
|
5401
|
-
});
|
|
5402
|
-
reject(pragmaErr);
|
|
5403
|
-
}
|
|
5404
|
-
});
|
|
5405
|
-
});
|
|
5406
|
-
}
|
|
5407
|
-
/** 测试连接是否有效(执行 SELECT 1) */
|
|
5408
|
-
async test() {
|
|
5409
|
-
return new Promise((resolve7) => {
|
|
5410
|
-
if (!this.isValid) return resolve7(false);
|
|
5411
|
-
this.db.get("SELECT 1", (err) => {
|
|
5412
|
-
this.isValid = !err;
|
|
5413
|
-
resolve7(this.isValid);
|
|
5414
|
-
});
|
|
5415
|
-
});
|
|
5416
|
-
}
|
|
5417
|
-
/** 关闭连接 */
|
|
5418
|
-
async close() {
|
|
5419
|
-
return new Promise((resolve7, reject) => {
|
|
5420
|
-
this.db.close((err) => {
|
|
5421
|
-
if (err) reject(err);
|
|
5422
|
-
else resolve7();
|
|
5423
|
-
});
|
|
5424
|
-
});
|
|
5425
|
-
}
|
|
5426
|
-
/** 标记连接为无效 */
|
|
5427
|
-
invalidate() {
|
|
5428
|
-
this.isValid = false;
|
|
5429
|
-
}
|
|
5430
|
-
};
|
|
5431
|
-
var SQLitePool = class {
|
|
5432
|
-
// 初始化完成 Promise
|
|
5433
|
-
constructor(config) {
|
|
5434
|
-
this.pool = [];
|
|
5435
|
-
this.waitingQueue = [];
|
|
5436
|
-
this.idleCheckerTimer = null;
|
|
5437
|
-
this.isDestroyed = false;
|
|
5438
|
-
this.config = {
|
|
5439
|
-
database: config.database,
|
|
5440
|
-
poolMin: config.poolMin ?? 1,
|
|
5441
|
-
poolMax: config.poolMax ?? 1,
|
|
5442
|
-
idleTimeout: config.idleTimeout ?? 3e4,
|
|
5443
|
-
acquireTimeout: config.acquireTimeout ?? DEFAULT_ACQUIRE_TIMEOUT_MS,
|
|
5444
|
-
busyTimeout: config.busyTimeout ?? DEFAULT_BUSY_TIMEOUT_MS,
|
|
5445
|
-
enableCreateTable: config.enableCreateTable ?? true,
|
|
5446
|
-
$asai: config.$asai ?? null
|
|
5447
|
-
};
|
|
5448
|
-
this.initPromise = this.initializePool();
|
|
5449
|
-
this.startIdleChecker();
|
|
5450
|
-
}
|
|
5451
|
-
static {
|
|
5452
|
-
__name(this, "SQLitePool");
|
|
5453
|
-
}
|
|
5454
|
-
/** 统一日志输出(前缀 ♣ 标识来自 SQLite 连接池模块) */
|
|
5455
|
-
dbLog(...args) {
|
|
5456
|
-
(this.config?.$asai?.$log || console.log)("sqlite", ...args);
|
|
5457
|
-
}
|
|
5458
|
-
/** 初始化连接池,创建最小连接数 */
|
|
5459
|
-
async initializePool() {
|
|
5460
|
-
const promises = [];
|
|
5461
|
-
for (let i = 0; i < this.config.poolMin; i++) {
|
|
5462
|
-
promises.push(this.createConnection());
|
|
5463
|
-
}
|
|
5464
|
-
const results = await Promise.allSettled(promises);
|
|
5465
|
-
const successCount = results.filter((r) => r.status === "fulfilled").length;
|
|
5466
|
-
if (successCount === 0) {
|
|
5467
|
-
this.dbLog("Initialization failed", "[POOL] Initialization failed: no connections created");
|
|
5468
|
-
} else {
|
|
5469
|
-
this.dbLog("Initialized", "[POOL] Initialized, size:", this.pool.length);
|
|
5470
|
-
}
|
|
5471
|
-
}
|
|
5472
|
-
/** 创建一个新连接并加入池中 */
|
|
5473
|
-
async createConnection() {
|
|
5474
|
-
if (this.pool.length >= this.config.poolMax) return;
|
|
5475
|
-
try {
|
|
5476
|
-
const conn = await PooledConnection.create(this.config);
|
|
5477
|
-
const isValid = await conn.test();
|
|
5478
|
-
if (isValid) {
|
|
5479
|
-
this.pool.push(conn);
|
|
5480
|
-
} else {
|
|
5481
|
-
await conn.close().catch(() => {
|
|
5482
|
-
});
|
|
5483
|
-
throw new Error("Connection test failed");
|
|
5634
|
+
function dbDriver(opt = {}) {
|
|
5635
|
+
const { Sql, DbCommon } = opt;
|
|
5636
|
+
const { isPathInside: isPathInside2, joinTablePath: joinTablePath2 } = DbCommon;
|
|
5637
|
+
class DbFile {
|
|
5638
|
+
static {
|
|
5639
|
+
__name(this, "DbFile");
|
|
5640
|
+
}
|
|
5641
|
+
constructor(config) {
|
|
5642
|
+
this.config = config;
|
|
5643
|
+
this.fileExt = config.ext === ".*" ? "" : config.ext || "";
|
|
5644
|
+
this.fileDel = (config.del || 0) !== 0;
|
|
5645
|
+
this.createDir = config.create || false;
|
|
5646
|
+
this.baseDirAbs = path6.resolve(process.cwd(), config.dir);
|
|
5647
|
+
}
|
|
5648
|
+
dbLog(...args) {
|
|
5649
|
+
(this.config?.$asai?.$log || console.log)("FILE", ...args);
|
|
5650
|
+
}
|
|
5651
|
+
checkFile(file) {
|
|
5652
|
+
return fs11.existsSync(this.getAbsPath(file, false));
|
|
5653
|
+
}
|
|
5654
|
+
/** 解析相对路径并限制在数据根目录内,防止路径穿越(跨平台) */
|
|
5655
|
+
resolvePath(relPath) {
|
|
5656
|
+
if (!relPath) {
|
|
5657
|
+
return this.baseDirAbs;
|
|
5658
|
+
}
|
|
5659
|
+
const abs = path6.isAbsolute(relPath) ? path6.normalize(relPath) : path6.resolve(this.baseDirAbs, relPath);
|
|
5660
|
+
if (!isPathInside2(this.baseDirAbs, abs)) {
|
|
5661
|
+
return this.baseDirAbs;
|
|
5662
|
+
}
|
|
5663
|
+
return abs;
|
|
5664
|
+
}
|
|
5665
|
+
/** 将 table 与文件名片段拼为平台无关相对路径 */
|
|
5666
|
+
tableFilePath(table, ...segments) {
|
|
5667
|
+
return joinTablePath2(table, ...segments);
|
|
5668
|
+
}
|
|
5669
|
+
normalizeRelPath(relPath) {
|
|
5670
|
+
if (!relPath) {
|
|
5671
|
+
return "";
|
|
5484
5672
|
}
|
|
5485
|
-
|
|
5486
|
-
this.dbLog("Database create failed", "[CONN] Failed to create connection:", err.message);
|
|
5487
|
-
throw err;
|
|
5673
|
+
return path6.isAbsolute(relPath) ? path6.relative(this.baseDirAbs, relPath) : relPath;
|
|
5488
5674
|
}
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
if (this.isDestroyed) {
|
|
5499
|
-
throw new Error("Pool has been destroyed");
|
|
5675
|
+
ensureDir(relPath) {
|
|
5676
|
+
const relative4 = this.normalizeRelPath(relPath);
|
|
5677
|
+
const dirPath = path6.dirname(relative4);
|
|
5678
|
+
const absDir = this.resolvePath(dirPath === "." ? "" : dirPath);
|
|
5679
|
+
if (this.createDir && !fs11.existsSync(absDir)) {
|
|
5680
|
+
this.dbLog("mkdir", "[MKDIR]", absDir);
|
|
5681
|
+
fs11.mkdirSync(absDir, { recursive: true });
|
|
5682
|
+
}
|
|
5683
|
+
return absDir;
|
|
5500
5684
|
}
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
this.removeConnection(conn);
|
|
5509
|
-
continue;
|
|
5510
|
-
}
|
|
5511
|
-
}
|
|
5512
|
-
conn.inUse = true;
|
|
5513
|
-
conn.lastUsed = now;
|
|
5514
|
-
return conn;
|
|
5685
|
+
getAbsPath(file, autoCreateDir = true) {
|
|
5686
|
+
let rel = file;
|
|
5687
|
+
if (this.fileExt && !rel.endsWith(this.fileExt)) {
|
|
5688
|
+
rel += this.fileExt;
|
|
5689
|
+
}
|
|
5690
|
+
if (autoCreateDir) {
|
|
5691
|
+
this.ensureDir(rel);
|
|
5515
5692
|
}
|
|
5693
|
+
return this.resolvePath(rel);
|
|
5516
5694
|
}
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5695
|
+
deldir(relativePath) {
|
|
5696
|
+
const absPath = this.resolvePath(relativePath);
|
|
5697
|
+
if (!fs11.existsSync(absPath)) {
|
|
5698
|
+
return;
|
|
5699
|
+
}
|
|
5700
|
+
this.dbLog("delete", "[RMDIR]", absPath);
|
|
5701
|
+
const entries = fs11.readdirSync(absPath, { withFileTypes: true });
|
|
5702
|
+
for (const entry of entries) {
|
|
5703
|
+
const current = path6.join(absPath, entry.name);
|
|
5704
|
+
if (entry.isDirectory()) {
|
|
5705
|
+
this.deldir(path6.join(relativePath, entry.name));
|
|
5526
5706
|
} else {
|
|
5527
|
-
|
|
5528
|
-
|
|
5707
|
+
this.dbLog("delete", "[UNLINK]", current);
|
|
5708
|
+
fs11.unlinkSync(current);
|
|
5529
5709
|
}
|
|
5530
|
-
} catch (e) {
|
|
5531
|
-
this.dbLog("create failed", "[ACQUIRE] Failed to create new connection, waiting:", e.message);
|
|
5532
5710
|
}
|
|
5711
|
+
fs11.rmdirSync(absPath);
|
|
5533
5712
|
}
|
|
5534
|
-
|
|
5535
|
-
const
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
if (!conn) return;
|
|
5548
|
-
conn.inUse = false;
|
|
5549
|
-
conn.lastUsed = Date.now();
|
|
5550
|
-
if (this.waitingQueue.length > 0) {
|
|
5551
|
-
const next = this.waitingQueue.shift();
|
|
5552
|
-
clearTimeout(next.timer);
|
|
5553
|
-
conn.inUse = true;
|
|
5554
|
-
conn.lastUsed = Date.now();
|
|
5555
|
-
next.resolve(conn);
|
|
5556
|
-
}
|
|
5557
|
-
}
|
|
5558
|
-
/** 从池中移除并关闭指定连接 */
|
|
5559
|
-
removeConnection(conn) {
|
|
5560
|
-
const index = this.pool.indexOf(conn);
|
|
5561
|
-
if (index !== -1) {
|
|
5562
|
-
this.pool.splice(index, 1);
|
|
5563
|
-
conn.close().catch(() => {
|
|
5564
|
-
});
|
|
5565
|
-
}
|
|
5566
|
-
}
|
|
5567
|
-
/** 启动空闲连接回收定时器(每 60 秒检查一次) */
|
|
5568
|
-
startIdleChecker() {
|
|
5569
|
-
this.idleCheckerTimer = setInterval(async () => {
|
|
5570
|
-
if (this.isDestroyed) return;
|
|
5571
|
-
const now = Date.now();
|
|
5572
|
-
const toRemove = [];
|
|
5573
|
-
for (const conn of this.pool) {
|
|
5574
|
-
if (!conn.inUse && now - conn.lastUsed > this.config.idleTimeout && this.pool.length > this.config.poolMin) {
|
|
5575
|
-
toRemove.push(conn);
|
|
5713
|
+
getReadPath(file, sql) {
|
|
5714
|
+
const candidates = [];
|
|
5715
|
+
if (sql?.filename) {
|
|
5716
|
+
candidates.push({ dir: path6.join(file, sql.filename) });
|
|
5717
|
+
candidates.push({ dir: path6.join(file + ".delete" + this.fileExt, sql.filename) });
|
|
5718
|
+
} else {
|
|
5719
|
+
candidates.push({ dir: file });
|
|
5720
|
+
candidates.push({ dir: file + this.fileExt + ".delete" });
|
|
5721
|
+
}
|
|
5722
|
+
for (const candidate of candidates) {
|
|
5723
|
+
const abs = this.getAbsPath(candidate.dir, false);
|
|
5724
|
+
if (fs11.existsSync(abs)) {
|
|
5725
|
+
return { path: abs, dir: candidate.dir };
|
|
5576
5726
|
}
|
|
5577
5727
|
}
|
|
5578
|
-
|
|
5579
|
-
|
|
5728
|
+
return { path: "", dir: "" };
|
|
5729
|
+
}
|
|
5730
|
+
getDelPath(file, sql) {
|
|
5731
|
+
const read2 = this.getReadPath(file, sql);
|
|
5732
|
+
if (!read2.path) {
|
|
5733
|
+
return { path: "", dir: read2.dir };
|
|
5580
5734
|
}
|
|
5581
|
-
if (
|
|
5582
|
-
|
|
5735
|
+
if (sql?.filename) {
|
|
5736
|
+
return { path: path6.dirname(read2.path), dir: path6.dirname(read2.dir) };
|
|
5583
5737
|
}
|
|
5584
|
-
|
|
5585
|
-
}
|
|
5586
|
-
/** 获取连接池当前状态 */
|
|
5587
|
-
getStatus() {
|
|
5588
|
-
const inUse = this.pool.filter((c) => c.inUse).length;
|
|
5589
|
-
return {
|
|
5590
|
-
total: this.pool.length,
|
|
5591
|
-
inUse,
|
|
5592
|
-
idle: this.pool.length - inUse,
|
|
5593
|
-
waiting: this.waitingQueue.length,
|
|
5594
|
-
min: this.config.poolMin,
|
|
5595
|
-
max: this.config.poolMax
|
|
5596
|
-
};
|
|
5597
|
-
}
|
|
5598
|
-
/** 销毁连接池,释放所有资源 */
|
|
5599
|
-
async destroy() {
|
|
5600
|
-
if (this.isDestroyed) return;
|
|
5601
|
-
this.isDestroyed = true;
|
|
5602
|
-
if (this.idleCheckerTimer) {
|
|
5603
|
-
clearInterval(this.idleCheckerTimer);
|
|
5604
|
-
this.idleCheckerTimer = null;
|
|
5605
|
-
}
|
|
5606
|
-
for (const waiter of this.waitingQueue) {
|
|
5607
|
-
clearTimeout(waiter.timer);
|
|
5608
|
-
waiter.reject(new Error("Pool has been closed"));
|
|
5738
|
+
return { path: read2.path, dir: read2.dir };
|
|
5609
5739
|
}
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
};
|
|
5616
|
-
var DbSQLite3 = class {
|
|
5617
|
-
constructor(config) {
|
|
5618
|
-
this.pool = null;
|
|
5619
|
-
this.singleDb = null;
|
|
5620
|
-
this.singleDbReady = null;
|
|
5621
|
-
/** 串行化同库 CRUD,避免 node-sqlite3 回调并发导致锁等待与连接泄漏 */
|
|
5622
|
-
this.opChain = Promise.resolve();
|
|
5623
|
-
this.config = config;
|
|
5624
|
-
const verbose = sqlite3.verbose();
|
|
5625
|
-
if (config.usePool === true) {
|
|
5626
|
-
this.pool = new SQLitePool({
|
|
5627
|
-
database: config.database,
|
|
5628
|
-
poolMin: config.poolMin ?? 1,
|
|
5629
|
-
poolMax: config.poolMax ?? 1,
|
|
5630
|
-
idleTimeout: config.idleTimeout,
|
|
5631
|
-
acquireTimeout: config.acquireTimeout ?? DEFAULT_ACQUIRE_TIMEOUT_MS,
|
|
5632
|
-
busyTimeout: config.busyTimeout ?? DEFAULT_BUSY_TIMEOUT_MS,
|
|
5633
|
-
enableCreateTable: config.create,
|
|
5634
|
-
$asai: config.$asai
|
|
5635
|
-
});
|
|
5636
|
-
} else {
|
|
5637
|
-
this.singleDbReady = this.initSingleConnection(config.database, verbose);
|
|
5740
|
+
getNewPath(file, sql) {
|
|
5741
|
+
if (sql?.filename) {
|
|
5742
|
+
return this.getAbsPath(path6.join(file, sql.filename), true);
|
|
5743
|
+
}
|
|
5744
|
+
return this.getAbsPath(file, true);
|
|
5638
5745
|
}
|
|
5639
|
-
|
|
5640
|
-
static {
|
|
5641
|
-
__name(this, "DbSQLite3");
|
|
5642
|
-
}
|
|
5643
|
-
runSerialized(task) {
|
|
5644
|
-
const run = this.opChain.then(() => task());
|
|
5645
|
-
this.opChain = run.then(
|
|
5646
|
-
() => void 0,
|
|
5647
|
-
() => void 0
|
|
5648
|
-
);
|
|
5649
|
-
return run;
|
|
5650
|
-
}
|
|
5651
|
-
dbLog(...args) {
|
|
5652
|
-
(this.config?.$asai?.$log || console.log)("sqlite", ...args);
|
|
5653
|
-
}
|
|
5654
|
-
async initSingleConnection(databasePath, verbose) {
|
|
5655
|
-
const absolutePath = path6.resolve(databasePath);
|
|
5656
|
-
const dir = path6.dirname(absolutePath);
|
|
5657
|
-
if (!fs11.existsSync(dir)) {
|
|
5658
|
-
fs11.mkdirSync(dir, { recursive: true });
|
|
5659
|
-
this.dbLog("mkdir", "[MKDIR]", dir);
|
|
5660
|
-
}
|
|
5661
|
-
return new Promise((resolve7, reject) => {
|
|
5662
|
-
this.singleDb = new verbose.Database(absolutePath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, async (err) => {
|
|
5663
|
-
if (err) {
|
|
5664
|
-
this.dbLog("open failed", "[ERROR] Failed to open single connection:", absolutePath, err.message);
|
|
5665
|
-
reject(err);
|
|
5666
|
-
return;
|
|
5667
|
-
}
|
|
5668
|
-
try {
|
|
5669
|
-
await configureSqliteConnection(this.singleDb, this.config.busyTimeout ?? DEFAULT_BUSY_TIMEOUT_MS);
|
|
5670
|
-
this.dbLog("open", "[OPEN] Single connection:", absolutePath);
|
|
5671
|
-
resolve7();
|
|
5672
|
-
} catch (pragmaErr) {
|
|
5673
|
-
this.singleDb.close(() => {
|
|
5674
|
-
});
|
|
5675
|
-
reject(pragmaErr);
|
|
5676
|
-
}
|
|
5677
|
-
});
|
|
5678
|
-
});
|
|
5679
|
-
}
|
|
5680
|
-
getErr(_sql, err) {
|
|
5681
|
-
return err?.message || null;
|
|
5682
|
-
}
|
|
5683
|
-
getResult(rows) {
|
|
5684
|
-
return rows ?? "OK";
|
|
5685
|
-
}
|
|
5686
|
-
/**
|
|
5687
|
-
* 执行 SQL 查询(回调风格)
|
|
5688
|
-
*/
|
|
5689
|
-
query(sql, callback) {
|
|
5690
|
-
const sqlStr = Sql_default.makeSql(sql);
|
|
5691
|
-
const executeQuery = /* @__PURE__ */ __name((db, release) => {
|
|
5692
|
-
const finish = /* @__PURE__ */ __name((err, rows) => {
|
|
5693
|
-
release?.();
|
|
5694
|
-
callback(this.getErr(sql, err), this.getResult(rows));
|
|
5695
|
-
}, "finish");
|
|
5746
|
+
write(file, data, sql) {
|
|
5696
5747
|
try {
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
finish(err, "OK");
|
|
5702
|
-
});
|
|
5703
|
-
}
|
|
5748
|
+
const filePath = sql?.type === "insert" ? this.getNewPath(file, sql) : this.getReadPath(file, sql).path || this.getNewPath(file, sql);
|
|
5749
|
+
this.dbLog("write", "[WRITE]", filePath);
|
|
5750
|
+
fs11.writeFileSync(filePath, data, "utf8");
|
|
5751
|
+
return "ok";
|
|
5704
5752
|
} catch (e) {
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
}
|
|
5708
|
-
}, "executeQuery");
|
|
5709
|
-
if (this.pool) {
|
|
5710
|
-
this.pool.acquire().then((conn) => {
|
|
5711
|
-
executeQuery(conn.db, () => this.pool.release(conn));
|
|
5712
|
-
}).catch((err) => {
|
|
5713
|
-
callback(this.getErr(sql, err), this.getResult("pool error"));
|
|
5714
|
-
});
|
|
5715
|
-
} else {
|
|
5716
|
-
(this.singleDbReady || Promise.resolve()).then(() => {
|
|
5717
|
-
executeQuery(this.singleDb);
|
|
5718
|
-
}).catch((err) => {
|
|
5719
|
-
callback(this.getErr(sql, err), this.getResult("connection error"));
|
|
5720
|
-
});
|
|
5721
|
-
}
|
|
5722
|
-
}
|
|
5723
|
-
/** 执行 SQL 操作(Promise 风格,串行 + 缺表时自动建表并重试一次) */
|
|
5724
|
-
sqlDb(sql) {
|
|
5725
|
-
return this.runSerialized(() => this.executeSqlDb(sql));
|
|
5726
|
-
}
|
|
5727
|
-
async executeSqlDb(sql) {
|
|
5728
|
-
try {
|
|
5729
|
-
return await promisifySqlDb(this.query.bind(this), sql);
|
|
5730
|
-
} catch (err) {
|
|
5731
|
-
const errObj = { message: String(err?.message || err || "") };
|
|
5732
|
-
if (this.config.create && sql.type === "insert" && isTableMissingError(errObj)) {
|
|
5733
|
-
await this.createTable(sql);
|
|
5734
|
-
return promisifySqlDb(this.query.bind(this), sql);
|
|
5753
|
+
this.dbLog("write failed", "[WRITE ERROR]", e);
|
|
5754
|
+
return e;
|
|
5735
5755
|
}
|
|
5736
|
-
throw err;
|
|
5737
5756
|
}
|
|
5738
|
-
|
|
5739
|
-
/**
|
|
5740
|
-
* 自动建表(当表不存在时触发)
|
|
5741
|
-
*/
|
|
5742
|
-
async createTable(sql) {
|
|
5743
|
-
const createSql = Sql_default.createSql(sql, "sqlite");
|
|
5744
|
-
if (!createSql) return;
|
|
5745
|
-
if (this.pool) {
|
|
5746
|
-
let conn = null;
|
|
5757
|
+
read(file, sql, encoding = "utf8") {
|
|
5747
5758
|
try {
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5759
|
+
const read2 = this.getReadPath(file, sql);
|
|
5760
|
+
if (!read2.path) {
|
|
5761
|
+
return { error: "empty" };
|
|
5762
|
+
}
|
|
5763
|
+
this.dbLog("read", "[READ]", read2.path);
|
|
5764
|
+
const data = fs11.readFileSync(read2.path, { encoding }) || "";
|
|
5765
|
+
const fileName = this.fileExt && read2.dir.endsWith(this.fileExt) ? read2.dir : read2.dir + this.fileExt;
|
|
5766
|
+
return [{ data, file: fileName }];
|
|
5756
5767
|
} catch (e) {
|
|
5757
|
-
this.dbLog("
|
|
5758
|
-
|
|
5759
|
-
if (conn) this.pool.release(conn);
|
|
5768
|
+
this.dbLog("read failed", "[READ ERROR]", e);
|
|
5769
|
+
return e;
|
|
5760
5770
|
}
|
|
5761
|
-
} else {
|
|
5762
|
-
await this.singleDbReady;
|
|
5763
|
-
await new Promise((resolve7, reject) => {
|
|
5764
|
-
this.singleDb.run(createSql, (err) => {
|
|
5765
|
-
if (err) reject(err);
|
|
5766
|
-
else resolve7();
|
|
5767
|
-
});
|
|
5768
|
-
});
|
|
5769
|
-
}
|
|
5770
|
-
}
|
|
5771
|
-
/** 获取连接池状态(仅连接池模式有效) */
|
|
5772
|
-
getPoolStatus() {
|
|
5773
|
-
if (this.pool) {
|
|
5774
|
-
return this.pool.getStatus();
|
|
5775
|
-
}
|
|
5776
|
-
return { mode: "single-connection" };
|
|
5777
|
-
}
|
|
5778
|
-
/** 关闭所有连接 */
|
|
5779
|
-
async close() {
|
|
5780
|
-
if (this.pool) {
|
|
5781
|
-
await this.pool.destroy();
|
|
5782
|
-
} else if (this.singleDb) {
|
|
5783
|
-
await new Promise((resolve7, reject) => {
|
|
5784
|
-
this.singleDb.close((err) => {
|
|
5785
|
-
if (err) reject(err);
|
|
5786
|
-
else resolve7();
|
|
5787
|
-
});
|
|
5788
|
-
});
|
|
5789
|
-
}
|
|
5790
|
-
}
|
|
5791
|
-
};
|
|
5792
|
-
var Index_default3 = DbSQLite3;
|
|
5793
|
-
|
|
5794
|
-
// src/sysserver/db/src/file/Index.ts
|
|
5795
|
-
import * as fs12 from "fs";
|
|
5796
|
-
import * as path7 from "path";
|
|
5797
|
-
var DbFile = class {
|
|
5798
|
-
static {
|
|
5799
|
-
__name(this, "DbFile");
|
|
5800
|
-
}
|
|
5801
|
-
constructor(config) {
|
|
5802
|
-
this.config = config;
|
|
5803
|
-
this.fileExt = config.ext === ".*" ? "" : config.ext || "";
|
|
5804
|
-
this.fileDel = (config.del || 0) !== 0;
|
|
5805
|
-
this.createDir = config.create || false;
|
|
5806
|
-
this.baseDirAbs = path7.resolve(process.cwd(), config.dir);
|
|
5807
|
-
}
|
|
5808
|
-
dbLog(...args) {
|
|
5809
|
-
(this.config?.$asai?.$log || console.log)("FILE", ...args);
|
|
5810
|
-
}
|
|
5811
|
-
checkFile(file) {
|
|
5812
|
-
return fs12.existsSync(this.getAbsPath(file, false));
|
|
5813
|
-
}
|
|
5814
|
-
/** 解析相对路径并限制在数据根目录内,防止路径穿越(跨平台) */
|
|
5815
|
-
resolvePath(relPath) {
|
|
5816
|
-
if (!relPath) {
|
|
5817
|
-
return this.baseDirAbs;
|
|
5818
|
-
}
|
|
5819
|
-
const abs = path7.isAbsolute(relPath) ? path7.normalize(relPath) : path7.resolve(this.baseDirAbs, relPath);
|
|
5820
|
-
if (!isPathInside(this.baseDirAbs, abs)) {
|
|
5821
|
-
return this.baseDirAbs;
|
|
5822
|
-
}
|
|
5823
|
-
return abs;
|
|
5824
|
-
}
|
|
5825
|
-
/** 将 table 与文件名片段拼为平台无关相对路径 */
|
|
5826
|
-
tableFilePath(table, ...segments) {
|
|
5827
|
-
return joinTablePath(table, ...segments);
|
|
5828
|
-
}
|
|
5829
|
-
normalizeRelPath(relPath) {
|
|
5830
|
-
if (!relPath) {
|
|
5831
|
-
return "";
|
|
5832
|
-
}
|
|
5833
|
-
return path7.isAbsolute(relPath) ? path7.relative(this.baseDirAbs, relPath) : relPath;
|
|
5834
|
-
}
|
|
5835
|
-
ensureDir(relPath) {
|
|
5836
|
-
const relative4 = this.normalizeRelPath(relPath);
|
|
5837
|
-
const dirPath = path7.dirname(relative4);
|
|
5838
|
-
const absDir = this.resolvePath(dirPath === "." ? "" : dirPath);
|
|
5839
|
-
if (this.createDir && !fs12.existsSync(absDir)) {
|
|
5840
|
-
this.dbLog("mkdir", "[MKDIR]", absDir);
|
|
5841
|
-
fs12.mkdirSync(absDir, { recursive: true });
|
|
5842
|
-
}
|
|
5843
|
-
return absDir;
|
|
5844
|
-
}
|
|
5845
|
-
getAbsPath(file, autoCreateDir = true) {
|
|
5846
|
-
let rel = file;
|
|
5847
|
-
if (this.fileExt && !rel.endsWith(this.fileExt)) {
|
|
5848
|
-
rel += this.fileExt;
|
|
5849
|
-
}
|
|
5850
|
-
if (autoCreateDir) {
|
|
5851
|
-
this.ensureDir(rel);
|
|
5852
|
-
}
|
|
5853
|
-
return this.resolvePath(rel);
|
|
5854
|
-
}
|
|
5855
|
-
deldir(relativePath) {
|
|
5856
|
-
const absPath = this.resolvePath(relativePath);
|
|
5857
|
-
if (!fs12.existsSync(absPath)) {
|
|
5858
|
-
return;
|
|
5859
|
-
}
|
|
5860
|
-
this.dbLog("delete", "[RMDIR]", absPath);
|
|
5861
|
-
const entries = fs12.readdirSync(absPath, { withFileTypes: true });
|
|
5862
|
-
for (const entry of entries) {
|
|
5863
|
-
const current = path7.join(absPath, entry.name);
|
|
5864
|
-
if (entry.isDirectory()) {
|
|
5865
|
-
this.deldir(path7.join(relativePath, entry.name));
|
|
5866
|
-
} else {
|
|
5867
|
-
this.dbLog("delete", "[UNLINK]", current);
|
|
5868
|
-
fs12.unlinkSync(current);
|
|
5869
|
-
}
|
|
5870
|
-
}
|
|
5871
|
-
fs12.rmdirSync(absPath);
|
|
5872
|
-
}
|
|
5873
|
-
getReadPath(file, sql) {
|
|
5874
|
-
const candidates = [];
|
|
5875
|
-
if (sql?.filename) {
|
|
5876
|
-
candidates.push({ dir: path7.join(file, sql.filename) });
|
|
5877
|
-
candidates.push({ dir: path7.join(file + ".delete" + this.fileExt, sql.filename) });
|
|
5878
|
-
} else {
|
|
5879
|
-
candidates.push({ dir: file });
|
|
5880
|
-
candidates.push({ dir: file + this.fileExt + ".delete" });
|
|
5881
|
-
}
|
|
5882
|
-
for (const candidate of candidates) {
|
|
5883
|
-
const abs = this.getAbsPath(candidate.dir, false);
|
|
5884
|
-
if (fs12.existsSync(abs)) {
|
|
5885
|
-
return { path: abs, dir: candidate.dir };
|
|
5886
|
-
}
|
|
5887
|
-
}
|
|
5888
|
-
return { path: "", dir: "" };
|
|
5889
|
-
}
|
|
5890
|
-
getDelPath(file, sql) {
|
|
5891
|
-
const read2 = this.getReadPath(file, sql);
|
|
5892
|
-
if (!read2.path) {
|
|
5893
|
-
return { path: "", dir: read2.dir };
|
|
5894
5771
|
}
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
}
|
|
5916
|
-
}
|
|
5917
|
-
read(file, sql, encoding = "utf8") {
|
|
5918
|
-
try {
|
|
5919
|
-
const read2 = this.getReadPath(file, sql);
|
|
5920
|
-
if (!read2.path) {
|
|
5921
|
-
return { error: "empty" };
|
|
5922
|
-
}
|
|
5923
|
-
this.dbLog("read", "[READ]", read2.path);
|
|
5924
|
-
const data = fs12.readFileSync(read2.path, { encoding }) || "";
|
|
5925
|
-
const fileName = this.fileExt && read2.dir.endsWith(this.fileExt) ? read2.dir : read2.dir + this.fileExt;
|
|
5926
|
-
return [{ data, file: fileName }];
|
|
5927
|
-
} catch (e) {
|
|
5928
|
-
this.dbLog("read failed", "[READ ERROR]", e);
|
|
5929
|
-
return e;
|
|
5930
|
-
}
|
|
5931
|
-
}
|
|
5932
|
-
del(file, sql) {
|
|
5933
|
-
try {
|
|
5934
|
-
const del = this.getDelPath(file, sql);
|
|
5935
|
-
if (!del.path) {
|
|
5936
|
-
return "";
|
|
5937
|
-
}
|
|
5938
|
-
if (this.fileDel && sql?.deltrue) {
|
|
5939
|
-
const target = del.path + ".delete" + this.fileExt;
|
|
5940
|
-
this.dbLog("soft delete", "[SOFT DELETE]", `${del.path} -> ${target}`);
|
|
5941
|
-
fs12.renameSync(del.path, target);
|
|
5942
|
-
} else if (sql?.filename) {
|
|
5943
|
-
this.deldir(del.dir);
|
|
5944
|
-
} else {
|
|
5945
|
-
this.dbLog("delete", "[DELETE]", del.path);
|
|
5946
|
-
fs12.unlinkSync(del.path);
|
|
5772
|
+
del(file, sql) {
|
|
5773
|
+
try {
|
|
5774
|
+
const del = this.getDelPath(file, sql);
|
|
5775
|
+
if (!del.path) {
|
|
5776
|
+
return "";
|
|
5777
|
+
}
|
|
5778
|
+
if (this.fileDel && sql?.deltrue) {
|
|
5779
|
+
const target = del.path + ".delete" + this.fileExt;
|
|
5780
|
+
this.dbLog("soft delete", "[SOFT DELETE]", `${del.path} -> ${target}`);
|
|
5781
|
+
fs11.renameSync(del.path, target);
|
|
5782
|
+
} else if (sql?.filename) {
|
|
5783
|
+
this.deldir(del.dir);
|
|
5784
|
+
} else {
|
|
5785
|
+
this.dbLog("delete", "[DELETE]", del.path);
|
|
5786
|
+
fs11.unlinkSync(del.path);
|
|
5787
|
+
}
|
|
5788
|
+
return "ok";
|
|
5789
|
+
} catch (e) {
|
|
5790
|
+
this.dbLog("delete failed", "[DELETE ERROR]", e);
|
|
5791
|
+
return e;
|
|
5947
5792
|
}
|
|
5948
|
-
return "ok";
|
|
5949
|
-
} catch (e) {
|
|
5950
|
-
this.dbLog("delete failed", "[DELETE ERROR]", e);
|
|
5951
|
-
return e;
|
|
5952
5793
|
}
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
return "";
|
|
5959
|
-
}
|
|
5960
|
-
if (this.fileDel) {
|
|
5961
|
-
const extPattern = this.fileExt ? this.fileExt.replace(/\./g, "\\.") : "";
|
|
5962
|
-
const original = del.path.replace(new RegExp(`.delete${extPattern}$`), "");
|
|
5963
|
-
if (original !== del.path) {
|
|
5964
|
-
this.dbLog("restore", "[RESTORE]", `${del.path} -> ${original}`);
|
|
5965
|
-
fs12.renameSync(del.path, original);
|
|
5794
|
+
re(file, sql) {
|
|
5795
|
+
try {
|
|
5796
|
+
const del = this.getDelPath(file, sql);
|
|
5797
|
+
if (!del.path) {
|
|
5798
|
+
return "";
|
|
5966
5799
|
}
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5800
|
+
if (this.fileDel) {
|
|
5801
|
+
const extPattern = this.fileExt ? this.fileExt.replace(/\./g, "\\.") : "";
|
|
5802
|
+
const original = del.path.replace(new RegExp(`.delete${extPattern}$`), "");
|
|
5803
|
+
if (original !== del.path) {
|
|
5804
|
+
this.dbLog("restore", "[RESTORE]", `${del.path} -> ${original}`);
|
|
5805
|
+
fs11.renameSync(del.path, original);
|
|
5806
|
+
}
|
|
5807
|
+
} else {
|
|
5808
|
+
this.dbLog("force delete", "[FORCE DELETE]", del.path);
|
|
5809
|
+
fs11.unlinkSync(del.path);
|
|
5810
|
+
}
|
|
5811
|
+
return "ok";
|
|
5812
|
+
} catch (e) {
|
|
5813
|
+
this.dbLog("restore failed", "[RESTORE ERROR]", e);
|
|
5814
|
+
return e;
|
|
5970
5815
|
}
|
|
5971
|
-
return "ok";
|
|
5972
|
-
} catch (e) {
|
|
5973
|
-
this.dbLog("restore failed", "[RESTORE ERROR]", e);
|
|
5974
|
-
return e;
|
|
5975
5816
|
}
|
|
5976
|
-
|
|
5977
|
-
list(dir, sql) {
|
|
5978
|
-
try {
|
|
5979
|
-
const absDir = this.resolvePath(dir);
|
|
5980
|
-
this.dbLog("list", "[LIST]", absDir);
|
|
5981
|
-
let entries;
|
|
5817
|
+
list(dir, sql) {
|
|
5982
5818
|
try {
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
}
|
|
5987
|
-
const result = { open: [], delete: [] };
|
|
5988
|
-
const deleteSuffix = ".delete" + this.fileExt;
|
|
5989
|
-
for (const entry of entries) {
|
|
5990
|
-
if (!entry.isFile()) continue;
|
|
5991
|
-
const fileName = entry.name;
|
|
5992
|
-
const isDelete = fileName.endsWith(deleteSuffix);
|
|
5993
|
-
const baseName = isDelete ? fileName.slice(0, -deleteSuffix.length) : fileName;
|
|
5994
|
-
if (this.fileExt && !baseName.endsWith(this.fileExt)) continue;
|
|
5995
|
-
const fullPath = path7.join(absDir, fileName);
|
|
5819
|
+
const absDir = this.resolvePath(dir);
|
|
5820
|
+
this.dbLog("list", "[LIST]", absDir);
|
|
5821
|
+
let entries;
|
|
5996
5822
|
try {
|
|
5997
|
-
|
|
5998
|
-
const entryData = { data, file: baseName };
|
|
5999
|
-
if (isDelete) {
|
|
6000
|
-
if (sql?.list) result.delete.push(entryData);
|
|
6001
|
-
} else {
|
|
6002
|
-
result.open.push(entryData);
|
|
6003
|
-
}
|
|
5823
|
+
entries = fs11.readdirSync(absDir, { withFileTypes: true });
|
|
6004
5824
|
} catch {
|
|
5825
|
+
return sql?.list ? { open: [], delete: [] } : [];
|
|
6005
5826
|
}
|
|
5827
|
+
const result = { open: [], delete: [] };
|
|
5828
|
+
const deleteSuffix = ".delete" + this.fileExt;
|
|
5829
|
+
for (const entry of entries) {
|
|
5830
|
+
if (!entry.isFile()) continue;
|
|
5831
|
+
const fileName = entry.name;
|
|
5832
|
+
const isDelete = fileName.endsWith(deleteSuffix);
|
|
5833
|
+
const baseName = isDelete ? fileName.slice(0, -deleteSuffix.length) : fileName;
|
|
5834
|
+
if (this.fileExt && !baseName.endsWith(this.fileExt)) continue;
|
|
5835
|
+
const fullPath = path6.join(absDir, fileName);
|
|
5836
|
+
try {
|
|
5837
|
+
const data = fs11.readFileSync(fullPath, "utf8");
|
|
5838
|
+
const entryData = { data, file: baseName };
|
|
5839
|
+
if (isDelete) {
|
|
5840
|
+
if (sql?.list) result.delete.push(entryData);
|
|
5841
|
+
} else {
|
|
5842
|
+
result.open.push(entryData);
|
|
5843
|
+
}
|
|
5844
|
+
} catch {
|
|
5845
|
+
}
|
|
5846
|
+
}
|
|
5847
|
+
return sql?.list ? result : result.open;
|
|
5848
|
+
} catch (e) {
|
|
5849
|
+
this.dbLog("list failed", "[LIST ERROR]", e);
|
|
5850
|
+
return e;
|
|
6006
5851
|
}
|
|
6007
|
-
return sql?.list ? result : result.open;
|
|
6008
|
-
} catch (e) {
|
|
6009
|
-
this.dbLog("list failed", "[LIST ERROR]", e);
|
|
6010
|
-
return e;
|
|
6011
5852
|
}
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
5853
|
+
/** 从 where 首条条件中提取文件名(去掉 SQL 风格引号) */
|
|
5854
|
+
getFileNameFromWhere(where) {
|
|
5855
|
+
if (!where || !Array.isArray(where)) return "";
|
|
5856
|
+
const first = where.find((item) => Array.isArray(item));
|
|
5857
|
+
const raw = first?.[2];
|
|
5858
|
+
if (typeof raw === "string") {
|
|
5859
|
+
return raw.replace(/^'+|'+$/g, "");
|
|
5860
|
+
}
|
|
5861
|
+
return String(raw ?? "");
|
|
6020
5862
|
}
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
result = this.write(this.tableFilePath(sql.table, file), data, sql);
|
|
6031
|
-
} else if (sql.type === "delete") {
|
|
6032
|
-
const file = this.getFileNameFromWhere(sql.where);
|
|
6033
|
-
result = this.del(this.tableFilePath(sql.table, file), sql);
|
|
6034
|
-
} else if (sql.type === "re") {
|
|
6035
|
-
const file = this.getFileNameFromWhere(sql.where);
|
|
6036
|
-
result = this.re(this.tableFilePath(sql.table, file), sql);
|
|
6037
|
-
} else if (sql.type === "update") {
|
|
6038
|
-
const file = this.getFileNameFromWhere(sql.where);
|
|
6039
|
-
const data = sql.set[0][1];
|
|
6040
|
-
result = this.write(this.tableFilePath(sql.table, file), data, sql);
|
|
6041
|
-
} else if (sql.type === "select") {
|
|
6042
|
-
if (sql?.where?.length && sql?.as !== 2) {
|
|
5863
|
+
sqlDb(sql) {
|
|
5864
|
+
return new Promise((resolve6, reject) => {
|
|
5865
|
+
try {
|
|
5866
|
+
let result;
|
|
5867
|
+
const tableDir = this.tableFilePath(sql.table);
|
|
5868
|
+
if (sql.type === "insert") {
|
|
5869
|
+
const [file, data] = sql.value[0];
|
|
5870
|
+
result = this.write(this.tableFilePath(sql.table, file), data, sql);
|
|
5871
|
+
} else if (sql.type === "delete") {
|
|
6043
5872
|
const file = this.getFileNameFromWhere(sql.where);
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
5873
|
+
result = this.del(this.tableFilePath(sql.table, file), sql);
|
|
5874
|
+
} else if (sql.type === "re") {
|
|
5875
|
+
const file = this.getFileNameFromWhere(sql.where);
|
|
5876
|
+
result = this.re(this.tableFilePath(sql.table, file), sql);
|
|
5877
|
+
} else if (sql.type === "update") {
|
|
5878
|
+
const file = this.getFileNameFromWhere(sql.where);
|
|
5879
|
+
const data = sql.set[0][1];
|
|
5880
|
+
result = this.write(this.tableFilePath(sql.table, file), data, sql);
|
|
5881
|
+
} else if (sql.type === "select") {
|
|
5882
|
+
if (sql?.where?.length && sql?.as !== 2) {
|
|
5883
|
+
const file = this.getFileNameFromWhere(sql.where);
|
|
5884
|
+
const readRes = this.read(this.tableFilePath(sql.table, file), sql);
|
|
5885
|
+
const resObj = {};
|
|
5886
|
+
if (readRes?.[0]?.file && !readRes.error) {
|
|
5887
|
+
if (sql.as === 1) {
|
|
5888
|
+
try {
|
|
5889
|
+
const parsed = JSON.parse(readRes[0].data);
|
|
5890
|
+
if (sql.field?.length) {
|
|
5891
|
+
sql.field.forEach((f) => {
|
|
5892
|
+
resObj[f] = parsed[f] ?? "";
|
|
5893
|
+
});
|
|
5894
|
+
} else {
|
|
5895
|
+
Object.assign(resObj, parsed);
|
|
5896
|
+
}
|
|
5897
|
+
} catch {
|
|
5898
|
+
}
|
|
5899
|
+
} else {
|
|
5900
|
+
const fields = sql.field;
|
|
5901
|
+
resObj[fields[0]] = readRes[0].file;
|
|
5902
|
+
if (fields[1]) {
|
|
5903
|
+
resObj[fields[1]] = readRes[0].data;
|
|
6056
5904
|
}
|
|
6057
|
-
} catch {
|
|
6058
|
-
}
|
|
6059
|
-
} else {
|
|
6060
|
-
const fields = sql.field;
|
|
6061
|
-
resObj[fields[0]] = readRes[0].file;
|
|
6062
|
-
if (fields[1]) {
|
|
6063
|
-
resObj[fields[1]] = readRes[0].data;
|
|
6064
5905
|
}
|
|
6065
5906
|
}
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
5907
|
+
result = [resObj];
|
|
5908
|
+
} else {
|
|
5909
|
+
const listRes = this.list(tableDir, sql);
|
|
5910
|
+
if (listRes && !listRes.error) {
|
|
5911
|
+
if (sql?.list) {
|
|
5912
|
+
const mapEntry = /* @__PURE__ */ __name((el) => {
|
|
5913
|
+
const fields = sql.field;
|
|
5914
|
+
const item = { [fields[0]]: el.file };
|
|
5915
|
+
if (fields[1]) item[fields[1]] = el.data;
|
|
5916
|
+
return item;
|
|
5917
|
+
}, "mapEntry");
|
|
5918
|
+
if (listRes.open) listRes.open = listRes.open.map(mapEntry);
|
|
5919
|
+
if (listRes.delete) listRes.delete = listRes.delete.map(mapEntry);
|
|
5920
|
+
result = listRes;
|
|
5921
|
+
} else {
|
|
5922
|
+
if (sql?.as === 2) {
|
|
5923
|
+
let items = listRes;
|
|
5924
|
+
if (sql.where?.length) {
|
|
5925
|
+
const where = sql.where;
|
|
5926
|
+
const whereType = typeof where[0] === "string" ? where[0] : "and";
|
|
5927
|
+
items = items.filter((el) => {
|
|
5928
|
+
let match = whereType === "and";
|
|
5929
|
+
for (let i = 0; i < where.length; i++) {
|
|
5930
|
+
if (typeof where[i] === "string") continue;
|
|
5931
|
+
const cond = where[i];
|
|
5932
|
+
if (!cond) continue;
|
|
5933
|
+
const field = cond[0];
|
|
5934
|
+
const searchVal = String(cond[2]).replace(/%/g, "");
|
|
5935
|
+
let fieldVal = "";
|
|
5936
|
+
try {
|
|
5937
|
+
fieldVal = JSON.parse(el.data)[field] ?? "";
|
|
5938
|
+
} catch {
|
|
5939
|
+
}
|
|
5940
|
+
const condMatch = String(fieldVal).includes(searchVal);
|
|
5941
|
+
if (whereType === "and") {
|
|
5942
|
+
match = match && condMatch;
|
|
5943
|
+
if (!match) break;
|
|
5944
|
+
} else {
|
|
5945
|
+
match = match || condMatch;
|
|
5946
|
+
if (match) break;
|
|
5947
|
+
}
|
|
6099
5948
|
}
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
5949
|
+
return match;
|
|
5950
|
+
});
|
|
5951
|
+
}
|
|
5952
|
+
items = items.map((el) => {
|
|
5953
|
+
const obj = {};
|
|
5954
|
+
try {
|
|
5955
|
+
const parsed = JSON.parse(el.data);
|
|
5956
|
+
if (sql.field?.length) {
|
|
5957
|
+
sql.field.forEach((f) => {
|
|
5958
|
+
obj[f] = parsed[f] ?? "";
|
|
5959
|
+
});
|
|
6104
5960
|
} else {
|
|
6105
|
-
|
|
6106
|
-
if (match) break;
|
|
5961
|
+
Object.assign(obj, parsed);
|
|
6107
5962
|
}
|
|
5963
|
+
} catch {
|
|
6108
5964
|
}
|
|
6109
|
-
return
|
|
5965
|
+
return obj;
|
|
6110
5966
|
});
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
}
|
|
6121
|
-
Object.assign(obj, parsed);
|
|
6122
|
-
}
|
|
6123
|
-
} catch {
|
|
5967
|
+
if (sql.order?.length) {
|
|
5968
|
+
const orderField = sql.order[0];
|
|
5969
|
+
const orderFieldName = typeof orderField === "string" ? orderField : orderField[0];
|
|
5970
|
+
const direction = typeof orderField === "string" ? "ASC" : String(orderField[1]).toUpperCase();
|
|
5971
|
+
items.sort((a, b) => {
|
|
5972
|
+
const valA = String(a[orderFieldName] ?? "");
|
|
5973
|
+
const valB = String(b[orderFieldName] ?? "");
|
|
5974
|
+
const cmp = valA.localeCompare(valB);
|
|
5975
|
+
return direction === "DESC" ? -cmp : cmp;
|
|
5976
|
+
});
|
|
6124
5977
|
}
|
|
6125
|
-
|
|
6126
|
-
}
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
const valA = String(a[orderFieldName] ?? "");
|
|
6133
|
-
const valB = String(b[orderFieldName] ?? "");
|
|
6134
|
-
const cmp = valA.localeCompare(valB);
|
|
6135
|
-
return direction === "DESC" ? -cmp : cmp;
|
|
6136
|
-
});
|
|
5978
|
+
result = items;
|
|
5979
|
+
} else {
|
|
5980
|
+
const fields = sql.field;
|
|
5981
|
+
result = listRes.map((el) => ({
|
|
5982
|
+
[fields[0]]: el.file,
|
|
5983
|
+
...fields[1] ? { [fields[1]]: el.data } : {}
|
|
5984
|
+
}));
|
|
6137
5985
|
}
|
|
6138
|
-
result = items;
|
|
6139
|
-
} else {
|
|
6140
|
-
const fields = sql.field;
|
|
6141
|
-
result = listRes.map((el) => ({
|
|
6142
|
-
[fields[0]]: el.file,
|
|
6143
|
-
...fields[1] ? { [fields[1]]: el.data } : {}
|
|
6144
|
-
}));
|
|
6145
5986
|
}
|
|
5987
|
+
} else {
|
|
5988
|
+
result = sql?.list ? { open: [], delete: [] } : [];
|
|
6146
5989
|
}
|
|
6147
|
-
} else {
|
|
6148
|
-
result = sql?.list ? { open: [], delete: [] } : [];
|
|
6149
5990
|
}
|
|
6150
5991
|
}
|
|
5992
|
+
if (result !== void 0) {
|
|
5993
|
+
resolve6(result);
|
|
5994
|
+
} else {
|
|
5995
|
+
reject("");
|
|
5996
|
+
}
|
|
5997
|
+
} catch (e) {
|
|
5998
|
+
reject(e);
|
|
6151
5999
|
}
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
} else {
|
|
6155
|
-
reject("");
|
|
6156
|
-
}
|
|
6157
|
-
} catch (e) {
|
|
6158
|
-
reject(e);
|
|
6159
|
-
}
|
|
6160
|
-
});
|
|
6000
|
+
});
|
|
6001
|
+
}
|
|
6161
6002
|
}
|
|
6162
|
-
|
|
6163
|
-
|
|
6003
|
+
return DbFile;
|
|
6004
|
+
}
|
|
6005
|
+
__name(dbDriver, "dbDriver");
|
|
6006
|
+
var Index_default2 = dbDriver;
|
|
6164
6007
|
|
|
6165
6008
|
// src/sysserver/db/db.ts
|
|
6166
|
-
var
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
function
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6009
|
+
var BUILTIN_FILE = "file";
|
|
6010
|
+
function buildFileDriver() {
|
|
6011
|
+
return Index_default2({ IdbType: {}, Sql: Sql_default, DbCommon: common_exports });
|
|
6012
|
+
}
|
|
6013
|
+
__name(buildFileDriver, "buildFileDriver");
|
|
6014
|
+
function resolveDriver(type, dbDrivers) {
|
|
6015
|
+
if (type === BUILTIN_FILE) {
|
|
6016
|
+
return buildFileDriver();
|
|
6017
|
+
}
|
|
6018
|
+
const factory = dbDrivers[type];
|
|
6019
|
+
if (typeof factory !== "function") {
|
|
6020
|
+
return null;
|
|
6175
6021
|
}
|
|
6176
|
-
return {};
|
|
6022
|
+
return factory({ IdbType: {}, Sql: Sql_default, DbCommon: common_exports });
|
|
6177
6023
|
}
|
|
6178
|
-
__name(
|
|
6024
|
+
__name(resolveDriver, "resolveDriver");
|
|
6025
|
+
var initDb = /* @__PURE__ */ __name((type = BUILTIN_FILE, config = {}, dbDrivers = {}) => {
|
|
6026
|
+
let Driver = resolveDriver(type, dbDrivers);
|
|
6027
|
+
if (!Driver) {
|
|
6028
|
+
if (type !== BUILTIN_FILE) {
|
|
6029
|
+
console.warn(
|
|
6030
|
+
`[initDb] \u672A\u6CE8\u518C\u9A71\u52A8 "${type}"\uFF08\u8BF7\u5728 sysserver($asai, { db: { ${type}: driver } }) \u6302\u8F7D\uFF09\uFF0C\u5DF2\u56DE\u9000 file \u5B58\u50A8`
|
|
6031
|
+
);
|
|
6032
|
+
}
|
|
6033
|
+
Driver = buildFileDriver();
|
|
6034
|
+
}
|
|
6035
|
+
return new Driver(config);
|
|
6036
|
+
}, "initDb");
|
|
6179
6037
|
var db_default = initDb;
|
|
6180
6038
|
|
|
6181
6039
|
// src/sysserver/index.ts
|
|
6182
|
-
|
|
6040
|
+
function sysserver($asai, opt = {}) {
|
|
6183
6041
|
if (!$asai.$lib) $asai.$lib = {};
|
|
6042
|
+
$asai.$lib = { ...lib_default, ...$asai.$lib, ...opt || {} };
|
|
6184
6043
|
$asai.dataserver = db_default;
|
|
6185
|
-
$asai.$lib = { ...lib_default, ...$asai.$lib };
|
|
6186
|
-
if (!$asai.asaifs) {
|
|
6187
|
-
$asai.asaifs = $asai.$lib.AsFs;
|
|
6188
|
-
}
|
|
6189
6044
|
$asai.$lib.ErrorCode?.loadErrorCodes?.($asai);
|
|
6190
6045
|
if (!$asai.$lib.api) $asai.$lib.api = {};
|
|
6191
6046
|
$asai.$lib.api = { ...api_default, ...$asai.$lib.api };
|
|
6192
6047
|
if (!$asai.$lib.ws) $asai.$lib.ws = {};
|
|
6193
6048
|
$asai.$lib.ws = { ...ws_default, ...$asai.$lib.ws };
|
|
6194
|
-
}
|
|
6049
|
+
}
|
|
6050
|
+
__name(sysserver, "sysserver");
|
|
6195
6051
|
|
|
6196
6052
|
// src/config/validate.ts
|
|
6197
6053
|
function normalizeHostConfig(cfg) {
|
|
@@ -6306,6 +6162,12 @@ function normalizeUserStoreConfig(cfg) {
|
|
|
6306
6162
|
} else if (store.type === "sqlite") {
|
|
6307
6163
|
if (!store.opt.database) store.opt.database = "./webdata/webdb/users/users.db";
|
|
6308
6164
|
if (store.opt.create == null) store.opt.create = 1;
|
|
6165
|
+
} else if (store.type === "mysql") {
|
|
6166
|
+
if (!store.opt.host) store.opt.host = "127.0.0.1";
|
|
6167
|
+
if (!store.opt.user) store.opt.user = "root";
|
|
6168
|
+
if (store.opt.password == null) store.opt.password = "";
|
|
6169
|
+
if (!store.opt.database) store.opt.database = "asai";
|
|
6170
|
+
if (store.opt.create == null) store.opt.create = 1;
|
|
6309
6171
|
}
|
|
6310
6172
|
}
|
|
6311
6173
|
__name(normalizeUserStoreConfig, "normalizeUserStoreConfig");
|
|
@@ -6348,6 +6210,30 @@ function normalizeDataStoresConfig(cfg) {
|
|
|
6348
6210
|
if (!stores.user && cfg.userStore) {
|
|
6349
6211
|
stores.user = { type: cfg.userStore.type || "file", opt: { ...cfg.userStore.opt || {} } };
|
|
6350
6212
|
}
|
|
6213
|
+
if (!stores.user) {
|
|
6214
|
+
stores.user = {
|
|
6215
|
+
type: "file",
|
|
6216
|
+
opt: { dir: "webdata/webdb/users/", ext: ".json", create: 1 }
|
|
6217
|
+
};
|
|
6218
|
+
}
|
|
6219
|
+
if (stores.user && typeof stores.user === "object") {
|
|
6220
|
+
const userStore = stores.user;
|
|
6221
|
+
const userType = userStore.type || "file";
|
|
6222
|
+
if (!userStore.type) userStore.type = "file";
|
|
6223
|
+
if (!userStore.opt || typeof userStore.opt !== "object") userStore.opt = {};
|
|
6224
|
+
if (userType === "file") {
|
|
6225
|
+
if (!userStore.opt.dir) userStore.opt.dir = "webdata/webdb/users/";
|
|
6226
|
+
if (!userStore.opt.ext) userStore.opt.ext = ".json";
|
|
6227
|
+
} else if (userType === "sqlite") {
|
|
6228
|
+
if (!userStore.opt.database) userStore.opt.database = "./webdata/webdb/users/users.db";
|
|
6229
|
+
} else if (userType === "mysql") {
|
|
6230
|
+
if (!userStore.opt.host) userStore.opt.host = "127.0.0.1";
|
|
6231
|
+
if (!userStore.opt.user) userStore.opt.user = "root";
|
|
6232
|
+
if (userStore.opt.password == null) userStore.opt.password = "";
|
|
6233
|
+
if (!userStore.opt.database) userStore.opt.database = "asai";
|
|
6234
|
+
}
|
|
6235
|
+
if (userStore.opt.create == null) userStore.opt.create = 1;
|
|
6236
|
+
}
|
|
6351
6237
|
if (!stores.log && cfg.logger?.store) {
|
|
6352
6238
|
const s = cfg.logger.store;
|
|
6353
6239
|
stores.log = {
|
|
@@ -6674,10 +6560,10 @@ function hostDefault(mod) {
|
|
|
6674
6560
|
}
|
|
6675
6561
|
__name(hostDefault, "hostDefault");
|
|
6676
6562
|
var hostMain = hostDefault(src_default);
|
|
6677
|
-
var
|
|
6563
|
+
var initAsaiHostNodejs2 = {
|
|
6678
6564
|
name: PLUGIN_NAME,
|
|
6679
6565
|
fn: /* @__PURE__ */ __name(($asai, opt) => hostMain($asai, opt), "fn"),
|
|
6680
|
-
sysserver
|
|
6566
|
+
sysserver,
|
|
6681
6567
|
AsaiCommon: Index_default,
|
|
6682
6568
|
AsaiUtils: utils_exports,
|
|
6683
6569
|
normalizeHostConfig,
|
|
@@ -6714,8 +6600,8 @@ var initAsaiHost2 = {
|
|
|
6714
6600
|
createTransportDataHandler,
|
|
6715
6601
|
wireTransportConnectionLogs
|
|
6716
6602
|
};
|
|
6717
|
-
var PLUGIN_VERSION = true ? "0.0.
|
|
6718
|
-
var index_default =
|
|
6603
|
+
var PLUGIN_VERSION = true ? "0.0.2" : "0.0.1";
|
|
6604
|
+
var index_default = initAsaiHostNodejs2;
|
|
6719
6605
|
export {
|
|
6720
6606
|
Index_default as AsaiCommon,
|
|
6721
6607
|
utils_exports as AsaiUtils,
|
|
@@ -6730,7 +6616,7 @@ export {
|
|
|
6730
6616
|
getTransportBlock,
|
|
6731
6617
|
getTransportDefault,
|
|
6732
6618
|
hasTransportDefault,
|
|
6733
|
-
|
|
6619
|
+
initAsaiHostNodejs2 as initAsaiHostNodejs,
|
|
6734
6620
|
initHostserverApi,
|
|
6735
6621
|
initHostserverWs,
|
|
6736
6622
|
initTransportBridge,
|
|
@@ -6751,7 +6637,7 @@ export {
|
|
|
6751
6637
|
stripForbiddenSecretsFromHostConfig,
|
|
6752
6638
|
syncApiRouteKeys,
|
|
6753
6639
|
syncWsRouteKeys,
|
|
6754
|
-
|
|
6640
|
+
sysserver,
|
|
6755
6641
|
validateAsaiSecrets,
|
|
6756
6642
|
validateHostConfig,
|
|
6757
6643
|
validateHttpAuth,
|