@maiyunnet/kebab 9.7.3 → 9.8.0

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.7.3"` = `'9.7.3'`
1363
+ > `const` **VER**: `"9.8.0"` = `'9.8.0'`
1364
1364
 
1365
1365
  Defined in: [index.ts:10](https://github.com/maiyunnet/kebab/blob/master/index.ts#L10)
1366
1366
 
@@ -3258,7 +3258,7 @@ lib/core/functions/clone.md
3258
3258
 
3259
3259
  > **clone**\<`T`\>(`obj`): `T`
3260
3260
 
3261
- Defined in: [lib/core.ts:1167](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1167)
3261
+ Defined in: [lib/core.ts:1162](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1162)
3262
3262
 
3263
3263
  完整的克隆一份数组/对象
3264
3264
 
@@ -3322,7 +3322,7 @@ lib/core/functions/debug.md
3322
3322
 
3323
3323
  > **debug**(`message?`, ...`optionalParams`): `void`
3324
3324
 
3325
- Defined in: [lib/core.ts:1201](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1201)
3325
+ Defined in: [lib/core.ts:1196](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1196)
3326
3326
 
3327
3327
  打印调试信息,线上环境不会打印
3328
3328
 
@@ -3357,7 +3357,7 @@ lib/core/functions/display.md
3357
3357
 
3358
3358
  > **display**(`message?`, ...`optionalParams`): `void`
3359
3359
 
3360
- Defined in: [lib/core.ts:1214](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1214)
3360
+ Defined in: [lib/core.ts:1209](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1209)
3361
3361
 
3362
3362
  向控制台直接显示内容,一般情况下禁止使用
3363
3363
 
@@ -3460,7 +3460,7 @@ lib/core/functions/getLog.md
3460
3460
 
3461
3461
  # Function: getLog()
3462
3462
 
3463
- > **getLog**(`opt`): `Promise`\<`false` \| `any`[] \| `string`[][] \| `null`\>
3463
+ > **getLog**(`opt`): `Promise`\<`false` \| \{ `list`: `any`[] \| `string`[][]; `total`: `number`; \}\>
3464
3464
 
3465
3465
  Defined in: [lib/core.ts:1074](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1074)
3466
3466
 
@@ -3514,15 +3514,9 @@ Defined in: [lib/core.ts:1074](https://github.com/maiyunnet/kebab/blob/master/li
3514
3514
 
3515
3515
  仅显示被搜索到的行
3516
3516
 
3517
- #### start?
3518
-
3519
- `number`
3520
-
3521
- 跳过的字节数,默认不跳过
3522
-
3523
3517
  ## Returns
3524
3518
 
3525
- `Promise`\<`false` \| `any`[] \| `string`[][] \| `null`\>
3519
+ `Promise`\<`false` \| \{ `list`: `any`[] \| `string`[][]; `total`: `number`; \}\>
3526
3520
 
3527
3521
  lib/core/functions/ip.md
3528
3522
  ---
@@ -3603,7 +3597,7 @@ lib/core/functions/loadEnv.md
3603
3597
 
3604
3598
  > **loadEnv**(`dir`): `Promise`\<`void`\>
3605
3599
 
3606
- Defined in: [lib/core.ts:1266](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1266)
3600
+ Defined in: [lib/core.ts:1261](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1261)
3607
3601
 
3608
3602
  加载 .env 文件到 process.env,若文件不存在则跳过
3609
3603
 
@@ -3673,7 +3667,7 @@ lib/core/functions/ls.md
3673
3667
 
3674
3668
  > **ls**(`opt`): `Promise`\<`object`[]\>
3675
3669
 
3676
- Defined in: [lib/core.ts:1128](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1128)
3670
+ Defined in: [lib/core.ts:1123](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1123)
3677
3671
 
3678
3672
  获取目录内文件/文件夹列表
3679
3673
 
@@ -4034,7 +4028,7 @@ lib/core/functions/resolveEnvVars.md
4034
4028
 
4035
4029
  > **resolveEnvVars**(`obj`): `void`
4036
4030
 
4037
- Defined in: [lib/core.ts:1296](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1296)
4031
+ Defined in: [lib/core.ts:1291](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1291)
4038
4032
 
4039
4033
  将配置对象中的 ${ENV_VAR} 占位符替换为 process.env 的值
4040
4034
 
@@ -4476,7 +4470,7 @@ lib/core/functions/writeEventStreamHead.md
4476
4470
 
4477
4471
  > **writeEventStreamHead**(`res`): `void`
4478
4472
 
4479
- Defined in: [lib/core.ts:1236](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1236)
4473
+ Defined in: [lib/core.ts:1231](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1231)
4480
4474
 
4481
4475
  ## Parameters
4482
4476
 
@@ -4501,7 +4495,7 @@ lib/core/functions/writeHead.md
4501
4495
 
4502
4496
  > **writeHead**(`res`, `statusCode`, `headers?`): `void`
4503
4497
 
4504
- Defined in: [lib/core.ts:1225](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1225)
4498
+ Defined in: [lib/core.ts:1220](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1220)
4505
4499
 
4506
4500
  让 res 发送头部(前提是头部没有被发送才能调用本方法
4507
4501
 
@@ -4542,7 +4536,7 @@ lib/core/functions/write.md
4542
4536
 
4543
4537
  > **write**(`res`, `data`): `void`
4544
4538
 
4545
- Defined in: [lib/core.ts:1248](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1248)
4539
+ Defined in: [lib/core.ts:1243](https://github.com/maiyunnet/kebab/blob/master/lib/core.ts#L1243)
4546
4540
 
4547
4541
  向 res 发送数据
4548
4542
 
package/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * --- 本文件用来定义每个目录实体地址的常量 ---
6
6
  */
7
7
  /** --- 当前系统版本号 --- */
8
- export declare const VER = "9.7.3";
8
+ export declare const VER = "9.8.0";
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.7.3';
9
+ export const VER = '9.8.0';
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
@@ -284,15 +284,16 @@ export declare function getLog(opt: {
284
284
  'fend'?: string;
285
285
  /** --- 仅显示被搜索到的行 --- */
286
286
  'search'?: string;
287
- /** --- 跳过的字节数,默认不跳过 --- */
288
- 'start'?: number;
289
287
  /** --- 跳过条数 --- */
290
288
  'offset'?: number;
291
289
  /** --- 最大限制,默认 100 --- */
292
290
  'limit'?: number;
293
291
  /** --- 获取局域网服务器的日志,为空代表获取本机的 --- */
294
292
  'host'?: string;
295
- }): Promise<string[][] | kebab.Json[] | null | false>;
293
+ }): Promise<{
294
+ 'list': string[][] | kebab.Json[];
295
+ 'total': number;
296
+ } | false>;
296
297
  /**
297
298
  * --- 获取目录内文件/文件夹列表 ---
298
299
  * @param opt 参数
package/lib/core.js CHANGED
@@ -929,33 +929,27 @@ export async function getLog(opt) {
929
929
  opt.host ??= '127.0.0.1';
930
930
  // --- 局域网模式 ---
931
931
  const time = lTime.stamp();
932
- const res = await lUndici.get('http://' + opt.host + ':' + globalConfig.rpcPort.toString() + '/' + lCrypto.aesEncrypt(lText.stringifyJson({
932
+ const res = await lUndici.getResponseJson('http://' + opt.host + ':' + globalConfig.rpcPort.toString() + '/' + lCrypto.aesEncrypt(lText.stringifyJson({
933
933
  'action': 'log',
934
934
  'time': time,
935
935
  'hostname': opt.hostname,
936
936
  'path': opt.path,
937
937
  'fend': opt.fend,
938
938
  'search': opt.search,
939
- 'start': opt.start,
940
939
  'offset': opt.offset,
941
940
  'limit': opt.limit,
942
941
  }), globalConfig.rpcSecret), {
943
942
  'timeout': 2
944
943
  });
945
- const content = await res.getContent();
946
- if (!content) {
947
- // --- 连接失败,系统错误 ---
948
- debug('[CORE][getLog] rpc server error');
949
- return false;
950
- }
951
- const str = content.toString();
952
- const j = lText.parseJson(str);
953
- if (!j) {
944
+ if (!res) {
954
945
  // --- 解析失败,系统错误 ---
955
946
  debug('[CORE][getLog] rpc server content error');
956
947
  return false;
957
948
  }
958
- return j.data;
949
+ return {
950
+ 'list': res.list,
951
+ 'total': res.total,
952
+ };
959
953
  }
960
954
  /**
961
955
  * --- 获取目录内文件/文件夹列表 ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "9.7.3",
3
+ "version": "9.8.0",
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/master.js CHANGED
@@ -519,10 +519,52 @@ function createRpcListener() {
519
519
  }));
520
520
  return;
521
521
  }
522
+ // --- 用 wc -l 高效获取总行数 ---
523
+ let total = 0;
524
+ const wclRtn = await lCore.exec(`wc -l "${path}"`);
525
+ if (wclRtn !== false) {
526
+ const wclMatch = /^\s*(\d+)/.exec(wclRtn);
527
+ if (wclMatch) {
528
+ total = parseInt(wclMatch[1]);
529
+ if (format === 'csv') {
530
+ // --- csv 有表头,数据行数减 1 ---
531
+ total = Math.max(0, total - 1);
532
+ }
533
+ }
534
+ }
522
535
  /** --- 剩余 limit --- */
523
536
  let limit = msg.limit ?? 100;
524
- /** --- 剩余 offset --- */
525
- let offset = msg.offset ?? 0;
537
+ /** --- offset --- */
538
+ const offset = msg.offset ?? 0;
539
+ /**
540
+ * --- 用 grep -b '^' 获取目标行的字节偏移,直接传给 createReadStream start 跳过 offset ---
541
+ * --- jsonl 无表头:跳过 offset 行,从第 offset+1 行开始读 ---
542
+ * --- csv 有表头:跳过表头+offset 行,从第 offset+2 行开始读 ---
543
+ */
544
+ let startByte = 0;
545
+ /** --- offset 定位是否成功(offset=0 直接成功;offset>0 需 grep 定位)--- */
546
+ let offsetLocated = offset === 0;
547
+ if (offset > 0) {
548
+ const skipLines = format === 'jsonl' ? offset : offset + 1;
549
+ // --- sed -n '${N}{p;q}' 找到第 N 行后立即退出,使 grep 收到 SIGPIPE 提前终止 ---
550
+ const grepRtn = await lCore.exec(`grep -b '^' "${path}" | sed -n '${skipLines + 1}{p;q}'`);
551
+ if (grepRtn !== false) {
552
+ const grepMatch = /^(\d+):/.exec(grepRtn.trim());
553
+ if (grepMatch) {
554
+ startByte = parseInt(grepMatch[1]);
555
+ offsetLocated = true;
556
+ }
557
+ }
558
+ }
559
+ if (!offsetLocated) {
560
+ // --- offset 超出文件范围,返回空列表 ---
561
+ res.end(lText.stringifyJson({
562
+ 'result': 1,
563
+ 'data': [],
564
+ 'total': total,
565
+ }));
566
+ return;
567
+ }
526
568
  /**
527
569
  * --- csv 格式:string[][] 每行是字段数组,顺序与表头一致 ---
528
570
  * --- [['H:i:s', unix, url, cookie, session, userAgent, realIp, cfIp, xIp, osMem, procMem, message], ...] ---
@@ -531,14 +573,14 @@ function createRpcListener() {
531
573
  */
532
574
  const rtn = await new Promise(resolve => {
533
575
  const list = [];
534
- /** --- 当前行号(jsonl 无表头,csv 有表头,两者处理逻辑不同)--- */
576
+ /** --- 当前行号 --- */
535
577
  let line = 0;
536
578
  /** --- 当前行数据 --- */
537
579
  let packet = '';
538
580
  lFs.createReadStream(path, {
539
581
  'encoding': 'utf8',
540
- 'start': msg.start,
541
- }).on('data', (buf) => {
582
+ 'start': startByte,
583
+ }).on('data', buf => {
542
584
  if (typeof buf !== 'string') {
543
585
  return;
544
586
  }
@@ -557,50 +599,45 @@ function createRpcListener() {
557
599
  packet += buf.slice(0, index);
558
600
  buf = buf.slice(index + 1);
559
601
  ++line;
560
- // --- 先执行下本次完成的 ---
561
- // --- csv:line > 1 跳过表头行;jsonl:无表头,line >= 1 即可处理 ---
562
- if (format === 'jsonl' ? line >= 1 : line > 1) {
563
- if (offset === 0) {
564
- if (!msg.search || packet.includes(msg.search)) {
565
- if (format === 'jsonl') {
566
- const obj = lText.parseJson(packet);
567
- if (obj) {
568
- list.push(obj);
569
- --limit;
570
- }
602
+ // --- startByte > 0 时已跳过 offset(及 csv 表头),从第一行起均为数据行 ---
603
+ // --- startByte === 0 且 csv 时仍需跳过表头(line > 1)---
604
+ if (format === 'csv' && startByte === 0 ? line > 1 : line >= 1) {
605
+ if (!msg.search || packet.includes(msg.search)) {
606
+ if (format === 'jsonl') {
607
+ const obj = lText.parseJson(packet);
608
+ if (obj) {
609
+ list.push(obj);
610
+ --limit;
571
611
  }
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 = '';
612
+ }
613
+ else {
614
+ const result = [];
615
+ let currentField = '';
616
+ let inQuotes = false;
617
+ for (let i = 0; i < packet.length; ++i) {
618
+ const char = packet[i];
619
+ if (char === '"') {
620
+ if (inQuotes && packet[i + 1] === '"') {
621
+ currentField += '"';
622
+ ++i;
590
623
  }
591
624
  else {
592
- currentField += char;
625
+ inQuotes = !inQuotes;
593
626
  }
594
627
  }
595
- result.push(currentField);
596
- list.push(result);
597
- --limit;
628
+ else if (char === ',' && !inQuotes) {
629
+ result.push(currentField);
630
+ currentField = '';
631
+ }
632
+ else {
633
+ currentField += char;
634
+ }
598
635
  }
636
+ result.push(currentField);
637
+ list.push(result);
638
+ --limit;
599
639
  }
600
640
  }
601
- else {
602
- --offset;
603
- }
604
641
  }
605
642
  // --- 处理结束 ---
606
643
  packet = '';
@@ -619,7 +656,8 @@ function createRpcListener() {
619
656
  });
620
657
  res.end(lText.stringifyJson({
621
658
  'result': 1,
622
- 'data': rtn,
659
+ 'list': rtn,
660
+ 'total': total,
623
661
  }));
624
662
  return;
625
663
  }
@@ -1387,16 +1387,16 @@ for (let i = 0; i < 30000; ++i) {
1387
1387
  }
1388
1388
  async coreGetlog() {
1389
1389
  const path = lTime.format(null, 'Y/m/d/H');
1390
- const list = await lCore.getLog({
1390
+ const log = await lCore.getLog({
1391
1391
  'hostname': this._config.const.hostname,
1392
1392
  'path': path,
1393
1393
  'fend': '-visit',
1394
1394
  });
1395
1395
  const echo = [];
1396
- echo.push('<table style="width: 100%;">');
1397
- if (list) {
1396
+ echo.push(`total: ${log ? log.total : 0}<br><table style="width: 100%;">`);
1397
+ if (log) {
1398
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
- for (const row of list) {
1399
+ for (const row of log.list) {
1400
1400
  echo.push('<tr>');
1401
1401
  if (Array.isArray(row)) {
1402
1402
  // --- csv 格式:string[] ---
@@ -1418,7 +1418,7 @@ for (let i = 0; i < 30000; ++i) {
1418
1418
  }
1419
1419
  }
1420
1420
  else {
1421
- echo.push('<tr><th>' + JSON.stringify(list) + '</th></tr>');
1421
+ echo.push('<tr><th>' + JSON.stringify(log) + '</th></tr>');
1422
1422
  }
1423
1423
  echo.push('</table>');
1424
1424
  return echo.join('') + '<br>' + this._getEnd();