jsir 2.5.0 → 2.5.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/cmd/oaa.js CHANGED
@@ -585,8 +585,7 @@ function wrapperAlias(str) {
585
585
  return str;
586
586
  }
587
587
  let fstr = str.split(/\s+/)[0];
588
- fstr = getAlias(aliasMap, fstr);
589
- return [fstr, str.substring(fstr.length)].map(trim).filter(i => i).join(' ')
588
+ return [getAlias(aliasMap, fstr), str.substring(fstr.length)].map(trim).filter(i => i).join(' ')
590
589
  }
591
590
 
592
591
  async function _wrapperInput(str) {
@@ -646,7 +645,7 @@ async function _wrapperInput(str) {
646
645
  if (strs[1] === '-') {
647
646
  await offServer(uniqueName)
648
647
  } else {
649
- await joinServer(uniqueName)
648
+ await joinServer(uniqueName, strs[1] === '+')
650
649
  }
651
650
  } else {
652
651
  console.log(String(fs.readFileSync(path)));
@@ -657,7 +656,7 @@ async function _wrapperInput(str) {
657
656
  }
658
657
  }
659
658
 
660
- async function joinServer(uniqueName) {
659
+ async function joinServer(uniqueName, isApi) {
661
660
  let exportLib = await _requireSource(setting.defaultSpace, uniqueName, true);
662
661
  let asyncFnSize = 0
663
662
  for (let key of Object.keys(exportLib)) {
@@ -676,12 +675,12 @@ async function joinServer(uniqueName) {
676
675
  let val = exportLib[key];
677
676
  Server.setRoute('get', `/${pair[0]}/${trimJsirFileName(pair[1])}/${key}`,async (req, res) => {
678
677
  res.result = await val(...req);
679
- })
678
+ }, isApi)
680
679
  Server.setRoute('post', `/${pair[0]}/${trimJsirFileName(pair[1])}/${key}`,async (req, res) => {
681
680
  res.result = await val(...req);
682
- })
681
+ }, isApi)
683
682
  }
684
- console.msg(uniqueName, 'has become a service.');
683
+ console.msg(uniqueName, `has become a ${isApi ? "api ":""}service.`);
685
684
  }
686
685
 
687
686
  async function offServer(uniqueName) {
@@ -1405,13 +1404,11 @@ const keywordDef = {
1405
1404
  }
1406
1405
  }
1407
1406
  let pair = parseUniqueName(keyword)
1408
- let cmds;
1409
- if (isMd5Key(pair[1]) || pair[1].indexOf(",") !== -1) {
1410
- cmds = getQuickRunCmds(keyword, keyword)
1411
- } else {
1412
- cmds = getQuickRunCmds(keyword, '^e ' + toJsirFileName(pair[1]) + '$')
1407
+ let cmds = getQuickRunCmds(keyword, pair[1])
1408
+ if (cmds.length > 1) {
1409
+ cmds = getQuickRunCmds(keyword, '^' + toJsirFileName(pair[1]) + '$')
1413
1410
  if (cmds.length === 0) {
1414
- cmds = getQuickRunCmds(keyword, keyword)
1411
+ cmds = getQuickRunCmds(keyword, '^e[^a-zA-Z]' + toJsirFileName(pair[1]) + '$')
1415
1412
  }
1416
1413
  }
1417
1414
  if (cmds.length === 1) {
@@ -1421,7 +1418,7 @@ const keywordDef = {
1421
1418
  await runScript(cmds[0], args.slice(1))
1422
1419
  }
1423
1420
  } else {
1424
- console.warn(cmds.length > 1 ? "multiple match" : "no match")
1421
+ console.warn(`require "${keyword}" not unique`)
1425
1422
  }
1426
1423
  },
1427
1424
  args: {
@@ -1635,17 +1632,17 @@ async function _dealKeyword(keyword, args) {
1635
1632
  if (item.short === shortKey) {
1636
1633
  unMatched = false;
1637
1634
  if (item === keywordDef.run) {
1638
- console.$log('execute', key)
1635
+ console.$log('Execute', key)
1639
1636
  await item.exeFn(args);
1640
1637
  } else {
1641
- console.$log('execute', key)
1638
+ console.$log('Execute', key)
1642
1639
  await item.exeFn(args.map(trim));
1643
1640
  }
1644
1641
  break;
1645
1642
  }
1646
1643
  }
1647
1644
  if (unMatched) {
1648
- console.$log('execute run', keyword)
1645
+ console.$log('Execute run', keyword)
1649
1646
  await keywordDef.run.exeFn([keyword, ...args])
1650
1647
  }
1651
1648
  }
