@maiyunnet/kebab 9.3.14 → 9.4.1

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/doc/kebab-rag.md CHANGED
@@ -1360,7 +1360,7 @@ index/variables/VER.md
1360
1360
 
1361
1361
  # Variable: VER
1362
1362
 
1363
- > `const` **VER**: `"9.3.14"` = `'9.3.14'`
1363
+ > `const` **VER**: `"9.4.1"` = `'9.4.1'`
1364
1364
 
1365
1365
  Defined in: [index.ts:10](https://github.com/maiyunnet/kebab/blob/master/index.ts#L10)
1366
1366
 
@@ -3460,7 +3460,7 @@ lib/core/functions/getLog.md
3460
3460
 
3461
3461
  # Function: getLog()
3462
3462
 
3463
- > **getLog**(`opt`): `Promise`\<`false` \| `string`[][] \| `null`\>
3463
+ > **getLog**(`opt`): `Promise`\<`false` \| `any`[] \| `string`[][] \| `null`\>
3464
3464
 
3465
3465
  Defined in: [lib/core.ts:1031](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1031)
3466
3466
 
@@ -3522,7 +3522,7 @@ Defined in: [lib/core.ts:1031](https://github.com/maiyunnet/kebab/blob/master/li
3522
3522
 
3523
3523
  ## Returns
3524
3524
 
3525
- `Promise`\<`false` \| `string`[][] \| `null`\>
3525
+ `Promise`\<`false` \| `any`[] \| `string`[][] \| `null`\>
3526
3526
 
3527
3527
  lib/core/functions/ip.md
3528
3528
  ---
package/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * --- 本文件用来定义每个目录实体地址的常量 ---
6
6
  */
7
7
  /** --- 当前系统版本号 --- */
8
- export declare const VER = "9.3.14";
8
+ export declare const VER = "9.4.1";
9
9
  /** --- 框架根目录,以 / 结尾 --- */
10
10
  export declare const ROOT_PATH: string;
11
11
  /** --- 框架的 LIB,以 / 结尾 --- */
package/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * --- 本文件用来定义每个目录实体地址的常量 ---
7
7
  */
8
8
  /** --- 当前系统版本号 --- */
9
- export const VER = '9.3.14';
9
+ export const VER = '9.4.1';
10
10
  // --- 服务端用的路径 ---
11
11
  const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
12
12
  /** --- /xxx/xxx --- */
package/lib/core.d.ts CHANGED
@@ -268,7 +268,7 @@ export declare function getLog(opt: {
268
268
  'limit'?: number;
269
269
  /** --- 获取局域网服务器的日志,为空代表获取本机的 --- */
270
270
  'host'?: string;
271
- }): Promise<string[][] | null | false>;
271
+ }): Promise<string[][] | kebab.Json[] | null | false>;
272
272
  /**
273
273
  * --- 获取目录内文件/文件夹列表 ---
274
274
  * @param opt 参数
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "9.3.14",
3
+ "version": "9.4.1",
4
4
  "description": "Simple, easy-to-use, and fully-featured Node.js framework that is ready-to-use out of the box.",
5
5
  "type": "module",
6
6
  "keywords": [
package/sys/child.js CHANGED
@@ -45,6 +45,8 @@ let httpServer;
45
45
  let http2Server;
46
46
  /** --- 当前使用中的连接 --- */
47
47
  const linkCount = {};
48
+ /** --- 是否正在停止,停止时对 HTTP/1.1 响应追加 Connection: close,避免请求完成后连接回到保活池 --- */
49
+ let stopping = false;
48
50
  /**
49
51
  * --- 包装请求处理函数,统一管理 linkCount 计数和错误处理 ---
50
52
  * @param key 连接标识
@@ -205,6 +207,10 @@ async function requestHandler(req, res, https) {
205
207
  res.setHeader('Server', 'Kebab/' + kebab.VER);
206
208
  res.setHeader('expires', 'Mon, 26 Jul 1994 05:00:00 GMT');
207
209
  res.setHeader('cache-control', 'no-store');
210
+ // --- 停止中:通知 HTTP/1.1 客户端不要复用此连接(HTTP/2 由 GOAWAY 帧处理) ---
211
+ if (stopping && res instanceof http.ServerResponse) {
212
+ res.setHeader('connection', 'close');
213
+ }
208
214
  // --- 当前 uri ---
209
215
  let host = req.headers[':authority'];
210
216
  if (host === undefined || typeof host !== 'string') {
@@ -551,26 +557,31 @@ process.on('message', function (msg) {
551
557
  }
552
558
  case 'stop': {
553
559
  // --- 需要停止监听,等待已有连接全部断开,然后关闭线程 ---
560
+ stopping = true;
554
561
  httpServer.close();
555
562
  http2Server.close();
563
+ // --- 立即关闭空闲保活连接(无活跃请求的 keep-alive socket),避免进程长时间等待 ---
564
+ httpServer.closeIdleConnections();
556
565
  clearInterval(hbTimer);
557
566
  sMonitor.stop();
558
- // --- 等待连接全部断开 ---
567
+ // --- 等待活跃请求全部完成 ---
559
568
  /** --- 当前已等待时间,等待不超过 1 小时 --- */
560
569
  let waiting = 0;
