jsir 2.3.2 → 2.3.3
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/cmd/oaa.js +10 -37
- package/deps/room.js +232 -0
- package/deps/setting.js +3 -3
- package/deps/util.js +123 -24
- package/package.json +3 -2
package/cmd/oaa.js
CHANGED
|
@@ -11,7 +11,7 @@ const {
|
|
|
11
11
|
createConsole, setTips, delTips,
|
|
12
12
|
getEditor, errorStr, getConfigDir,
|
|
13
13
|
getFullPath, parseUniqueName, toUniqueName, isJsirFileName, toJsirFileName,
|
|
14
|
-
getAlias, wrapperJsirText, eia
|
|
14
|
+
getAlias, wrapperJsirText, eia, getKeyTips, getValTips
|
|
15
15
|
} = $lib;
|
|
16
16
|
const _args = process.argv.slice(2).map(trim);
|
|
17
17
|
const evalCode = require('../deps/evalCode')
|
|
@@ -28,6 +28,7 @@ const _libDataDir = getLibDataDir()
|
|
|
28
28
|
const _fileWatcherMap = {}
|
|
29
29
|
const _types = setting.fileType
|
|
30
30
|
const console = createConsole();
|
|
31
|
+
const room = require('../deps/room');
|
|
31
32
|
|
|
32
33
|
let lastFilterArg = '';
|
|
33
34
|
let _cmdMapFile = setting.name + 'CmdMap.json'
|
|
@@ -345,28 +346,7 @@ function initRl(callback, promptStr, hidden) {
|
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
function getTipStr(showKey = false) {
|
|
348
|
-
let
|
|
349
|
-
for (let key of Object.keys(setting.tips)) {
|
|
350
|
-
let val = setting.tips[key].map(i => {
|
|
351
|
-
let item = trim(i)
|
|
352
|
-
if (item.indexOf(',') !== -1) {
|
|
353
|
-
item = `[${item}]`;
|
|
354
|
-
}
|
|
355
|
-
return item;
|
|
356
|
-
}).join("|");
|
|
357
|
-
key = trim(key)
|
|
358
|
-
|
|
359
|
-
let item
|
|
360
|
-
if (!showKey && vl(val)) {
|
|
361
|
-
item = val;
|
|
362
|
-
} else if (vl(key)) {
|
|
363
|
-
item = key
|
|
364
|
-
}
|
|
365
|
-
if (item) {
|
|
366
|
-
items.push(item)
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
let tips = items.join(',')
|
|
349
|
+
let tips = showKey ? getKeyTips().join(","):getValTips().join(",")
|
|
370
350
|
if (!showKey && !_isTipsDoneShowKey && tips.length > 64) {
|
|
371
351
|
return '...'
|
|
372
352
|
}
|
|
@@ -380,6 +360,10 @@ function closeRl() {
|
|
|
380
360
|
}
|
|
381
361
|
|
|
382
362
|
function _nextLine(callback, promptStr, hidden, resolve, end, isText) {
|
|
363
|
+
if (!setting.enableNextLine) {
|
|
364
|
+
console.$log(warnStr("[warn]"), "NextLine Disabled");
|
|
365
|
+
return
|
|
366
|
+
}
|
|
383
367
|
end = trim(end)
|
|
384
368
|
if (!_haveWrapperInput) {
|
|
385
369
|
return
|
|
@@ -1345,6 +1329,7 @@ const keywordDef = {
|
|
|
1345
1329
|
quit: {
|
|
1346
1330
|
comment: 'Exit',
|
|
1347
1331
|
exeFn: (args) => {
|
|
1332
|
+
room.offRoom();
|
|
1348
1333
|
delTips();
|
|
1349
1334
|
console.log(infoStr("Bye!"));
|
|
1350
1335
|
_noAppendNextLine = true;
|
|
@@ -1357,6 +1342,8 @@ const keywordDef = {
|
|
|
1357
1342
|
_noAppendNextLine = false
|
|
1358
1343
|
resetCmdMap()
|
|
1359
1344
|
console.log(warnStr(`(${setting.name} ${packageJson.version}) You can start with .help, use * to expand context.`))
|
|
1345
|
+
room.onRoom()
|
|
1346
|
+
nextLine()
|
|
1360
1347
|
},
|
|
1361
1348
|
short: 'p'
|
|
1362
1349
|
},
|
|
@@ -2085,20 +2072,9 @@ async function evalText($text = '', $cmdName = '', $args = []) {
|
|
|
2085
2072
|
$homeDir, $lib, _cmdMap);
|
|
2086
2073
|
}
|
|
2087
2074
|
|
|
2088
|
-
function clearFileLock() {
|
|
2089
|
-
for (let file of Object.keys(fileLockMap)) {
|
|
2090
|
-
fp.rmdir(file)
|
|
2091
|
-
delete setting.fileLock[file]
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
2075
|
function sigExit() {
|
|
2096
|
-
if (!setting.sigExit) {
|
|
2097
|
-
return;
|
|
2098
|
-
}
|
|
2099
2076
|
if (_noAppendNextLine) {
|
|
2100
2077
|
delTips();
|
|
2101
|
-
clearFileLock();
|
|
2102
2078
|
process.exit(0);
|
|
2103
2079
|
} else {
|
|
2104
2080
|
nextLine();
|
|
@@ -2107,15 +2083,12 @@ function sigExit() {
|
|
|
2107
2083
|
|
|
2108
2084
|
process.on('uncaughtException',function(err){
|
|
2109
2085
|
console.$error('uncaughtException', err)
|
|
2110
|
-
_noAppendNextLine || nextLine()
|
|
2111
2086
|
})
|
|
2112
2087
|
process.on('unhandledRejection',function(err){
|
|
2113
2088
|
console.$error('unhandledRejection', err)
|
|
2114
|
-
_noAppendNextLine || nextLine()
|
|
2115
2089
|
})
|
|
2116
2090
|
process.on('rejectionHandled',function(err){
|
|
2117
2091
|
console.$error('rejectionHandled', err)
|
|
2118
|
-
_noAppendNextLine || nextLine()
|
|
2119
2092
|
})
|
|
2120
2093
|
process.on('SIGINT', sigExit);
|
|
2121
2094
|
process.on('SIGTERM', sigExit);
|
package/deps/room.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const {fileJson, fileLock, vl, createConsole, getKeyTips, getValTips} = require('./util');
|
|
3
|
+
const roomDataFile = "jsirRoom.json"
|
|
4
|
+
const console = createConsole();
|
|
5
|
+
const ping = require("ping");
|
|
6
|
+
const net = require("net");
|
|
7
|
+
const setting = require('../deps/setting')
|
|
8
|
+
|
|
9
|
+
function isPidAlive(pid) {
|
|
10
|
+
try {
|
|
11
|
+
process.kill(Number(pid), 0); // 信号 0 不会实际发送,但会检查进程是否存在
|
|
12
|
+
return true; // PID 存在
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (err.code === 'ESRCH') {
|
|
15
|
+
return false; // 进程不存在
|
|
16
|
+
}
|
|
17
|
+
console.$error(`check isPidAlive ${pid} failed`, err)
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function onRoom() {
|
|
23
|
+
return
|
|
24
|
+
if (!setting.roomTid[0]) {
|
|
25
|
+
_onRoom();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function _onRoom() {
|
|
30
|
+
setting.roomTid[0] = setTimeout(async () => {
|
|
31
|
+
try {
|
|
32
|
+
await _initRoom();
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.$error('initRoom', e)
|
|
35
|
+
}
|
|
36
|
+
if (setting.roomTid[0]) {
|
|
37
|
+
_onRoom();
|
|
38
|
+
}
|
|
39
|
+
}, 1000)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function offRoom() {
|
|
43
|
+
return;
|
|
44
|
+
if (setting.roomTid[0]) {
|
|
45
|
+
clearTimeout(setting.roomTid[0])
|
|
46
|
+
setting.roomTid[0] = null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function getTailScaleNodes() {
|
|
51
|
+
console.$log("getNodes")
|
|
52
|
+
let ips = getLocalIPs().ipv4;
|
|
53
|
+
ips = ips.filter(i => i.startsWith("100.")); // tailScale ip前缀
|
|
54
|
+
let nodes = []
|
|
55
|
+
for (let ip of ips) {
|
|
56
|
+
let netIps = await scanLocalNetwork(ip);
|
|
57
|
+
netIps = netIps.filter(i => i !== ip)
|
|
58
|
+
nodes.push(...netIps)
|
|
59
|
+
}
|
|
60
|
+
return await sshEnables(nodes);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function initRoomInfo() {
|
|
64
|
+
await fileLock(roomDataFile + "getNodes", async () => {
|
|
65
|
+
let nodes = await getTailScaleNodes();
|
|
66
|
+
await fileJson(roomDataFile, async room => {
|
|
67
|
+
// 设置roomName
|
|
68
|
+
let name = os.hostname();
|
|
69
|
+
if (!vl(room.name) || room.name !== name) {
|
|
70
|
+
room.name = name;
|
|
71
|
+
console.$log("set roomName", name)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
room.nodes = nodes;
|
|
75
|
+
room.lastUpdateTime = Date.now()
|
|
76
|
+
console.$log("init room", room.name)
|
|
77
|
+
})
|
|
78
|
+
}, false)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function _initRoom() {
|
|
82
|
+
let roomUpdateTouchTime = false;
|
|
83
|
+
await fileJson(roomDataFile, async room => {
|
|
84
|
+
await initRoomJsir(room)
|
|
85
|
+
if (!vl(room.lastUpdateTime) || (Date.now() - room.lastUpdateTime) > 9000) {
|
|
86
|
+
roomUpdateTouchTime = true;
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
if (roomUpdateTouchTime) {
|
|
91
|
+
initRoomInfo();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function getEventLoopDelay() {
|
|
96
|
+
const start = process.hrtime.bigint();
|
|
97
|
+
const delay = 1; // 用于测量的短暂延迟(单位:毫秒)
|
|
98
|
+
const timeoutPromise = new Promise(resolve => setTimeout(resolve, delay));
|
|
99
|
+
return await timeoutPromise.then(() => {
|
|
100
|
+
const end = process.hrtime.bigint();
|
|
101
|
+
return Number(Math.max(Number(end - start) / 1e4 - (delay * 1e2), 0).toFixed(0)); // 避免负值
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function initRoomJsir(room) {
|
|
106
|
+
room.jsir = room.jsir || {};
|
|
107
|
+
|
|
108
|
+
for (let pid of [...Object.keys(room.jsir)]) {
|
|
109
|
+
if (!isPidAlive(pid)) {
|
|
110
|
+
delete room.jsir[pid]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
room.jsir[process.pid] = {
|
|
114
|
+
pid: process.pid,
|
|
115
|
+
space: setting.defaultSpace,
|
|
116
|
+
tips: getKeyTips().reduce((obj, key, index) => {
|
|
117
|
+
obj[key] = getValTips()[index];
|
|
118
|
+
return obj;
|
|
119
|
+
}, {}),
|
|
120
|
+
busy: await getEventLoopDelay(),
|
|
121
|
+
back: isRunningInBackground(),
|
|
122
|
+
lastUpdateTime: Date.now()
|
|
123
|
+
}
|
|
124
|
+
console.$log("init jsir", process.pid)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isRunningInBackground() {
|
|
128
|
+
return !(process.stdout.isTTY && process.stdin.isTTY);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 获取本机所有内网 IP
|
|
133
|
+
* @returns {{ ipv4: string[], ipv6: string[] }}
|
|
134
|
+
*/
|
|
135
|
+
function getLocalIPs() {
|
|
136
|
+
const networkInterfaces = os.networkInterfaces();
|
|
137
|
+
|
|
138
|
+
const result = {
|
|
139
|
+
ipv4: [],
|
|
140
|
+
ipv6: []
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
for (const interfaceName of Object.keys(networkInterfaces)) {
|
|
144
|
+
for (const netInfo of networkInterfaces[interfaceName]) {
|
|
145
|
+
const { family, address, internal } = netInfo;
|
|
146
|
+
|
|
147
|
+
// 只关心非回环地址
|
|
148
|
+
if (!internal) {
|
|
149
|
+
if (family === 'IPv4') {
|
|
150
|
+
result.ipv4.push(address);
|
|
151
|
+
} else if (family === 'IPv6') {
|
|
152
|
+
result.ipv6.push(address);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function sshEnables(ips) {
|
|
162
|
+
// 检查开放 22 端口的 IP
|
|
163
|
+
const sshEnabledIPs = [];
|
|
164
|
+
const portCheckPromises = ips.map((ip) =>
|
|
165
|
+
checkPortOpen(ip, 22).then((isOpen) => {
|
|
166
|
+
if (isOpen) {
|
|
167
|
+
sshEnabledIPs.push(ip);
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
await Promise.all(portCheckPromises);
|
|
173
|
+
return sshEnabledIPs;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 检查指定 IP 的指定端口是否开放
|
|
178
|
+
* @param {string} ip - 目标 IP 地址
|
|
179
|
+
* @param {number} port - 目标端口
|
|
180
|
+
* @returns {Promise<boolean>} - 是否开放
|
|
181
|
+
*/
|
|
182
|
+
function checkPortOpen(ip, port) {
|
|
183
|
+
return new Promise((resolve) => {
|
|
184
|
+
const socket = new net.Socket();
|
|
185
|
+
socket.setTimeout(1000); // 设置超时时间
|
|
186
|
+
|
|
187
|
+
socket
|
|
188
|
+
.connect(port, ip, () => {
|
|
189
|
+
socket.destroy(); // 成功连接后关闭 socket
|
|
190
|
+
resolve(true);
|
|
191
|
+
})
|
|
192
|
+
.on("error", () => {
|
|
193
|
+
socket.destroy(); // 遇到错误时关闭 socket
|
|
194
|
+
resolve(false);
|
|
195
|
+
})
|
|
196
|
+
.on("timeout", () => {
|
|
197
|
+
socket.destroy(); // 超时时关闭 socket
|
|
198
|
+
resolve(false);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 扫描局域网中的可访问IP
|
|
205
|
+
* @param {string} localIP - 本机内网IP地址
|
|
206
|
+
* @returns {Promise<string[]>} - 可访问的IP地址列表
|
|
207
|
+
*/
|
|
208
|
+
async function scanLocalNetwork(localIP) {
|
|
209
|
+
const subnet = localIP.substring(0, localIP.lastIndexOf('.') + 1); // 例如 "192.168.1."
|
|
210
|
+
const reachableIPs = [];
|
|
211
|
+
|
|
212
|
+
// 扫描 1 到 254 的 IP
|
|
213
|
+
const promises = [];
|
|
214
|
+
for (let i = 1; i <= 254; i++) {
|
|
215
|
+
const ip = `${subnet}${i}`;
|
|
216
|
+
promises.push(
|
|
217
|
+
ping.promise.probe(ip, { timeout: 1 }).then((res) => {
|
|
218
|
+
if (res.alive) {
|
|
219
|
+
reachableIPs.push(ip);
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
await Promise.all(promises);
|
|
226
|
+
return reachableIPs;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
module.exports = {
|
|
230
|
+
onRoom,
|
|
231
|
+
offRoom
|
|
232
|
+
}
|
package/deps/setting.js
CHANGED
package/deps/util.js
CHANGED
|
@@ -758,7 +758,7 @@ async function fileExist(path) {
|
|
|
758
758
|
}
|
|
759
759
|
}
|
|
760
760
|
|
|
761
|
-
async function fileJson(key, fn, fmt = true) {
|
|
761
|
+
async function fileJson(key, fn, fmt = true, safeMs = 49000) {
|
|
762
762
|
`
|
|
763
763
|
多进程安全文件读写
|
|
764
764
|
`
|
|
@@ -789,7 +789,7 @@ async function fileJson(key, fn, fmt = true) {
|
|
|
789
789
|
result = val
|
|
790
790
|
}
|
|
791
791
|
await fp.writeFile(path, prefixStr + JSON.stringify(result, null, fmt ? 2:null))
|
|
792
|
-
});
|
|
792
|
+
}, true, safeMs);
|
|
793
793
|
return result;
|
|
794
794
|
}
|
|
795
795
|
|
|
@@ -1037,7 +1037,7 @@ function getLockDir() {
|
|
|
1037
1037
|
return lockDir;
|
|
1038
1038
|
}
|
|
1039
1039
|
|
|
1040
|
-
function
|
|
1040
|
+
function getLockKeyDir(key) {
|
|
1041
1041
|
if (!key) {
|
|
1042
1042
|
throw "invalid args"
|
|
1043
1043
|
}
|
|
@@ -1046,36 +1046,110 @@ function getLockFile(key) {
|
|
|
1046
1046
|
return lockDir + "/" + key;
|
|
1047
1047
|
}
|
|
1048
1048
|
|
|
1049
|
-
|
|
1049
|
+
/**
|
|
1050
|
+
* 从目录中读取过期时间戳
|
|
1051
|
+
* @param {string} lockDir - 锁目录路径
|
|
1052
|
+
* @returns {Promise<number | null>} 如果目录下存在过期时间戳文件,则返回其数值;否则返回 null
|
|
1053
|
+
*/
|
|
1054
|
+
async function readExpireTimestamp(lockDir) {
|
|
1055
|
+
try {
|
|
1056
|
+
const files = await fp.readdir(lockDir);
|
|
1057
|
+
// 假设我们只会创建一个文件,其文件名包含过期时间,如 `expire-1672531200000`
|
|
1058
|
+
const expireFile = files.find(name => name.startsWith('expire-'));
|
|
1059
|
+
if (!expireFile) {
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
const timestamp = parseInt(expireFile.replace('expire-', ''), 10);
|
|
1063
|
+
return isNaN(timestamp) ? null : timestamp;
|
|
1064
|
+
} catch (err) {
|
|
1065
|
+
// 读取目录出错(可能不存在?)直接返回 null
|
|
1066
|
+
return null;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
async function _fileLock(key, fn, expireMs = 49000) {
|
|
1050
1071
|
`
|
|
1051
1072
|
文件锁,返回true/false,
|
|
1052
1073
|
如果没锁住则不执行fn
|
|
1053
1074
|
`
|
|
1054
|
-
if (!key ||
|
|
1055
|
-
throw new Error('invalid
|
|
1075
|
+
if (!key || typeof fn !== 'function') {
|
|
1076
|
+
throw new Error('invalid arguments');
|
|
1056
1077
|
}
|
|
1057
|
-
|
|
1078
|
+
|
|
1079
|
+
let lockKeyDir = getLockKeyDir(key)
|
|
1080
|
+
const now = Date.now();
|
|
1081
|
+
const expireAt = expireMs > 0 ? now + expireMs : 0;
|
|
1082
|
+
|
|
1083
|
+
// 1. 尝试判断锁目录是否已存在
|
|
1084
|
+
let lockExists = false;
|
|
1058
1085
|
try {
|
|
1059
|
-
await fp.
|
|
1060
|
-
|
|
1086
|
+
await fp.access(lockKeyDir); // 若存在则不抛错
|
|
1087
|
+
lockExists = true;
|
|
1088
|
+
} catch (_) {
|
|
1089
|
+
// 不存在时会抛ENOENT错误,说明还没有锁
|
|
1090
|
+
lockExists = false;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
if (lockExists) {
|
|
1094
|
+
// 目录存在时,读取文件名中的过期时间戳
|
|
1095
|
+
const storedExpireAt = await readExpireTimestamp(lockKeyDir);
|
|
1096
|
+
if (storedExpireAt === null) {
|
|
1097
|
+
// 理论上不会出现没有“expire-xxxx”文件的情况,除非中途被手动改动
|
|
1098
|
+
// 可以选择强制删除目录,或者直接返回 false
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
if (storedExpireAt === 0) {
|
|
1102
|
+
// 说明当时设置的是永不过期
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
if (storedExpireAt > now) {
|
|
1106
|
+
// 说明锁尚未过期
|
|
1107
|
+
return false;
|
|
1108
|
+
}
|
|
1109
|
+
// 如果过期了,则清理原目录,以便重新加锁
|
|
1110
|
+
try {
|
|
1111
|
+
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
1112
|
+
} catch (err) {
|
|
1113
|
+
// 删除失败,可能是权限问题;这里直接返回 false 或者抛错
|
|
1114
|
+
return false;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// 2. 创建锁目录
|
|
1119
|
+
try {
|
|
1120
|
+
await fp.mkdir(lockKeyDir, { recursive: false });
|
|
1121
|
+
} catch (err) {
|
|
1122
|
+
// 如果 mkdir 依旧失败,说明在这段时间里又被别人抢先创建了
|
|
1061
1123
|
return false;
|
|
1062
1124
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1125
|
+
|
|
1126
|
+
// 3. 在锁目录下创建一个文件,文件名带“过期时间戳”
|
|
1127
|
+
// expireAt === 0 表示永不过期
|
|
1128
|
+
const expireFilename = `expire-${expireAt}`;
|
|
1129
|
+
const expireFilePath = path.join(lockKeyDir, expireFilename);
|
|
1130
|
+
try {
|
|
1131
|
+
await fp.writeFile(expireFilePath, '');
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
// 无法写文件就释放锁并抛错
|
|
1134
|
+
try {
|
|
1135
|
+
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
1136
|
+
} catch (_) {}
|
|
1137
|
+
return false;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// 4. 成功加锁后执行 fn
|
|
1065
1141
|
try {
|
|
1066
1142
|
await fn();
|
|
1067
1143
|
return true;
|
|
1068
|
-
} catch (e) {
|
|
1069
|
-
throw e;
|
|
1070
1144
|
} finally {
|
|
1145
|
+
// 5. 执行完毕后,手动释放锁目录(也可选择保留到过期自动失效,但一般都建议主动释放)
|
|
1071
1146
|
try {
|
|
1072
|
-
await fp.
|
|
1073
|
-
|
|
1074
|
-
} catch (_){}
|
|
1147
|
+
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
1148
|
+
} catch (_) {}
|
|
1075
1149
|
}
|
|
1076
1150
|
}
|
|
1077
1151
|
|
|
1078
|
-
async function fileLock(key, fn, wait = true) {
|
|
1152
|
+
async function fileLock(key, fn, wait = true, expireMs = 49000) {
|
|
1079
1153
|
`
|
|
1080
1154
|
文件锁, 默认一直等待,直到加锁成功执行fn
|
|
1081
1155
|
wait = false, 加锁失败则不执行fn
|
|
@@ -1083,13 +1157,13 @@ async function fileLock(key, fn, wait = true) {
|
|
|
1083
1157
|
`
|
|
1084
1158
|
if (wait) {
|
|
1085
1159
|
while (true) {
|
|
1086
|
-
if (await _fileLock(key, fn)) {
|
|
1160
|
+
if (await _fileLock(key, fn, expireMs)) {
|
|
1087
1161
|
break;
|
|
1088
1162
|
}
|
|
1089
|
-
await sleep(
|
|
1163
|
+
await sleep(9);
|
|
1090
1164
|
}
|
|
1091
1165
|
} else {
|
|
1092
|
-
await _fileLock(key, fn);
|
|
1166
|
+
await _fileLock(key, fn, expireMs);
|
|
1093
1167
|
}
|
|
1094
1168
|
}
|
|
1095
1169
|
|
|
@@ -1395,17 +1469,17 @@ async function eia(cmd, args = [], shell = false) {
|
|
|
1395
1469
|
`
|
|
1396
1470
|
当前进程不会卡住,输入输出由cmd进程持有
|
|
1397
1471
|
`
|
|
1398
|
-
setting.
|
|
1472
|
+
setting.enableNextLine = false;
|
|
1399
1473
|
let child = spawn(cmd, args, {stdio:"inherit", shell});
|
|
1400
1474
|
return new Promise((resolve, reject) => {
|
|
1401
1475
|
// 监听子进程的关闭事件
|
|
1402
1476
|
child.on('close', (code) => {
|
|
1403
|
-
setting.
|
|
1477
|
+
setting.enableNextLine = true;
|
|
1404
1478
|
resolve(code);
|
|
1405
1479
|
});
|
|
1406
1480
|
// 可选:监听子进程的错误事件
|
|
1407
1481
|
child.on('error', (err) => {
|
|
1408
|
-
setting.
|
|
1482
|
+
setting.enableNextLine = true;
|
|
1409
1483
|
reject(err)
|
|
1410
1484
|
});
|
|
1411
1485
|
})
|
|
@@ -1863,6 +1937,29 @@ end tell
|
|
|
1863
1937
|
}
|
|
1864
1938
|
}
|
|
1865
1939
|
|
|
1940
|
+
function getKeyTips() {
|
|
1941
|
+
let items = [];
|
|
1942
|
+
for (let key of Object.keys(setting.tips)) {
|
|
1943
|
+
items.push(trim(key))
|
|
1944
|
+
}
|
|
1945
|
+
return items
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
function getValTips() {
|
|
1949
|
+
let items = [];
|
|
1950
|
+
for (let key of Object.keys(setting.tips)) {
|
|
1951
|
+
let val = setting.tips[key].map(i => {
|
|
1952
|
+
let item = trim(i)
|
|
1953
|
+
if (item.indexOf(',') !== -1) {
|
|
1954
|
+
item = `[${item}]`;
|
|
1955
|
+
}
|
|
1956
|
+
return item;
|
|
1957
|
+
}).join("|");
|
|
1958
|
+
items.push(val)
|
|
1959
|
+
}
|
|
1960
|
+
return items
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1866
1963
|
module.exports = {
|
|
1867
1964
|
wrapperJsirText,
|
|
1868
1965
|
run,
|
|
@@ -1963,5 +2060,7 @@ module.exports = {
|
|
|
1963
2060
|
createDirs,
|
|
1964
2061
|
getTempDir,
|
|
1965
2062
|
terminalRun,
|
|
1966
|
-
eia
|
|
2063
|
+
eia,
|
|
2064
|
+
getKeyTips,
|
|
2065
|
+
getValTips
|
|
1967
2066
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsir",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "JavaScript Script Management Tool",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"chokidar": "^3.5.2",
|
|
23
23
|
"console.table": "^0.10.0",
|
|
24
24
|
"dayjs": "^1.10.4",
|
|
25
|
-
"pad": "^3.2.0"
|
|
25
|
+
"pad": "^3.2.0",
|
|
26
|
+
"ping": "^0.4.4"
|
|
26
27
|
}
|
|
27
28
|
}
|