@@ -2135,25 +2132,24 @@ function filterRequire(currSpace, cmdMatchStr) {
2135
2132
  }
2136
2133
  cmdMatchStr = trim(cmdMatchStr);
2137
2134
 
2138
- let cmds;
2139
- let uName = toUniqueName(cmdMatchStr, currSpace)
2140
- let fullPath = getFullPath(uName)
2141
- if (fullPath && fs.existsSync(fullPath)) {
2142
- cmds = [uName]
2143
- } else {
2144
- let appointSpace = parseUniqueName(cmdMatchStr)[0]
2145
- cmds = filterCmd(cmdMatchStr);
2146
- if (cmds.length > 0) {
2147
- cmds = cmds.filter(cmd => parseUniqueName(cmd)[0] === (appointSpace || currSpace))
2148
- }
2149
- if (cmds.length > 0) {
2150
- cmds = cmds.filter(cmd => getJsirTypeKey(parseUniqueName(cmd)[1]) === setting.initKey)
2151
- }
2135
+ let pair = parseUniqueName(cmdMatchStr);
2136
+ let appointSpace = pair[0] || currSpace;
2137
+ let cmds = filterCmd(appointSpace + '/' + pair[1]);
2138
+ if (cmds.length > 1) {
2139
+ cmds = cmds.filter(cmd => [setting.initKey, setting.exeKey].indexOf(getJsirTypeKey(parseUniqueName(cmd)[1])) !== -1)
2152
2140
  }
2153
2141
  if (cmds.length > 1) {
2154
- throw `multiple match: ${cmdMatchStr}`
2155
- } else if (cmds.length < 1) {
2156
- throw `none match: ${cmdMatchStr}`
2142
+ cmds = cmds.filter(cmd => setting.initKey === getJsirTypeKey(parseUniqueName(cmd)[1]))
2143
+ }
2144
+ if (cmds.length > 1) {
2145
+ let jsirFileName = toJsirFileName(pair[1])
2146
+ cmds = filterCmd(appointSpace + '/^' + jsirFileName + "$");
2147
+ if (cmds.length === 0) {
2148
+ cmds = filterCmd(appointSpace + '/^[ei][^a-zA-Z]' + jsirFileName + "$");
2149
+ }
2150
+ }
2151
+ if (cmds.length !== 1) {
2152
+ throw `require "${cmdMatchStr}" not unique`
2157
2153
  }
2158
2154
  return cmds[0];
2159
2155
  }
@@ -2217,6 +2213,9 @@ function addErrorTag(text) {
2217
2213
  }
2218
2214
 
