@maiyunnet/kebab 9.7.2 → 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 +12 -18
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/core.d.ts +4 -3
- package/lib/core.js +6 -12
- package/package.json +1 -1
- package/sys/child.js +4 -4
- package/sys/master.js +80 -42
- package/www/example/ctr/test.js +5 -5
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.
|
|
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:
|
|
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:
|
|
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:
|
|
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`[][]
|
|
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`[][]
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* --- 本文件用来定义每个目录实体地址的常量 ---
|
|
7
7
|
*/
|
|
8
8
|
/** --- 当前系统版本号 --- */
|
|
9
|
-
export const VER = '9.
|
|
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<
|
|
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.
|
|
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
|
-
|
|
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
|
|
949
|
+
return {
|
|
950
|
+
'list': res.list,
|
|
951
|
+
'total': res.total,
|
|
952
|
+
};
|
|
959
953
|
}
|
|
960
954
|
/**
|
|
961
955
|
* --- 获取目录内文件/文件夹列表 ---
|
package/package.json
CHANGED
package/sys/child.js
CHANGED
|
@@ -558,10 +558,10 @@ process.on('message', function (msg) {
|
|
|
558
558
|
case 'stop': {
|
|
559
559
|
// --- 需要停止监听,等待已有连接全部断开,然后关闭线程 ---
|
|
560
560
|
stopping = true;
|
|
561
|
-
httpServer
|
|
562
|
-
http2Server
|
|
561
|
+
httpServer?.close();
|
|
562
|
+
http2Server?.close();
|
|
563
563
|
// --- 立即关闭空闲保活连接(无活跃请求的 keep-alive socket),避免进程长时间等待 ---
|
|
564
|
-
httpServer
|
|
564
|
+
httpServer?.closeIdleConnections();
|
|
565
565
|
clearInterval(hbTimer);
|
|
566
566
|
sMonitor.stop();
|
|
567
567
|
// --- 等待活跃请求全部完成 ---
|
|
@@ -581,7 +581,7 @@ process.on('message', function (msg) {
|
|
|
581
581
|
await lCore.sleep(5_000);
|
|
582
582
|
waiting += 5_000;
|
|
583
583
|
// --- 再次清理已变为空闲的保活连接 ---
|
|
584
|
-
httpServer
|
|
584
|
+
httpServer?.closeIdleConnections();
|
|
585
585
|
if (waiting > 3600_000) {
|
|
586
586
|
break;
|
|
587
587
|
}
|
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
|
-
/** ---
|
|
525
|
-
|
|
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
|
-
/** ---
|
|
576
|
+
/** --- 当前行号 --- */
|
|
535
577
|
let line = 0;
|
|
536
578
|
/** --- 当前行数据 --- */
|
|
537
579
|
let packet = '';
|
|
538
580
|
lFs.createReadStream(path, {
|
|
539
581
|
'encoding': 'utf8',
|
|
540
|
-
'start':
|
|
541
|
-
}).on('data',
|
|
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
|
-
// ---
|
|
562
|
-
if (format === '
|
|
563
|
-
if (
|
|
564
|
-
if (
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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
|
-
|
|
625
|
+
inQuotes = !inQuotes;
|
|
593
626
|
}
|
|
594
627
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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
|
-
'
|
|
659
|
+
'list': rtn,
|
|
660
|
+
'total': total,
|
|
623
661
|
}));
|
|
624
662
|
return;
|
|
625
663
|
}
|
package/www/example/ctr/test.js
CHANGED
|
@@ -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
|
|
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(
|
|
1397
|
-
if (
|
|
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(
|
|
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();
|