@maiyunnet/kebab 3.1.9 → 3.1.10

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/index.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * ------------------------
9
9
  */
10
10
  /** --- 当前系统版本号 --- */
11
- export declare const VER = "3.1.9";
11
+ export declare const VER = "3.1.10";
12
12
  /** --- 框架根目录,以 / 结尾 --- */
13
13
  export declare const ROOT_PATH: string;
14
14
  export declare const LIB_PATH: string;
@@ -133,7 +133,9 @@ export interface IConfigSql {
133
133
  }
134
134
  /** --- 常量 --- */
135
135
  export interface IConfigConst {
136
+ /** --- 不以 / 开头,不含 qs --- */
136
137
  'path': string;
138
+ /** --- 不含 ? 开头 --- */
137
139
  'qs': string;
138
140
  'startTime': bigint;
139
141
  'startMemory': number;
package/index.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * ------------------------
10
10
  */
11
11
  /** --- 当前系统版本号 --- */
12
- export const VER = '3.1.9';
12
+ export const VER = '3.1.10';
13
13
  // --- 服务端用的路径 ---
14
14
  const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
15
15
  /** --- /xxx/xxx --- */
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Project: Kebab, User: JianSuoQiYue
3
+ * Date: 2025-9-25 16:49:45
4
+ * Last: 2025-9-25 16:49:48
5
+ */
6
+ import net from 'net';
7
+ import * as lNet from '#kebab/lib/net.js';
8
+ import * as lWs from '#kebab/lib/ws.js';
9
+ export interface IRwebsocketOptions {
10
+ /** --- 秒数 --- */
11
+ 'timeout'?: number;
12
+ 'hosts'?: Record<string, string>;
13
+ 'local'?: string;
14
+ 'headers'?: lNet.THttpHeaders;
15
+ /** --- cookie 托管对象 --- */
16
+ 'cookie'?: Record<string, lNet.ICookie>;
17
+ /** --- 小帧模式,默认 false --- */
18
+ 'mode'?: lWs.EFrameReceiveMode;
19
+ /** --- 加密模式,默认 true --- */
20
+ 'masking'?: boolean;
21
+ /** --- 正向 mproxy 代理,url 如 wss://xxx/abc --- */
22
+ 'mproxy'?: {
23
+ 'url': string;
24
+ 'auth': string;
25
+ };
26
+ }
27
+ /**
28
+ * --- 创建一个 Socket 服务器并反代到 WebSocket ---
29
+ * @param port 监听端口
30
+ * @param url 反代到的 WebSocket
31
+ * @param opt 选项
32
+ */
33
+ export declare function rwebsocket(port: number, url: string, opt?: IRwebsocketOptions): net.Server;
package/lib/socket.js ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Project: Kebab, User: JianSuoQiYue
3
+ * Date: 2025-9-25 16:49:45
4
+ * Last: 2025-9-25 16:49:48
5
+ */
6
+ import net from 'net';
7
+ import * as lWs from '#kebab/lib/ws.js';
8
+ import * as lCore from '#kebab/lib/core.js';
9
+ /**
10
+ * --- 创建一个 Socket 服务器并反代到 WebSocket ---
11
+ * @param port 监听端口
12
+ * @param url 反代到的 WebSocket
13
+ * @param opt 选项
14
+ */
15
+ export function rwebsocket(port, url, opt = {}) {
16
+ /** --- 请求端产生的双向 socket --- */
17
+ const server = net.createServer(socket => {
18
+ (async () => {
19
+ // --- 每次进一个新连接都反代到一个新 WebSocket ---
20
+ lCore.display('New client: ' + socket.remoteAddress + ':' + socket.remotePort);
21
+ /** --- 远程端的双向 websocket --- */
22
+ const rws = await lWs.connect(url, opt);
23
+ if (!rws) {
24
+ return false;
25
+ }
26
+ rws.on('message', msg => {
27
+ switch (msg.opcode) {
28
+ case lWs.EOpcode.TEXT:
29
+ case lWs.EOpcode.BINARY: {
30
+ socket.write(msg.data);
31
+ break;
32
+ }
33
+ case lWs.EOpcode.CLOSE: {
34
+ socket.end();
35
+ break;
36
+ }
37
+ case lWs.EOpcode.PING: {
38
+ rws.pong();
39
+ break;
40
+ }
41
+ case lWs.EOpcode.PONG: {
42
+ break;
43
+ }
44
+ default: {
45
+ // --- EOpcode.CONTINUATION ---
46
+ }
47
+ }
48
+ }).on('close', () => {
49
+ socket.end();
50
+ });
51
+ socket.on('data', data => {
52
+ rws.writeBinary(data);
53
+ }).on('end', () => {
54
+ rws.end();
55
+ lCore.display('Client disconnected: ' + socket.remoteAddress + ':' + socket.remotePort);
56
+ }).on('error', err => {
57
+ lCore.display('Client error: ' + socket.remoteAddress + ':' + socket.remotePort + ', ' + err.message);
58
+ });
59
+ })().catch(() => { });
60
+ }).listen(port, () => {
61
+ lCore.display('Listening:' + port);
62
+ });
63
+ return server;
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "3.1.9",
3
+ "version": "3.1.10",
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/ctr.d.ts CHANGED
@@ -94,6 +94,10 @@ export declare class Ctr {
94
94
  * --- 实例化后会执行的方法,可重写此方法 ---
95
95
  */
96
96
  onLoad(): boolean | string | kebab.DbValue[] | Promise<boolean | string | kebab.DbValue[]>;
97
+ /**
98
+ * --- onLoad 执行后会执行的方法,可重写此方法 ---
99
+ */
100
+ onReady(): boolean | string | kebab.DbValue[] | Promise<boolean | string | kebab.DbValue[]>;
97
101
  /**
98
102
  * --- 整个结束前会执行本方法,可重写此方法对输出结果再处理一次(Websocket 模式无效) ---
99
103
  * @param rtn 之前用户的输出结果
package/sys/ctr.js CHANGED
@@ -132,6 +132,12 @@ export class Ctr {
132
132
  onLoad() {
133
133
  return true;
134
134
  }
135
+ /**
136
+ * --- onLoad 执行后会执行的方法,可重写此方法 ---
137
+ */
138
+ onReady() {
139
+ return true;
140
+ }
135
141
  /**
136
142
  * --- 整个结束前会执行本方法,可重写此方法对输出结果再处理一次(Websocket 模式无效) ---
137
143
  * @param rtn 之前用户的输出结果
package/sys/route.js CHANGED
@@ -261,86 +261,96 @@ export async function run(data) {
261
261
  return true;
262
262
  }
263
263
  if (rtn === undefined || rtn === true) {
264
- await new Promise(function (resolve) {
265
- wsSocket.on('message', async function (msg) {
266
- switch (msg.opcode) {
267
- case ws.EOpcode.CLOSE: {
268
- const r = await cctr['onMessage'](msg.data, msg.opcode);
269
- if (r === false) {
270
- break;
271
- }
272
- wsSocket.end();
273
- break;
274
- }
275
- case ws.EOpcode.PING: {
276
- const r = await cctr['onMessage'](msg.data, msg.opcode);
277
- if (r === false) {
264
+ try {
265
+ rtn = await cctr.onReady();
266
+ }
267
+ catch (e) {
268
+ lCore.log(cctr, lText.stringifyJson(e.stack).slice(1, -1), '-error');
269
+ data.socket.destroy();
270
+ return true;
271
+ }
272
+ if (rtn === undefined || rtn === true) {
273
+ await new Promise(function (resolve) {
274
+ wsSocket.on('message', async function (msg) {
275
+ switch (msg.opcode) {
276
+ case ws.EOpcode.CLOSE: {
277
+ const r = await cctr['onMessage'](msg.data, msg.opcode);
278
+ if (r === false) {
279
+ break;
280
+ }
281
+ wsSocket.end();
278
282
  break;
279
283
  }
280
- wsSocket.pong();
281
- break;
282
- }
283
- case ws.EOpcode.BINARY:
284
- case ws.EOpcode.TEXT: {
285
- try {
284
+ case ws.EOpcode.PING: {
286
285
  const r = await cctr['onMessage'](msg.data, msg.opcode);
287
286
  if (r === false) {
288
287
  break;
289
288
  }
290
- const wrtn = await cctr['onData'](msg.data, msg.opcode);
291
- if (wrtn === false) {
292
- wsSocket.end();
293
- return;
294
- }
295
- if (!wrtn) {
296
- return;
297
- }
298
- if (wrtn instanceof Buffer) {
299
- wsSocket.writeBinary(wrtn);
300
- return;
301
- }
302
- if (typeof wrtn === 'string') {
289
+ wsSocket.pong();
290
+ break;
291
+ }
292
+ case ws.EOpcode.BINARY:
293
+ case ws.EOpcode.TEXT: {
294
+ try {
295
+ const r = await cctr['onMessage'](msg.data, msg.opcode);
296
+ if (r === false) {
297
+ break;
298
+ }
299
+ const wrtn = await cctr['onData'](msg.data, msg.opcode);
300
+ if (wrtn === false) {
301
+ wsSocket.end();
302
+ return;
303
+ }
303
304
  if (!wrtn) {
304
305
  return;
305
306
  }
306
- wsSocket.writeText(wrtn);
307
- return;
307
+ if (wrtn instanceof Buffer) {
308
+ wsSocket.writeBinary(wrtn);
309
+ return;
310
+ }
311
+ if (typeof wrtn === 'string') {
312
+ if (!wrtn) {
313
+ return;
314
+ }
315
+ wsSocket.writeText(wrtn);
316
+ return;
317
+ }
318
+ if (typeof wrtn === 'object') {
319
+ // --- 返回的是数组,那么代表可能是 JSON,可能是对象序列 ---
320
+ wsSocket.writeResult(wrtn);
321
+ return;
322
+ }
308
323
  }
309
- if (typeof wrtn === 'object') {
310
- // --- 返回的是数组,那么代表可能是 JSON,可能是对象序列 ---
311
- wsSocket.writeResult(wrtn);
312
- return;
324
+ catch (e) {
325
+ lCore.log(cctr, lText.stringifyJson(e.stack).slice(1, -1), '-error');
313
326
  }
327
+ break;
314
328
  }
315
- catch (e) {
316
- lCore.log(cctr, lText.stringifyJson(e.stack).slice(1, -1), '-error');
329
+ default: {
330
+ // --- nothing ---
317
331
  }
318
- break;
319
332
  }
320
- default: {
321
- // --- nothing ---
333
+ }).on('drain', async () => {
334
+ try {
335
+ await cctr['onDrain']();
322
336
  }
323
- }
324
- }).on('drain', async () => {
325
- try {
326
- await cctr['onDrain']();
327
- }
328
- catch (e) {
329
- lCore.log(cctr, lText.stringifyJson(e.stack).slice(1, -1), '-error');
330
- }
331
- }).on('error', (e) => {
332
- lCore.log(cctr, lText.stringifyJson(e.stack).slice(1, -1), '-error');
333
- }).on('close', async () => {
334
- try {
335
- await cctr['onClose']();
336
- }
337
- catch (e) {
337
+ catch (e) {
338
+ lCore.log(cctr, lText.stringifyJson(e.stack).slice(1, -1), '-error');
339
+ }
340
+ }).on('error', (e) => {
338
341
  lCore.log(cctr, lText.stringifyJson(e.stack).slice(1, -1), '-error');
339
- }
340
- resolve();
342
+ }).on('close', async () => {
343
+ try {
344
+ await cctr['onClose']();
345
+ }
346
+ catch (e) {
347
+ lCore.log(cctr, lText.stringifyJson(e.stack).slice(1, -1), '-error');
348
+ }
349
+ resolve();
350
+ });
341
351
  });
342
- });
343
- return true;
352
+ return true;
353
+ }
344
354
  }
345
355
  if (!wsSocket.ended) {
346
356
  wsSocket.end();
@@ -393,112 +403,142 @@ export async function run(data) {
393
403
  let httpCode = middle.getPrototype('_httpCode');
394
404
  if (rtn === undefined || rtn === true) {
395
405
  // --- 只有不返回或返回 true 时才加载控制文件 ---
396
- // --- 判断真实控制器文件是否存在 ---
397
- const filePath = config.const.ctrPath + pathLeft + '.js';
398
- if (!await lFs.isFile(filePath)) {
399
- // --- 指定的控制器不存在 ---
400
- const text = '[Error] Controller not found, path: ' + path + '.';
401
- if (config.route['#404']) {
402
- data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
403
- data.res.writeHead(302);
404
- data.res.end('');
405
- return true;
406
- }
407
- data.res.setHeader('content-type', 'text/html; charset=utf-8');
408
- data.res.setHeader('content-length', Buffer.byteLength(text));
409
- data.res.writeHead(404);
410
- data.res.end(text);
411
- return true;
412
- }
413
- // --- 加载控制器文件 ---
414
- const ctrCtr = (await import((!filePath.startsWith('/') ? '/' : '') + filePath)).default;
415
- cctr = new ctrCtr(config, data.req, data.res ?? data.socket);
416
- // --- 对信息进行初始化 ---
417
- cctr.setPrototype('_timer', middle.getPrototype('_timer'));
418
- cctr.setPrototype('_waitInfo', middle.getPrototype('_waitInfo'));
419
- // --- 路由定义的参数序列 ---
420
- cctr.setPrototype('_param', param);
421
- cctr.setPrototype('_action', middle.getPrototype('_action'));
422
- cctr.setPrototype('_headers', headers);
423
- cctr.setPrototype('_get', get);
424
- cctr.setPrototype('_rawPost', middle.getPrototype('_rawPost'));
425
- cctr.setPrototype('_input', middle.getPrototype('_input'));
426
- cctr.setPrototype('_files', middle.getPrototype('_files'));
427
- cctr.setPrototype('_post', middle.getPrototype('_post'));
428
- cctr.setPrototype('_cookie', cookies);
429
- cctr.setPrototype('_jwt', middle.getPrototype('_jwt'));
430
- if (!cctr.getPrototype('_sess') && middle.getPrototype('_sess')) {
431
- cctr.setPrototype('_session', middle.getPrototype('_session'));
432
- cctr.setPrototype('_sess', middle.getPrototype('_sess'));
406
+ try {
407
+ rtn = await middle.onReady();
433
408
  }
434
- cctr.setPrototype('_cacheTTL', middle.getPrototype('_cacheTTL'));
435
- cctr.setPrototype('_xsrf', middle.getPrototype('_xsrf'));
436
- cctr.setPrototype('_httpCode', middle.getPrototype('_httpCode'));
437
- lCore.log(cctr, '', '-visit');
438
- // --- 强制 HTTPS ---
439
- if (config.set.mustHttps && !config.const.https) {
440
- data.res.setHeader('location', data.req.url ?? '');
441
- data.res.writeHead(302);
409
+ catch (e) {
410
+ lCore.log(middle, '(E05)' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
411
+ data.res.setHeader('content-type', 'text/html; charset=utf-8');
412
+ data.res.setHeader('content-length', 25);
413
+ data.res.writeHead(500);
414
+ data.res.end('<h1>500 Server Error</h1><hr>Kebab');
442
415
  return true;
443
416
  }
444
- // --- 检测 action 是否存在,以及排除内部方法 ---
445
- if (pathRight.startsWith('_') || pathRight === 'onUpgrade' || pathRight === 'onLoad' || pathRight === 'onData' || pathRight === 'onDrain' || pathRight === 'onClose' || pathRight === 'setPrototype' || pathRight === 'getPrototype' || pathRight === 'getAuthorization') {
446
- // --- _ 开头的 action 是内部方法,不允许访问 ---
447
- if (config.route['#404']) {
448
- data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
449
- data.res.writeHead(302);
450
- data.res.end('');
417
+ cacheTTL = middle.getPrototype('_cacheTTL');
418
+ httpCode = middle.getPrototype('_httpCode');
419
+ if (rtn === undefined || rtn === true) {
420
+ // --- 判断真实控制器文件是否存在 ---
421
+ const filePath = config.const.ctrPath + pathLeft + '.js';
422
+ if (!await lFs.isFile(filePath)) {
423
+ // --- 指定的控制器不存在 ---
424
+ const text = '[Error] Controller not found, path: ' + path + '.';
425
+ if (config.route['#404']) {
426
+ data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
427
+ data.res.writeHead(302);
428
+ data.res.end('');
429
+ return true;
430
+ }
431
+ data.res.setHeader('content-type', 'text/html; charset=utf-8');
432
+ data.res.setHeader('content-length', Buffer.byteLength(text));
433
+ data.res.writeHead(404);
434
+ data.res.end(text);
451
435
  return true;
452
436
  }
453
- const text = '[Error] Action not found, path: ' + path + '.';
454
- data.res.setHeader('content-type', 'text/html; charset=utf-8');
455
- data.res.setHeader('content-length', Buffer.byteLength(text));
456
- data.res.writeHead(404);
457
- data.res.end(text);
458
- return true;
459
- }
460
- pathRight = pathRight.replace(/-([a-zA-Z0-9])/g, function (t, t1) {
461
- return t1.toUpperCase();
462
- });
463
- if (cctr[pathRight] === undefined) {
464
- if (config.route['#404']) {
465
- data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
437
+ // --- 加载控制器文件 ---
438
+ const ctrCtr = (await import((!filePath.startsWith('/') ? '/' : '') + filePath)).default;
439
+ cctr = new ctrCtr(config, data.req, data.res ?? data.socket);
440
+ // --- 对信息进行初始化 ---
441
+ cctr.setPrototype('_timer', middle.getPrototype('_timer'));
442
+ cctr.setPrototype('_waitInfo', middle.getPrototype('_waitInfo'));
443
+ // --- 路由定义的参数序列 ---
444
+ cctr.setPrototype('_param', param);
445
+ cctr.setPrototype('_action', middle.getPrototype('_action'));
446
+ cctr.setPrototype('_headers', headers);
447
+ cctr.setPrototype('_get', get);
448
+ cctr.setPrototype('_rawPost', middle.getPrototype('_rawPost'));
449
+ cctr.setPrototype('_input', middle.getPrototype('_input'));
450
+ cctr.setPrototype('_files', middle.getPrototype('_files'));
451
+ cctr.setPrototype('_post', middle.getPrototype('_post'));
452
+ cctr.setPrototype('_cookie', cookies);
453
+ cctr.setPrototype('_jwt', middle.getPrototype('_jwt'));
454
+ if (!cctr.getPrototype('_sess') && middle.getPrototype('_sess')) {
455
+ cctr.setPrototype('_session', middle.getPrototype('_session'));
456
+ cctr.setPrototype('_sess', middle.getPrototype('_sess'));
457
+ }
458
+ cctr.setPrototype('_cacheTTL', middle.getPrototype('_cacheTTL'));
459
+ cctr.setPrototype('_xsrf', middle.getPrototype('_xsrf'));
460
+ cctr.setPrototype('_httpCode', middle.getPrototype('_httpCode'));
461
+ lCore.log(cctr, '', '-visit');
462
+ // --- 强制 HTTPS ---
463
+ if (config.set.mustHttps && !config.const.https) {
464
+ data.res.setHeader('location', data.req.url ?? '');
466
465
  data.res.writeHead(302);
467
- data.res.end('');
468
466
  return true;
469
467
  }
470
- const text = '[Error] Action not found, path: ' + path + '.';
471
- data.res.setHeader('content-type', 'text/html; charset=utf-8');
472
- data.res.setHeader('content-length', Buffer.byteLength(text));
473
- data.res.writeHead(404);
474
- data.res.end(text);
475
- return true;
476
- }
477
- // --- 执行 onLoad 方法 ---
478
- try {
479
- rtn = await cctr.onLoad();
480
- // --- 执行 action ---
481
- if (rtn === undefined || rtn === true) {
482
- rtn = await cctr[pathRight]();
483
- rtn = await cctr.onUnload(rtn);
484
- rtn = await middle.onUnload(rtn);
485
- const sess = cctr.getPrototype('_sess');
486
- if (sess) {
487
- await sess.update();
468
+ // --- 检测 action 是否存在,以及排除内部方法 ---
469
+ if (pathRight.startsWith('_') || pathRight === 'onUpgrade' || pathRight === 'onLoad' || pathRight === 'onData' || pathRight === 'onDrain' || pathRight === 'onClose' || pathRight === 'setPrototype' || pathRight === 'getPrototype' || pathRight === 'getAuthorization') {
470
+ // --- _ 开头的 action 是内部方法,不允许访问 ---
471
+ if (config.route['#404']) {
472
+ data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
473
+ data.res.writeHead(302);
474
+ data.res.end('');
475
+ return true;
488
476
  }
477
+ const text = '[Error] Action not found, path: ' + path + '.';
478
+ data.res.setHeader('content-type', 'text/html; charset=utf-8');
479
+ data.res.setHeader('content-length', Buffer.byteLength(text));
480
+ data.res.writeHead(404);
481
+ data.res.end(text);
482
+ return true;
483
+ }
484
+ pathRight = pathRight.replace(/-([a-zA-Z0-9])/g, function (t, t1) {
485
+ return t1.toUpperCase();
486
+ });
487
+ if (cctr[pathRight] === undefined) {
488
+ if (config.route['#404']) {
489
+ data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
490
+ data.res.writeHead(302);
491
+ data.res.end('');
492
+ return true;
493
+ }
494
+ const text = '[Error] Action not found, path: ' + path + '.';
495
+ data.res.setHeader('content-type', 'text/html; charset=utf-8');
496
+ data.res.setHeader('content-length', Buffer.byteLength(text));
497
+ data.res.writeHead(404);
498
+ data.res.end(text);
499
+ return true;
500
+ }
501
+ // --- 执行 onLoad 方法 ---
502
+ try {
503
+ rtn = await cctr.onLoad();
504
+ // --- 执行 action ---
505
+ if (rtn === undefined || rtn === true) {
506
+ try {
507
+ rtn = await cctr.onReady();
508
+ // --- 执行 action ---
509
+ if (rtn === undefined || rtn === true) {
510
+ rtn = await cctr[pathRight]();
511
+ rtn = await cctr.onUnload(rtn);
512
+ rtn = await middle.onUnload(rtn);
513
+ const sess = cctr.getPrototype('_sess');
514
+ if (sess) {
515
+ await sess.update();
516
+ }
517
+ }
518
+ }
519
+ catch (e) {
520
+ lCore.log(cctr, '(E05)' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
521
+ data.res.setHeader('content-type', 'text/html; charset=utf-8');
522
+ data.res.setHeader('content-length', 25);
523
+ data.res.writeHead(500);
524
+ data.res.end('<h1>500 Server Error</h1><hr>Kebab');
525
+ await waitCtr(cctr);
526
+ return true;
527
+ }
528
+ }
529
+ // --- 获取 ctr 设置的 cache 和 hcode ---
530
+ cacheTTL = cctr.getPrototype('_cacheTTL');
531
+ httpCode = cctr.getPrototype('_httpCode');
532
+ }
533
+ catch (e) {
534
+ lCore.log(cctr, '(E04)' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
535
+ data.res.setHeader('content-type', 'text/html; charset=utf-8');
536
+ data.res.setHeader('content-length', 25);
537
+ data.res.writeHead(500);
538
+ data.res.end('<h1>500 Server Error</h1><hr>Kebab');
539
+ await waitCtr(cctr);
540
+ return true;
489
541
  }
490
- // --- 获取 ctr 设置的 cache 和 hcode ---
491
- cacheTTL = cctr.getPrototype('_cacheTTL');
492
- httpCode = cctr.getPrototype('_httpCode');
493
- }
494
- catch (e) {
495
- lCore.log(cctr, '(E04)' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
496
- data.res.setHeader('content-type', 'text/html; charset=utf-8');
497
- data.res.setHeader('content-length', 25);
498
- data.res.writeHead(500);
499
- data.res.end('<h1>500 Server Error</h1><hr>Kebab');
500
- await waitCtr(cctr);
501
- return true;
502
542
  }
503
543
  }
504
544
  // --- 设置缓存 ---
@@ -4,6 +4,7 @@ import * as sCtr from '#kebab/sys/ctr.js';
4
4
  export default class extends sCtr.Ctr {
5
5
  private _internalUrl;
6
6
  onLoad(): Array<string | number> | boolean;
7
+ onReady(): boolean;
7
8
  onUnload(rtn: string | boolean | kebab.DbValue[]): string | boolean | kebab.DbValue[];
8
9
  notfound(): string;
9
10
  index(): string;
@@ -45,6 +45,10 @@ export default class extends sCtr.Ctr {
45
45
  }
46
46
  return true;
47
47
  }
48
+ onReady() {
49
+ lCore.display('onReady');
50
+ return true;
51
+ }
48
52
  onUnload(rtn) {
49
53
  if (!Array.isArray(rtn)) {
50
54
  return rtn;