@maiyunnet/kebab 3.1.8 → 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 +3 -1
- package/index.js +1 -1
- package/lib/core.js +4 -1
- package/lib/lang.js +8 -8
- package/lib/net.js +6 -0
- package/lib/socket.d.ts +33 -0
- package/lib/socket.js +64 -0
- package/package.json +1 -1
- package/sys/ctr.d.ts +4 -0
- package/sys/ctr.js +6 -0
- package/sys/route.js +199 -159
- package/www/example/ctr/test.d.ts +1 -0
- package/www/example/ctr/test.js +4 -0
package/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* ------------------------
|
|
9
9
|
*/
|
|
10
10
|
/** --- 当前系统版本号 --- */
|
|
11
|
-
export declare const VER = "3.1.
|
|
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.
|
|
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 --- */
|
package/lib/core.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as cp from 'child_process';
|
|
7
7
|
import * as stream from 'stream';
|
|
8
|
+
import * as os from 'os';
|
|
8
9
|
import * as kebab from '#kebab/index.js';
|
|
9
10
|
import * as lTime from '#kebab/lib/time.js';
|
|
10
11
|
import * as lFs from '#kebab/lib/fs.js';
|
|
@@ -624,7 +625,7 @@ export function log(opt, msg, fend = '') {
|
|
|
624
625
|
}
|
|
625
626
|
path += h + '.csv';
|
|
626
627
|
if (!await lFs.isFile(path)) {
|
|
627
|
-
if (!await lFs.putContent(path, 'TIME,UNIX,URL,COOKIE,SESSION,JWT,USER_AGENT,REALIP,CLIENTIP,MESSAGE\n', {
|
|
628
|
+
if (!await lFs.putContent(path, 'TIME,UNIX,URL,COOKIE,SESSION,JWT,USER_AGENT,REALIP,CLIENTIP,OS,PROCESS,MESSAGE\n', {
|
|
628
629
|
'encoding': 'utf8',
|
|
629
630
|
'mode': 0o777
|
|
630
631
|
})) {
|
|
@@ -641,6 +642,8 @@ export function log(opt, msg, fend = '') {
|
|
|
641
642
|
(headers['user-agent']?.replace(/"/g, '""') ?? 'No HTTP_USER_AGENT') + '","' +
|
|
642
643
|
realIp.replace(/"/g, '""') + '","' +
|
|
643
644
|
clientIp.replace(/"/g, '""') + '","' +
|
|
645
|
+
lText.sizeFormat(os.totalmem() - os.freemem(), '') + '","' +
|
|
646
|
+
lText.sizeFormat(process.memoryUsage().rss, '') + '","' +
|
|
644
647
|
JSON.stringify(msg).slice(1, -1).replace(/"/g, '""') + '"\n', {
|
|
645
648
|
'encoding': 'utf8',
|
|
646
649
|
'mode': 0o777,
|
package/lib/lang.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** --- 支持的语言缩写列表 --- */
|
|
2
2
|
export const codes = [
|
|
3
|
-
'sc', 'tc', 'ja', 'ko', '
|
|
4
|
-
'
|
|
3
|
+
'sc', 'tc', 'ja', 'ko', 'th', 'vi', 'ar', 'id',
|
|
4
|
+
'en', 'es', 'de', 'fr', 'pt', 'ru', 'it', 'tr',
|
|
5
5
|
];
|
|
6
6
|
export const names = [
|
|
7
|
-
'简体中文', '繁體中文', '日本語', '한국어', '
|
|
8
|
-
'
|
|
7
|
+
'简体中文', '繁體中文', '日本語', '한국어', 'ไทย', 'Tiếng việt', 'العربية', 'Bahasa Indonesia',
|
|
8
|
+
'English', 'Español', 'Deutsch', 'Français', 'Português', 'Русский', 'Italiano', 'Türkçe',
|
|
9
9
|
];
|
|
10
10
|
/** --- 浏览器常用映射为本语言 --- */
|
|
11
11
|
export const map = {
|
|
@@ -13,16 +13,16 @@ export const map = {
|
|
|
13
13
|
'zh': 'tc',
|
|
14
14
|
'ja': 'ja',
|
|
15
15
|
'ko': 'ko',
|
|
16
|
-
'en': 'en',
|
|
17
|
-
'es': 'es',
|
|
18
16
|
'th': 'th',
|
|
19
17
|
'vi': 'vi',
|
|
18
|
+
'ar': 'ar',
|
|
19
|
+
'id': 'id',
|
|
20
|
+
'en': 'en',
|
|
21
|
+
'es': 'es',
|
|
20
22
|
'de': 'de',
|
|
21
23
|
'fr': 'fr',
|
|
22
24
|
'pt': 'pt',
|
|
23
25
|
'ru': 'ru',
|
|
24
|
-
'ar': 'ar',
|
|
25
|
-
'id': 'id',
|
|
26
26
|
'it': 'it',
|
|
27
27
|
'tr': 'tr',
|
|
28
28
|
};
|
package/lib/net.js
CHANGED
|
@@ -468,6 +468,9 @@ export async function mproxy(ctr, auth, opt = {}) {
|
|
|
468
468
|
const res = ctr.getPrototype('_res');
|
|
469
469
|
/** --- 客户端请求中的 get 的数据 --- */
|
|
470
470
|
const get = ctr.getPrototype('_get');
|
|
471
|
+
if (req.readableEnded) {
|
|
472
|
+
return -3;
|
|
473
|
+
}
|
|
471
474
|
if (get['auth'] !== auth) {
|
|
472
475
|
return 0;
|
|
473
476
|
}
|
|
@@ -519,6 +522,9 @@ export async function rproxy(ctr, route, opt = {}) {
|
|
|
519
522
|
const res = ctr.getPrototype('_res');
|
|
520
523
|
const config = ctr.getPrototype('_config');
|
|
521
524
|
const path = config.const.path + (config.const.qs ? '?' + config.const.qs : '');
|
|
525
|
+
if (req.readableEnded) {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
522
528
|
for (const key in route) {
|
|
523
529
|
if (!path.startsWith(key)) {
|
|
524
530
|
continue;
|
package/lib/socket.d.ts
ADDED
|
@@ -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
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
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
307
|
-
|
|
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
|
-
|
|
310
|
-
|
|
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
|
-
|
|
316
|
-
|
|
329
|
+
default: {
|
|
330
|
+
// --- nothing ---
|
|
317
331
|
}
|
|
318
|
-
break;
|
|
319
332
|
}
|
|
320
|
-
|
|
321
|
-
|
|
333
|
+
}).on('drain', async () => {
|
|
334
|
+
try {
|
|
335
|
+
await cctr['onDrain']();
|
|
322
336
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
data.
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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;
|