2219
2215
  async function evalText($text = '', $cmdName = '', $args = []) {
2216
+ if ($cmdName) {
2217
+ console.$log(`Execute ${$cmdName}`);
2218
+ }
2220
2219
  let currSpace;
2221
2220
  if ($cmdName) {
2222
2221
  let pair = parseUniqueName($cmdName)
package/deps/room.js CHANGED
@@ -54,7 +54,6 @@ async function localConfigs() {
54
54
  function onRoom(wrapperInput) {
55
55
  if (!setting.roomTid[0]) {
56
56
  try {
57
- setRoute("post", "/", (req, res) => setting.selfRoom)
58
57
  setRoute("get", "/", (req, res) => setting.selfRoom)
59
58
  setRoute("post", "/enter", async (req, res) => {
60
59
  if (!getConfig("enableRemote", true)) {
@@ -71,6 +70,7 @@ function onRoom(wrapperInput) {
71
70
  let outputs = setting.enterOutputs || [];
72
71
  setting.enterOutputs = null;
73
72
  res.output = outputs.join('');
73
+ global.$newInput = true;
74
74
  }
75
75
  })
76
76
  if (configMain) {
@@ -113,44 +113,45 @@ function offRoom() {
113
113
  function getSelfIP(nodes) {
114
114
  let ips = getLocalIPs().ipv4;
115
115
  ips = ips.filter(i => nodes.indexOf(i) !== -1);
116
- return ips[0]
116
+ return ips[0] || '127.0.0.1'
117
117
  }
118
118
 
119
119
  async function getTailscaleNodes() {
120
120
  let resp = await e(`${tailscalePath} status`);
121
121
  let nodes = {}
122
122
  for (let line of resp.split("\n").map(trim)) {
123
- if (line.indexOf('offline') !== -1) {
124
- continue
125
- }
123
+ let offline = line.indexOf('offline') !== -1
126
124
  let ss = line.split(/\s+/);
127
125
  if (/^\d+\.\d+\.\d+\.\d+$/.test(ss[0])) {
128
- nodes[ss[0]] = ss[1]
126
+ nodes[ss[0]] = {
127
+ name: ss[1],
128
+ offline
129
+ }
129
130
  }
130
131
  }
131
132
  return nodes;
132
133
  }
133
134
 
134
135
  async function updateRoomInfo() {
135
- let nodeMap = await getTailscaleNodes();
136
- let nodes = Object.keys(nodeMap)
136
+ setting.nodeMap = await getTailscaleNodes();
137
+ let nodes = Object.keys(setting.nodeMap)
137
138
  let ip = getSelfIP(nodes)
138
139
  await fileJson(roomDataFile, async room => {
140
+ room = {
141
+ jsirs: room.jsirs
142
+ }
139
143
  // 设置roomName
140
- let name = nodeMap[ip] || os.hostname();
144
+ let name = setting.nodeMap[ip]?.name || os.hostname();
141
145
  if (!vl(room.name) || room.name !== name) {
142
146
  room.name = name;
143
147
  debug("set roomName", name)
144
148
  }
145
149
  room.selfNode = ip;
146
- room.nodes = nodes;
147
150
  room.lastUpdateTime = Date.now()
148
151
  debug("init room", room.name)
149
152
 
150
- // 提前设置一下
151
- setting.selfRoom = Object.assign(setting.selfRoom || {}, room);
152
-
153
153
  await cleanRoom(room)
154
+ return room;
154
155
  })
155
156
  }
156
157
 
@@ -160,10 +161,13 @@ async function syncRooms() {
160
161
 
161
162
  let syncRooms = []
162
163
  let fns = []
163
- for (let node of room.nodes) {
164
+ for (let node of Object.keys(setting.nodeMap)) {
164
165
  if (node === room.selfNode) {
165
166
  continue
166
167
  }
168
+ if (setting.nodeMap[node]?.offline) {
169
+ continue
170
+ }
167
171
  fns.push(async () => {
168
172
  let respBody = ''
169
173
  try {
@@ -195,9 +199,8 @@ async function syncRooms() {
195
199
 
196
200
  // 清理 roomsDir
197
201
  let files = await fp.readdir(roomsDir)
198
- let nodes = room.nodes;
199
202
  for (let file of files) {
200
- if (nodes.indexOf(file) === -1) {
203
+ if (!setting.nodeMap[file]) {
201
204
  try {
202
205
  await fp.unlink(roomsDir + '/' + file)
203
206
  } catch (e) {
@@ -218,7 +221,7 @@ async function _syncSetting(room) {
218
221
  let roomDir = getRoomsDir();
219
222
  let rooms = []
220
223
  let files = await fp.readdir(roomDir);
221
- for (let node of setting.selfRoom.nodes) {
224
+ for (let node of Object.keys(setting.nodeMap)) {
222
225
  if (setting.selfRoom.selfNode === node) {
223
226
  continue
224
227
  }
@@ -246,6 +249,9 @@ async function _syncSetting(room) {
246
249
  setting.services = {}
247
250
  setting.serviceFns = {}
248
251
  for (const room of setting.rooms) {
252
+ if (setting.nodeMap[room.selfNode]?.offline) {
253
+ continue
254
+ }
249
255
  for (const pid of Object.keys(room.jsirs || {})) {
250
256
  let jsir = room.jsirs[pid];
251
257
  for (const service of Object.keys(jsir.services || {})) {
@@ -280,7 +286,7 @@ async function initRoom() {
280
286
  if (roomUpdateTouchTime) {
281
287
  await fileLock(updateRoomInfoLockKey, updateRoomInfo, false)
282
288
  fileLock(syncRoomsLockKey, syncRooms, false);
283
- if (!configMain && setting.serviceFns.hasOwnProperty(setting.configMainFnKey)) {
289
+ if (!configMain && setting.serviceFns[setting.configMainFnKey]) {
284
290
  fileLock(syncConfigsLockKey, syncConfigs, false);
285
291
  }
286
292
  }
@@ -381,7 +387,7 @@ async function initRoomJsir(room) {
381
387
  busy: await getEventLoopDelay() || (lastJsir ? lastJsir.busy:0),
382
388
  back: isRunningInBackground(),
383
389
  lastUpdateTime: Date.now(),
384
- port: setting.server ? setting.server.address().port:null,
390
+ port: setting.server ? setting.server.address()?.port:null,
385
391
  services: services,
386
392
  newError: global.$newError,
387
393
  version: packageJson.version,
package/deps/server.js CHANGED
@@ -7,7 +7,6 @@ const preferredPort = setting.defaultPort;
7
7
  // 路由存储
8
8
  const routes = setting.routes;
9
9
  let invokeStart = false;
10
- const apiReg = /[^a-zA-Z]api[^a-zA-Z]|[^a-zA-Z]api$/i;
11
10
 
12
11
  function debug(...args) {
13
12
  if (global.$DEBUG) {
@@ -17,7 +16,7 @@ function debug(...args) {
17
16
 
18
17
  function verifySign(sign) {
19
18
  try {
20
- let key = md5([...setting.selfRoom.nodes].sort().join(":")).substring(0, 16);
19
+ let key = md5(Object.keys(setting.nodeMap).sort().join(":")).substring(0, 16);
21
20
  return parseInt(aesDecipher(sign, key), 10); // 假设sign是base64编码的毫秒数
22
21
  } catch (e) {
23
22
  return null; // 解码失败返回null
@@ -26,16 +25,23 @@ function verifySign(sign) {
26
25
 
27
26
  function createSign() {
28
27
  try {
29
- let key = md5([...setting.selfRoom.nodes].sort().join(":")).substring(0, 16);
28
+ let key = md5(Object.keys(setting.nodeMap).sort().join(":")).substring(0, 16);
30
29
  return aesCipher(String(Date.now()), key)
31
30
  } catch (e) {
32
31
  return null; // 解码失败返回null
33
32
  }
34
33
  }
35
34
 
36
- async function process(routeKey, req, res, params) {
37
- if (routes[routeKey]) {
38
- if (!apiReg.test(routeKey)) {
35
+ async function process(routeKey, req, res, params, clientIp) {
36
+ if (!clientIp.endsWith(":" + setting.selfRoom.selfNode)) {
37
+ res.writeHead(403, {'Content-Type': 'text/plain'});
38
+ res.end('Forbidden: Not Allowed');
39
+ return;
40
+ }
41
+
42
+ let router = routes[routeKey]
43
+ if (router) {
44
+ if ("get /" !== routeKey && !router.isApi) {
39
45
  // 如果不是api接口,则校验签名
40
46
  // 取header里的sign字段
41
47
  const sign = req.headers['sign'];
@@ -70,7 +76,7 @@ async function process(routeKey, req, res, params) {
70
76
  resolve()
71
77
  })
72
78
  })
73
- await routes[routeKey](body || params, res);
79
+ await router.fn(body || params, res);
74
80
  } catch (e) {
75
81
  res.writeHead(500, {'Content-Type': 'text/plain'});
76
82
  res.end(String(isError(e) ? e.message : e));
@@ -101,7 +107,7 @@ async function createServer(port) {
101
107
  const routeKey = `${method} ${url}`;
102
108
  let time = Date.now();
103
109
  try {
104
- await process(routeKey, req, res, params);
110
+ await process(routeKey, req, res, params, clientIp);
105
111
  } finally {
106
112
  debug(`Process cost ${Date.now() - time}ms for ${routeKey} from ${clientIp}`);
107
113
  }
@@ -140,16 +146,22 @@ async function startServer() {
140
146
  method, url, fn(req, res, err), null
141
147
  err: {code, msg}
142
148
  */
143
- function setRoute(method, url, fn, handler = jsonHandle) {
149
+ function setRoute(method, url, fn, handler = jsonHandle, isApi) {
144
150
  startServer();
145
151
 
146
152
  const routeKey = `${method.toLowerCase()} ${url}`;
147
153
  if (handler) {
148
- routes[routeKey] = async (req, res) => {
149
- await handler(req, res, fn)
154
+ routes[routeKey] = {
155
+ fn: async (req, res) => {
156
+ await handler(req, res, fn)
157
+ },
158
+ isApi
150
159
  };
151
160
  } else {
152
- routes[routeKey] = fn;
161
+ routes[routeKey] = {
162
+ fn,
163
+ isApi
164
+ };
153
165
  }
154
166
  debug(`setRoute: ${method.toUpperCase()} ${url} ${handler ? handler.name:''}`);
155
167
  return setting.server;
package/deps/setting.js CHANGED
@@ -22,6 +22,7 @@ module.exports = {
22
22
  workspaceMap: {},
23
23
  enableNextLine: true,
24
24
  roomTid: [],
25
+ nodeMap: {},
25
26
  selfRoom: {},
26
27
  rooms: [],
27
28
  server: null,
package/deps/util.js CHANGED
@@ -935,21 +935,25 @@ function validStr(str, name) {
935
935
  }
936
936
  }
937
937
 
938
- function aesCipher(str, key){
938
+ function aesCipher(str, key, useIv = false){
939
939
  validStr(str, "str");
940
940
  validStr(key, "key");
941
941
 
942
942
  key = trim(key);
943
943
  if (!key || key.length > 16) {
944
- throw "aesCipher key length should between 1 and 16"
944
+ throw "aesCipher key length should be between 1 and 16"
945
945
  }
946
- try {
947
- const cipher = crypto.createCipheriv('aes-128-ecb', pad(key, 16, '0'), null);
948
- return cipher.update(str, 'utf8', 'hex') + cipher.final('hex');
949
- } catch(err) {
950
- $log(err)
951
- throw 'aesCipher failed: ' + String(err)
946
+ if (useIv) {
947
+ // Generate random IV
948
+ const iv = crypto.randomBytes(16);
949
+ const cipher = crypto.createCipheriv('aes-128-cbc', pad(key, 16, '0'), iv);
950
+
951
+ // Return IV and ciphertext, as IV is needed for decryption
952
+ const ciphertext = cipher.update(str, 'utf8', 'hex') + cipher.final('hex');
953
+ return iv.toString('hex') + ':' + ciphertext; // Combine IV and ciphertext
952
954
  }
955
+ const cipher = crypto.createCipheriv('aes-128-ecb', pad(key, 16, '0'), null);
956
+ return cipher.update(str, 'utf8', 'hex') + cipher.final('hex');
953
957
  }
954
958
 
955
959
  function aesDecipher(str, key){
@@ -958,15 +962,21 @@ function aesDecipher(str, key){
958
962
 
959
963
  key = trim(key);
960
964
  if (!key || key.length > 16) {
961
- throw "aesCipher key length should between 1 and 16"
965
+ throw "aesCipher key length should be between 1 and 16";
962
966
  }
963
- try {
964
- const decipher = crypto.createDecipheriv('aes-128-ecb', pad(key, 16, '0'), null);
965
- return decipher.update(str, 'hex', 'utf8') + decipher.final('utf8');
966
- } catch(err) {
967
- $log(err)
968
- throw 'aesDecipher failed: ' + String(err)
967
+
968
+ // Extract IV and ciphertext
969
+ if(str.indexOf(":") !== -1) {
970
+ const [ivHex, ciphertext] = str.split(':');
971
+ if (!ivHex || !ciphertext) {
972
+ throw "Invalid encrypted string format";
973
+ }
974
+ const iv = Buffer.from(ivHex, 'hex');
975
+ const decipher = crypto.createDecipheriv('aes-128-cbc', pad(key, 16, '0'), iv);
976
+ return decipher.update(ciphertext, 'hex', 'utf8') + decipher.final('utf8');
969
977
  }
978
+ const decipher = crypto.createDecipheriv('aes-128-ecb', pad(key, 16, '0'), null);
979
+ return decipher.update(str, 'hex', 'utf8') + decipher.final('utf8');
970
980
  }
971
981
 
972
982
  function getConfig(key, defaultVal) {
@@ -2089,7 +2099,7 @@ function interceptStdStreams() {
2089
2099
  if(setting.enterOutputs) {
2090
2100
  setting.enterOutputs.push(chunk.toString());
2091
2101
  } else if (isRunningInBackground()) {
2092
- console.$log(process.pid, "stdout", "\n" + chunk.toString().trimEnd());
2102
+ console.$log(chunk.toString().trimEnd());
2093
2103
  } else {
2094
2104
  originalStdoutWrite(chunk, ...args); // 保留原始行为
2095
2105
  }
@@ -2100,7 +2110,7 @@ function interceptStdStreams() {
2100
2110
  if(setting.enterOutputs) {
2101
2111
  setting.enterOutputs.push(chunk.toString());
2102
2112
  } else if (isRunningInBackground()) {
2103
- console.$error(process.pid, errorStr("stderr"), "\n" + chunk.toString().trimEnd());
2113
+ console.$error(chunk.toString().trimEnd());
2104
2114
  } else {
2105
2115
  originalStderrWrite(chunk, ...args); // 保留原始行为
2106
2116
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsir",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "JavaScript Script Management Tool",
5
5
  "main": "index.js",
6
6
  "scripts": {