561
570
  while (true) {
562
571
  if (!Object.keys(linkCount).length) {
563
572
  break;
564
573
  }
565
- // --- 有长连接,等待中 ---
574
+ // --- 有活跃连接,等待中 ---
566
575
  const str = [];
567
576
  for (const key in linkCount) {
568
577
  str.push(key + ':' + linkCount[key].toString());
569
578
  }
570
579
  lCore.debug(`[CHILD] Worker ${process.pid} busy: ${str.join(',')}.`);
571
580
  lCore.log({}, `[CHILD] Worker ${process.pid} busy: ${str.join(',')}.`, '-warning');
572
- await lCore.sleep(30_000);
573
- waiting += 30_000;
581
+ await lCore.sleep(5_000);
582
+ waiting += 5_000;
583
+ // --- 再次清理已变为空闲的保活连接 ---
584
+ httpServer.closeIdleConnections();
574
585
  if (waiting > 3600_000) {
575
586
  break;
576
587
  }
package/sys/master.js CHANGED
@@ -523,9 +523,15 @@ function createRpcListener() {
523
523
  let limit = msg.limit ?? 100;
524
524
  /** --- 剩余 offset --- */
525
525
  let offset = msg.offset ?? 0;
526
+ /**
527
+ * --- csv 格式:string[][] 每行是字段数组,顺序与表头一致 ---
528
+ * --- [['H:i:s', unix, url, cookie, session, userAgent, realIp, cfIp, xIp, osMem, procMem, message], ...] ---
529
+ * --- jsonl 格式:object[] 每行是解析后的 JSON 对象 ---
530
+ * --- [{ time, unix, url, cookie, session, userAgent, realIp, cfIp, xIp, osMem, procMem, message }, ...] ---
531
+ */
526
532
  const rtn = await new Promise(resolve => {
527
533
  const list = [];
528
- /** --- 当前行号 --- */
534
+ /** --- 当前行号(jsonl 无表头,csv 有表头,两者处理逻辑不同)--- */
529
535
  let line = 0;
530
536
  /** --- 当前行数据 --- */
531
537
  let packet = '';
@@ -552,34 +558,44 @@ function createRpcListener() {
552
558
  buf = buf.slice(index + 1);
553
559
  ++line;
554
560
  // --- 先执行下本次完成的 ---
555
- if (line > 1) {
561
+ // --- csv:line > 1 跳过表头行;jsonl:无表头,line >= 1 即可处理 ---
562
+ if (format === 'jsonl' ? line >= 1 : line > 1) {
556
563
  if (offset === 0) {
557
564
  if (!msg.search || packet.includes(msg.search)) {
558
- const result = [];
559
- let currentField = '';
560
- let inQuotes = false;
561
- for (let i = 0; i < packet.length; ++i) {
562
- const char = packet[i];
563
- if (char === '"') {
564
- if (inQuotes && packet[i + 1] === '"') {
565
- currentField += '"';
566
- ++i;
565
+ if (format === 'jsonl') {
566
+ const obj = lText.parseJson(packet);
567
+ if (obj) {
568
+ list.push(obj);
569
+ --limit;
570
+ }
571
+ }
572
+ else {
573
+ const result = [];
574
+ let currentField = '';
575
+ let inQuotes = false;
576
+ for (let i = 0; i < packet.length; ++i) {
577
+ const char = packet[i];
578
+ if (char === '"') {
579
+ if (inQuotes && packet[i + 1] === '"') {
580
+ currentField += '"';
581
+ ++i;
582
+ }
583
+ else {
584
+ inQuotes = !inQuotes;
585
+ }
586
+ }
587
+ else if (char === ',' && !inQuotes) {
588
+ result.push(currentField);
589
+ currentField = '';
567
590
  }
568
591
  else {
569
- inQuotes = !inQuotes;
592
+ currentField += char;
570
593
  }
571
594
  }
572
- else if (char === ',' && !inQuotes) {
573
- result.push(currentField);
574
- currentField = '';
575
- }
576
- else {
577
- currentField += char;
578
- }
595
+ result.push(currentField);
596
+ list.push(result);
597
+ --limit;
579
598
  }
580
- result.push(currentField);
581
- list.push(result);
582
- --limit;
583
599
  }
584
600
  }
585
601
  else {
@@ -1395,11 +1395,21 @@ for (let i = 0; i < 30000; ++i) {
1395
1395
  const echo = [];
1396
1396
  echo.push('<table style="width: 100%;">');
1397
1397
  if (list) {
1398
- echo.push('<tr><th>TIME</th><th>UNIX</th><th>URL</th><th>COOKIE</th><th>SESSION</th><th>USER_AGENT</th><th>REALIP</th><th>CLIENTIP</th><th>OS</th><th>PROCESS</th><th>MESSAGE</th></tr>');
1398
+ echo.push('<tr><th>TIME</th><th>UNIX</th><th>URL</th><th>COOKIE</th><th>SESSION</th><th>USER_AGENT</th><th>REALIP</th><th>CFIP</th><th>XIP</th><th>OS</th><th>PROCESS</th><th>MESSAGE</th></tr>');
1399
1399
  for (const row of list) {
1400
1400
  echo.push('<tr>');
1401
- for (const item of row) {
1402
- echo.push('<td>' + lText.htmlescape(item) + '</td>');
1401
+ if (Array.isArray(row)) {
1402
+ // --- csv 格式:string[] ---
1403
+ for (const item of row) {
1404
+ echo.push('<td>' + lText.htmlescape(item) + '</td>');
1405
+ }
1406
+ }
1407
+ else {
1408
+ // --- jsonl 格式:object ---
1409
+ const r = row;
1410
+ for (const val of [r.time, r.unix, r.url, r.cookie, r.session, r.userAgent, r.realIp, r.cfIp, r.xIp, r.osMem, r.procMem, r.message]) {
1411
+ echo.push('<td>' + lText.htmlescape(typeof val === 'string' ? val : lText.stringifyJson(val) ?? '') + '</td>');
1412
+ }
1403
1413
  }
1404
1414
  echo.push('</tr>');
1405
1415
  }