@maiyunnet/kebab 2.0.6 → 2.0.8

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.
Files changed (83) hide show
  1. package/index.d.ts +11 -1
  2. package/index.js +13 -1
  3. package/lib/buffer.d.ts +25 -0
  4. package/lib/buffer.js +30 -5
  5. package/lib/captcha.d.ts +15 -0
  6. package/lib/captcha.js +20 -0
  7. package/lib/consistent.d.ts +51 -0
  8. package/lib/consistent.js +59 -0
  9. package/lib/core.d.ts +134 -0
  10. package/lib/core.js +176 -0
  11. package/lib/crypto.d.ts +75 -6
  12. package/lib/crypto.js +206 -38
  13. package/lib/db.d.ts +104 -0
  14. package/lib/db.js +126 -0
  15. package/lib/dns.d.ts +51 -0
  16. package/lib/dns.js +54 -2
  17. package/lib/fs.d.ts +100 -0
  18. package/lib/fs.js +118 -0
  19. package/lib/jwt.d.ts +43 -0
  20. package/lib/jwt.js +45 -0
  21. package/lib/kv.d.ts +362 -0
  22. package/lib/kv.js +377 -0
  23. package/lib/lan.d.ts +6 -0
  24. package/lib/lan.js +7 -0
  25. package/lib/net/formdata.d.ts +38 -0
  26. package/lib/net/formdata.js +43 -0
  27. package/lib/net/request.d.ts +62 -0
  28. package/lib/net/request.js +57 -0
  29. package/lib/net/response.d.ts +21 -0
  30. package/lib/net/response.js +16 -0
  31. package/lib/net.d.ts +86 -0
  32. package/lib/net.js +140 -0
  33. package/lib/s3.d.ts +52 -0
  34. package/lib/s3.js +51 -0
  35. package/lib/scan.d.ts +52 -0
  36. package/lib/scan.js +84 -0
  37. package/lib/session.d.ts +31 -0
  38. package/lib/session.js +52 -1
  39. package/lib/sql.d.ts +176 -0
  40. package/lib/sql.js +287 -2
  41. package/lib/ssh/sftp.d.ts +106 -0
  42. package/lib/ssh/sftp.js +106 -0
  43. package/lib/ssh/shell.d.ts +37 -0
  44. package/lib/ssh/shell.js +31 -0
  45. package/lib/ssh.d.ts +32 -0
  46. package/lib/ssh.js +32 -0
  47. package/lib/text.d.ts +131 -0
  48. package/lib/text.js +188 -0
  49. package/lib/time.d.ts +53 -0
  50. package/lib/time.js +55 -0
  51. package/lib/ws.d.ts +68 -0
  52. package/lib/ws.js +74 -0
  53. package/lib/zip.d.ts +53 -0
  54. package/lib/zip.js +73 -0
  55. package/lib/zlib.d.ts +76 -0
  56. package/lib/zlib.js +78 -0
  57. package/main.d.ts +6 -1
  58. package/main.js +11 -1
  59. package/package.json +2 -2
  60. package/sys/child.js +104 -0
  61. package/sys/cmd.js +28 -0
  62. package/sys/ctr.d.ts +166 -0
  63. package/sys/ctr.js +177 -0
  64. package/sys/master.js +63 -0
  65. package/sys/mod.d.ts +266 -0
  66. package/sys/mod.js +335 -0
  67. package/sys/route.d.ts +34 -0
  68. package/sys/route.js +164 -0
  69. package/www/example/ctr/test.d.ts +3 -0
  70. package/www/example/ctr/test.js +63 -1
  71. package/www/example/mod/test.js +14 -0
  72. package/www/example/mod/testdata.js +9 -0
  73. package/www/example/ws/test.js +1 -0
  74. package/.VSCodeCounter/2025-02-14_14-46-44/details.md +0 -82
  75. package/.VSCodeCounter/2025-02-14_14-46-44/diff-details.md +0 -15
  76. package/.VSCodeCounter/2025-02-14_14-46-44/diff.csv +0 -2
  77. package/.VSCodeCounter/2025-02-14_14-46-44/diff.md +0 -19
  78. package/.VSCodeCounter/2025-02-14_14-46-44/diff.txt +0 -22
  79. package/.VSCodeCounter/2025-02-14_14-46-44/results.csv +0 -69
  80. package/.VSCodeCounter/2025-02-14_14-46-44/results.json +0 -1
  81. package/.VSCodeCounter/2025-02-14_14-46-44/results.md +0 -48
  82. package/.VSCodeCounter/2025-02-14_14-46-44/results.txt +0 -118
  83. package/.vscode/tasks.json +0 -15
package/sys/route.js CHANGED
@@ -39,9 +39,16 @@ exports.unlinkUploadFiles = unlinkUploadFiles;
39
39
  exports.waitCtr = waitCtr;
40
40
  exports.trimPost = trimPost;
41
41
  exports.getFormData = getFormData;
42
+ /**
43
+ * Project: Kebab, User: JianSuoQiYue
44
+ * Date: 2019-4-15 13:40
45
+ * Last: 2020-4-14 13:52:00, 2022-09-07 01:43:31, 2023-12-29 17:24:03, 2024-2-7 00:28:50, 2024-6-6 15:15:54, 2025-6-13 19:23:53
46
+ */
42
47
  const http = __importStar(require("http"));
43
48
  const stream = __importStar(require("stream"));
49
+ // --- 第三方 ---
44
50
  const ws = __importStar(require("@litert/websocket"));
51
+ // --- 库和定义 ---
45
52
  const lFs = __importStar(require("../lib/fs"));
46
53
  const lZlib = __importStar(require("../lib/zlib"));
47
54
  const lCore = __importStar(require("../lib/core"));
@@ -51,18 +58,29 @@ const lResponse = __importStar(require("../lib/net/response"));
51
58
  const lWs = __importStar(require("../lib/ws"));
52
59
  const sCtr = __importStar(require("./ctr"));
53
60
  const kebab = __importStar(require("../index"));
61
+ /** --- 动态层 kebab.json 缓存(文件路径: 最终合并值) --- */
54
62
  let kebabConfigs = {};
63
+ /**
64
+ * --- 清除已经加载的虚拟主机配置文件 ---
65
+ */
55
66
  function clearKebabConfigs() {
56
67
  kebabConfigs = {};
57
68
  }
69
+ /**
70
+ * --- 若为动态路径则执行此函数,此函数不进行判断 kebab.json 是否存在 ---
71
+ * @param data 传导的数据
72
+ */
58
73
  async function run(data) {
74
+ // --- 检测 path 是否是静态文件 ---
59
75
  if (/^(stc\/.*|favicon.\w+?\??.*|apple[\w-]+?\.png\??.*|[\w-]+?\.txt\??.*|[\w-]+?\.html\??.*)/.test(data.path)) {
60
76
  return false;
61
77
  }
78
+ // --- 根据 res 还是 socket 进行初始化设置 ---
62
79
  if (data.res) {
63
80
  data.res.setHeader('expires', 'Mon, 26 Jul 1994 05:00:00 GMT');
64
81
  data.res.setHeader('cache-control', 'no-store');
65
82
  }
83
+ // --- 判断 kebab config 是否已经读取过 ---
66
84
  if (!kebabConfigs[data.rootPath + 'kebab.json']) {
67
85
  const configContent = await lFs.getContent(data.rootPath + 'kebab.json', 'utf8');
68
86
  if (!configContent) {
@@ -81,6 +99,7 @@ async function run(data) {
81
99
  if (routeContent) {
82
100
  kebabConfigs[data.rootPath + 'kebab.json'].route = lText.parseJson(routeContent);
83
101
  }
102
+ // --- 将全局的项目应用到 vhostConfigs 里,但当 vhostConfigs 有项,则不应用 ---
84
103
  for (const name in lCore.globalConfig) {
85
104
  if (typeof lCore.globalConfig[name] !== 'object') {
86
105
  if (kebabConfigs[data.rootPath + 'kebab.json'][name] === undefined) {
@@ -98,6 +117,7 @@ async function run(data) {
98
117
  }
99
118
  }
100
119
  }
120
+ // --- 加载 kebab config ---
101
121
  const config = {};
102
122
  const configData = kebabConfigs[data.rootPath + 'kebab.json'];
103
123
  for (const name in configData) {
@@ -108,6 +128,7 @@ async function run(data) {
108
128
  'qs': data.uri.query ?? '',
109
129
  'startTime': process.hrtime.bigint(),
110
130
  'startMemory': process.memoryUsage().rss,
131
+ // --- 环境判断 ---
111
132
  'mobile': data.req.headers['user-agent'] ? data.req.headers['user-agent'].includes('mobile') : false,
112
133
  'wechat': data.req.headers['user-agent'] ? data.req.headers['user-agent'].includes('micromessenger') : false,
113
134
  'https': data.uri.protocol === 'https' ? true : false,
@@ -115,12 +136,14 @@ async function run(data) {
115
136
  'hostname': data.uri.hostname ?? '',
116
137
  'hostport': data.uri.port ? parseInt(data.uri.port) : (data.uri.protocol === 'https' ? 443 : 80),
117
138
  'uri': data.uri,
139
+ // --- 服务端用的路径 ---
118
140
  'rootPath': data.rootPath,
119
141
  'ctrPath': data.rootPath + 'ctr/',
120
142
  'viewPath': data.rootPath + 'view/',
121
143
  'dataPath': data.rootPath + 'data/',
122
144
  'modPath': data.rootPath + 'mod/',
123
145
  'wsPath': data.rootPath + 'ws/',
146
+ // --- 前端用的路径 ---
124
147
  'urlBase': data.urlBase,
125
148
  'urlStc': data.urlBase + 'stc/',
126
149
  'urlFull': (data.uri.protocol ?? '') + '//' + (data.uri.host ?? '') + data.urlBase,
@@ -133,13 +156,18 @@ async function run(data) {
133
156
  if (!config.set.staticPathFull) {
134
157
  config.set.staticPathFull = config.const.urlStcFull;
135
158
  }
159
+ // --- data.path 是安全的,不会是 ../../ 来访问到了外层,已经做过处理 ---
136
160
  let path = data.path;
161
+ // --- 如果为空则定义为 @ ---
137
162
  if (path === '') {
138
163
  path = '@';
139
164
  }
165
+ // --- 检测是否托管语言页面 ---
140
166
  const param = [];
141
167
  if (config.lang && data.res) {
168
+ // --- 仅仅 res 模式支持 config.lang ---
142
169
  if (path[2] !== '/') {
170
+ // --- 不管是首页还是 @ 页,都会到此处 ---
143
171
  let isDirect = false;
144
172
  if (!path) {
145
173
  if (config.lang.direct.includes('@')) {
@@ -151,11 +179,13 @@ async function run(data) {
151
179
  if (!path.startsWith(ditem)) {
152
180
  continue;
153
181
  }
182
+ // --- 放行 ---
154
183
  isDirect = true;
155
184
  break;
156
185
  }
157
186
  }
158
187
  if (!isDirect) {
188
+ // --- 不放行 ---
159
189
  const alang = data.req.headers['accept-language']?.toLowerCase() ?? config.lang.list[0];
160
190
  const apath = config.const.path + (config.const.qs ? '?' + config.const.qs : '');
161
191
  for (const lang of config.lang.list) {
@@ -181,6 +211,7 @@ async function run(data) {
181
211
  }
182
212
  }
183
213
  else {
214
+ // --- 已经是含语言的路径了 --
184
215
  param.push(path.slice(0, 2));
185
216
  path = path.slice(3);
186
217
  if (!path) {
@@ -188,6 +219,7 @@ async function run(data) {
188
219
  }
189
220
  }
190
221
  }
222
+ // --- 检查路由表 ---
191
223
  let match = null;
192
224
  let pathLeft = '', pathRight = '';
193
225
  for (const rule in config.route) {
@@ -213,6 +245,7 @@ async function run(data) {
213
245
  pathLeft = getWsCtrName(path);
214
246
  }
215
247
  }
248
+ // --- 若文件名为保留的 middle 将不允许进行 ---
216
249
  if (pathLeft.startsWith('middle')) {
217
250
  const text = '[Error] Controller not found, path: ' + path + '.';
218
251
  if (data.res) {
@@ -232,7 +265,9 @@ async function run(data) {
232
265
  }
233
266
  return true;
234
267
  }
268
+ // --- 原始 GET ---
235
269
  const get = data.uri.query ? lText.queryParse(data.uri.query) : {};
270
+ // --- Cookie ---
236
271
  const cookies = {};
237
272
  if (data.req.headers['cookie']) {
238
273
  const hcookies = data.req.headers['cookie'].split(';');
@@ -241,20 +276,26 @@ async function run(data) {
241
276
  cookies[co[0].trim()] = decodeURIComponent(co[1]);
242
277
  }
243
278
  }
279
+ // --- 处理 headers ---
244
280
  const headers = {};
245
281
  for (const key in data.req.headers) {
246
282
  headers[key.toLowerCase()] = data.req.headers[key];
247
283
  }
248
284
  headers['authorization'] ??= '';
285
+ /** --- 开发者返回值 --- */
249
286
  let rtn;
250
287
  if (data.socket && data.req instanceof http.IncomingMessage) {
288
+ // --- socket 模式,判断真实控制器文件是否存在 ---
251
289
  const filePath = config.const.wsPath + pathLeft + '.js';
252
290
  if (!await lFs.isFile(filePath)) {
291
+ // --- 指定的控制器不存在 ---
253
292
  data.socket?.destroy();
254
293
  return true;
255
294
  }
295
+ // --- 加载控制器文件 ---
256
296
  const ctrCtr = (await Promise.resolve(`${filePath}`).then(s => __importStar(require(s)))).default;
257
297
  const cctr = new ctrCtr(config, data.req);
298
+ // --- 先处理 web socket 的情况 ---
258
299
  let wsSocket;
259
300
  try {
260
301
  const options = await cctr.onUpgrade();
@@ -327,6 +368,7 @@ async function run(data) {
327
368
  return;
328
369
  }
329
370
  if (typeof wrtn === 'object') {
371
+ // --- 返回的是数组,那么代表可能是 JSON,可能是对象序列 ---
330
372
  wsSocket.writeResult(wrtn);
331
373
  return;
332
374
  }
@@ -337,6 +379,7 @@ async function run(data) {
337
379
  break;
338
380
  }
339
381
  default: {
382
+ // --- nothing ---
340
383
  }
341
384
  }
342
385
  }).on('drain', async () => {
@@ -368,22 +411,34 @@ async function run(data) {
368
411
  if (!data.res) {
369
412
  return true;
370
413
  }
414
+ // --- 加载中间控制器 ---
371
415
  const middleCtr = (await Promise.resolve(`${config.const.ctrPath + 'middle'}`).then(s => __importStar(require(s)))).default;
372
416
  const middle = new middleCtr(config, data.req, data.res);
417
+ /** --- 可能存在的最终控制器 --- */
373
418
  let cctr = null;
419
+ // --- 对信息进行初始化 ---
374
420
  if (data.timer) {
375
421
  middle.setPrototype('_timer', data.timer);
376
422
  }
423
+ // --- 路由定义的参数序列 ---
377
424
  middle.setPrototype('_param', param);
425
+ // --- action 名 ---
378
426
  middle.setPrototype('_action', pathRight);
379
427
  middle.setPrototype('_headers', headers);
380
428
  middle.setPrototype('_get', get);
381
429
  const rawPost = await getPost(data.req);
430
+ // --- 原始 POST ---
382
431
  middle.setPrototype('_rawPost', rawPost[1]);
432
+ // --- 原始 input ---
383
433
  middle.setPrototype('_input', rawPost[0]);
434
+ // --- 处理 POST 的值 JSON 上面就同时处理了 ---
435
+ // --- 格式化 post 数据 ---
436
+ // --- assign 是为了创建一份拷贝 ---
384
437
  const post = Object.assign({}, rawPost[1]);
385
438
  trimPost(post);
386
439
  middle.setPrototype('_post', post);
440
+ // --- form data 格式交由用户自行获取,可以直接获取文件流,然后做他想做的事情 ---
441
+ // --- 执行中间控制器的 onLoad ---
387
442
  try {
388
443
  rtn = await middle.onLoad();
389
444
  }
@@ -398,8 +453,11 @@ async function run(data) {
398
453
  let cacheTTL = middle.getPrototype('_cacheTTL');
399
454
  let httpCode = middle.getPrototype('_httpCode');
400
455
  if (rtn === undefined || rtn === true) {
456
+ // --- 只有不返回或返回 true 时才加载控制文件 ---
457
+ // --- 判断真实控制器文件是否存在 ---
401
458
  const filePath = config.const.ctrPath + pathLeft + '.js';
402
459
  if (!await lFs.isFile(filePath)) {
460
+ // --- 指定的控制器不存在 ---
403
461
  const text = '[Error] Controller not found, path: ' + path + '.';
404
462
  if (config.route['#404']) {
405
463
  data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
@@ -413,10 +471,13 @@ async function run(data) {
413
471
  data.res.end(text);
414
472
  return true;
415
473
  }
474
+ // --- 加载控制器文件 ---
416
475
  const ctrCtr = (await Promise.resolve(`${filePath}`).then(s => __importStar(require(s)))).default;
417
476
  cctr = new ctrCtr(config, data.req, data.res ?? data.socket);
477
+ // --- 对信息进行初始化 ---
418
478
  cctr.setPrototype('_timer', middle.getPrototype('_timer'));
419
479
  cctr.setPrototype('_waitInfo', middle.getPrototype('_waitInfo'));
480
+ // --- 路由定义的参数序列 ---
420
481
  cctr.setPrototype('_param', param);
421
482
  cctr.setPrototype('_action', middle.getPrototype('_action'));
422
483
  cctr.setPrototype('_headers', headers);
@@ -435,12 +496,15 @@ async function run(data) {
435
496
  cctr.setPrototype('_xsrf', middle.getPrototype('_xsrf'));
436
497
  cctr.setPrototype('_httpCode', middle.getPrototype('_httpCode'));
437
498
  await lCore.log(cctr, '', '-visit');
499
+ // --- 强制 HTTPS ---
438
500
  if (config.set.mustHttps && !config.const.https) {
439
501
  data.res.setHeader('location', data.req.url ?? '');
440
502
  data.res.writeHead(302);
441
503
  return true;
442
504
  }
505
+ // --- 检测 action 是否存在,以及排除内部方法 ---
443
506
  if (pathRight.startsWith('_') || pathRight === 'onUpgrade' || pathRight === 'onLoad' || pathRight === 'onData' || pathRight === 'onDrain' || pathRight === 'onClose' || pathRight === 'setPrototype' || pathRight === 'getPrototype' || pathRight === 'getAuthorization') {
507
+ // --- _ 开头的 action 是内部方法,不允许访问 ---
444
508
  if (config.route['#404']) {
445
509
  data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
446
510
  data.res.writeHead(302);
@@ -471,8 +535,10 @@ async function run(data) {
471
535
  data.res.end(text);
472
536
  return true;
473
537
  }
538
+ // --- 执行 onLoad 方法 ---
474
539
  try {
475
540
  rtn = await cctr.onLoad();
541
+ // --- 执行 action ---
476
542
  if (rtn === undefined || rtn === true) {
477
543
  rtn = await cctr[pathRight]();
478
544
  rtn = await cctr.onUnload(rtn);
@@ -482,6 +548,7 @@ async function run(data) {
482
548
  await sess.update();
483
549
  }
484
550
  }
551
+ // --- 获取 ctr 设置的 cache 和 hcode ---
485
552
  cacheTTL = cctr.getPrototype('_cacheTTL');
486
553
  httpCode = cctr.getPrototype('_httpCode');
487
554
  }
@@ -495,15 +562,19 @@ async function run(data) {
495
562
  return true;
496
563
  }
497
564
  }
565
+ // --- 设置缓存 ---
498
566
  if (!data.res.headersSent && (cacheTTL > 0)) {
499
567
  data.res.setHeader('expires', lTime.format(0, 'D, d M Y H:i:s', Date.now() + cacheTTL * 1000) + ' GMT');
500
568
  data.res.setHeader('cache-control', 'max-age=' + cacheTTL.toString());
501
569
  }
570
+ // --- 设置自定义 hcode ---
502
571
  if (httpCode === 0) {
503
572
  httpCode = 200;
504
573
  }
574
+ // --- 判断返回值 ---
505
575
  if (rtn === undefined || typeof rtn === 'boolean' || rtn === null) {
506
576
  if (data.res.headersSent) {
577
+ // --- 已经自行输出过 writeHead,可能自行处理了内容,如 pipe,则不再 writeHead ---
507
578
  }
508
579
  else {
509
580
  if (data.res.getHeader('location')) {
@@ -514,12 +585,15 @@ async function run(data) {
514
585
  }
515
586
  }
516
587
  if (!data.res.writableEnded) {
588
+ // --- 如果当前还没结束,则强制关闭连接,一切 pipe 请自行在方法中 await,否则会被中断 ---
517
589
  data.res.end('');
518
590
  }
519
591
  await waitCtr(cctr ?? middle);
520
592
  return true;
521
593
  }
522
594
  if (typeof rtn === 'string' || rtn instanceof Buffer) {
595
+ // --- 返回的是纯字符串,直接输出 ---
596
+ /** --- 当前的压缩对象 --- */
523
597
  let compress = null;
524
598
  if (!data.res.headersSent) {
525
599
  if (!data.res.getHeader('content-type')) {
@@ -545,7 +619,9 @@ async function run(data) {
545
619
  }
546
620
  }
547
621
  else if (rtn instanceof stream.Readable || rtn instanceof lResponse.Response) {
622
+ // --- 返回的是流,那就以管道的形式输出 ---
548
623
  const stm = rtn instanceof stream.Readable ? rtn : rtn.getStream();
624
+ /** --- 当前的压缩对象 --- */
549
625
  let compress = null;
550
626
  if (!data.res.headersSent) {
551
627
  if (!data.res.getHeader('content-type')) {
@@ -567,8 +643,11 @@ async function run(data) {
567
643
  }
568
644
  }
569
645
  else if (typeof rtn === 'object') {
646
+ // --- 返回的是数组,那么代表可能是 JSON,可能是对象序列 ---
570
647
  if (Array.isArray(rtn)) {
648
+ // --- [0, 'xxx'] 模式,或 [string, Readable] 模式 ---
571
649
  if (rtn.length === 0) {
650
+ // --- 异常 ---
572
651
  if (!data.res.headersSent) {
573
652
  data.res.writeHead(500);
574
653
  }
@@ -578,14 +657,17 @@ async function run(data) {
578
657
  }
579
658
  else {
580
659
  if (typeof rtn[0] === 'number') {
660
+ // --- 1. ---
581
661
  const json = { 'result': rtn[0] };
582
662
  if (rtn[1] !== undefined) {
583
663
  if (typeof rtn[1] === 'object') {
664
+ // --- [0, ...{'xx': 'xx'}] ---
584
665
  for (let i = 1; i < rtn.length; ++i) {
585
666
  Object.assign(json, rtn[i]);
586
667
  }
587
668
  }
588
669
  else {
670
+ // --- [0, 'xxx'], [0, 'xxx', ...{'xx': 'xx'}] ---
589
671
  json['msg'] = rtn[1];
590
672
  for (let i = 2; i < rtn.length; ++i) {
591
673
  Object.assign(json, rtn[i]);
@@ -593,6 +675,7 @@ async function run(data) {
593
675
  }
594
676
  }
595
677
  const writeData = lText.stringifyJson(json);
678
+ /** --- 当前的压缩对象 --- */
596
679
  let compress = null;
597
680
  if (!data.res.headersSent) {
598
681
  data.res.setHeader('content-type', 'application/json; charset=utf-8');
@@ -616,6 +699,8 @@ async function run(data) {
616
699
  }
617
700
  }
618
701
  else {
702
+ // --- 2. 字符串与 Readable 的组合 ---
703
+ /** --- 当前的压缩对象 --- */
619
704
  let compress = null;
620
705
  if (!data.res.headersSent) {
621
706
  if (!data.res.getHeader('content-type')) {
@@ -629,6 +714,7 @@ async function run(data) {
629
714
  }
630
715
  if (!data.res.writableEnded) {
631
716
  const passThrough = new stream.PassThrough();
717
+ // --- 先进行 pipe 绑定,之后 write 或 pipe 进 pass 的数据,直接就可以消费 ---
632
718
  if (compress) {
633
719
  passThrough.pipe(compress.compress).pipe(data.res);
634
720
  }
@@ -641,7 +727,9 @@ async function run(data) {
641
727
  }
642
728
  }
643
729
  else {
730
+ // --- 直接是个 json 对象 ---
644
731
  const writeData = lText.stringifyJson(rtn);
732
+ /** --- 当前的压缩对象 --- */
645
733
  let compress = null;
646
734
  if (!data.res.headersSent) {
647
735
  data.res.setHeader('content-type', 'application/json; charset=utf-8');
@@ -666,6 +754,7 @@ async function run(data) {
666
754
  }
667
755
  }
668
756
  else {
757
+ // --- 异常 ---
669
758
  if (!data.res.headersSent) {
670
759
  data.res.writeHead(500);
671
760
  }
@@ -676,6 +765,10 @@ async function run(data) {
676
765
  await waitCtr(cctr ?? middle);
677
766
  return true;
678
767
  }
768
+ /**
769
+ * --- 获取控制器 left 和 action ---
770
+ * @param path 相对路径
771
+ */
679
772
  function getPathLeftRight(path) {
680
773
  const pathLio = path.lastIndexOf('/');
681
774
  if (pathLio === -1) {
@@ -684,6 +777,10 @@ function getPathLeftRight(path) {
684
777
  const right = path.slice(pathLio + 1);
685
778
  return [path.slice(0, pathLio), right === '' ? 'index' : right];
686
779
  }
780
+ /**
781
+ * --- 根据 path 获取 ws 的控制器类名, Mutton: false, Kebab: true ---
782
+ * @param path 路径
783
+ */
687
784
  function getWsCtrName(path) {
688
785
  const pathLio = path.lastIndexOf('/');
689
786
  if (pathLio === -1) {
@@ -691,6 +788,10 @@ function getWsCtrName(path) {
691
788
  }
692
789
  return path.slice(pathLio + 1).toLowerCase();
693
790
  }
791
+ /**
792
+ * --- 删除本次请求所有已上传的临时文件, Mutton: false, Kebab: true ---
793
+ * @param cctr Ctr 对象 或 files
794
+ */
694
795
  async function unlinkUploadFiles(cctr) {
695
796
  const cfiles = cctr instanceof sCtr.Ctr ? cctr.getPrototype('_files') : cctr;
696
797
  for (const name in cfiles) {
@@ -703,18 +804,31 @@ async function unlinkUploadFiles(cctr) {
703
804
  }
704
805
  }
705
806
  }
807
+ /**
808
+ * --- 等待异步任务结束,并删除临时文件,如果结束后还有事务没关闭,则会在本函数中打印控制台并且写入 log 文件 ---
809
+ * --- 此时其实已经给客户端返回了,此处等待不消耗客户端的等待时间 ---
810
+ * @param cctr 要等待的控制器 ---
811
+ */
706
812
  async function waitCtr(cctr) {
813
+ // --- 先判断异步任务是否结束 ---
707
814
  const waitInfo = cctr.getPrototype('_waitInfo');
708
815
  if (waitInfo.asyncTask.count) {
709
816
  await waitInfo.asyncTask.callback();
710
817
  }
818
+ // --- 判断事务 ---
711
819
  if (waitInfo.transaction) {
820
+ // --- 有事务未关闭 ---
712
821
  const msg = 'transaction(' + waitInfo.transaction + ') not be closed';
713
822
  lCore.display('[ERROR][ROUTE][WAITCTR] ' + msg + ': ', cctr.getPrototype('_config').const.path);
714
823
  await lCore.log(cctr, msg, '-error');
715
824
  }
825
+ // --- 彻底结束,删除文件 ---
716
826
  await unlinkUploadFiles(cctr);
717
827
  }
828
+ /**
829
+ * --- 将 POST 数据的值执行 trim ---
830
+ * @param post
831
+ */
718
832
  function trimPost(post) {
719
833
  for (const key in post) {
720
834
  const val = post[key];
@@ -726,6 +840,10 @@ function trimPost(post) {
726
840
  }
727
841
  }
728
842
  }
843
+ /**
844
+ * --- 内部使用,获取 post 对象,如果是文件上传(formdata)的情况则不获取 ---
845
+ * @param req 请求对象
846
+ */
729
847
  function getPost(req) {
730
848
  return new Promise(function (resolve) {
731
849
  const ct = req.headers['content-type'] ?? '';
@@ -733,6 +851,7 @@ function getPost(req) {
733
851
  resolve(['', {}]);
734
852
  return;
735
853
  }
854
+ // --- json 或普通 post ---
736
855
  let buffer = Buffer.from('');
737
856
  req.on('data', function (chunk) {
738
857
  buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length);
@@ -743,6 +862,7 @@ function getPost(req) {
743
862
  resolve(['', {}]);
744
863
  return;
745
864
  }
865
+ // --- 判断 json 还是普通 ---
746
866
  if (ct.includes('json')) {
747
867
  try {
748
868
  resolve([s, lText.parseJson(s)]);
@@ -756,6 +876,11 @@ function getPost(req) {
756
876
  });
757
877
  });
758
878
  }
879
+ /**
880
+ * --- 获取 formdata 的 post ---
881
+ * @param req 请求头
882
+ * @param events 文件处理情况
883
+ */
759
884
  function getFormData(req, events = {}) {
760
885
  return new Promise(function (resolve) {
761
886
  const ct = req.headers['content-type'] ?? '';
@@ -763,54 +888,80 @@ function getFormData(req, events = {}) {
763
888
  resolve({ 'post': {}, 'files': {} });
764
889
  return;
765
890
  }
891
+ /** --- boundary 位置 --- */
766
892
  const clio = ct.lastIndexOf('boundary=');
767
893
  if (clio === -1) {
768
894
  resolve({ 'post': {}, 'files': {} });
769
895
  return;
770
896
  }
897
+ /** --- 最终返回 --- */
771
898
  const rtn = {
772
899
  'post': {},
773
900
  'files': {}
774
901
  };
902
+ /** --- 获取的 boundary 文本 --- */
775
903
  const boundary = ct.slice(clio + 9);
904
+ // 一下变量是相对于 data 事件的全局变量 ---
905
+ /** --- 当前临时读入的还未处理的 buffer --- */
776
906
  let buffer = Buffer.from('');
907
+ /** --- 状态机 --- */
777
908
  let EState;
778
909
  (function (EState) {
910
+ /** --- 判断头部是什么 --- */
779
911
  EState[EState["WAIT"] = 0] = "WAIT";
912
+ /** --- 当前是文件 --- */
780
913
  EState[EState["FILE"] = 1] = "FILE";
914
+ /** --- 当前是 post 数据 --- */
781
915
  EState[EState["POST"] = 2] = "POST";
782
916
  })(EState || (EState = {}));
917
+ /** --- 当前所处状态 --- */
783
918
  let state = EState.WAIT;
919
+ /** --- 当前处理中的 post 的 name --- */
784
920
  let name = '';
921
+ /** --- 当前处理中的 file 的 name --- */
785
922
  let fileName = '';
923
+ /** --- 当前处理中的临时文件名 --- */
786
924
  let ftmpName = '';
925
+ /** --- 当前正在写入的临时文件流 --- */
787
926
  let ftmpStream;
927
+ /** --- 当前临时文件已写入的流大小 --- */
788
928
  let ftmpSize = 0;
929
+ /** --- 当前正在写入的文件数量 --- */
789
930
  let writeFileLength = 0;
931
+ /** --- 当前读取是否已经完全结束 --- */
790
932
  let readEnd = false;
933
+ // --- 开始读取 ---
791
934
  req.on('data', function (chunk) {
792
935
  buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length);
793
936
  while (true) {
794
937
  switch (state) {
795
938
  case EState.WAIT: {
939
+ /** --- 中断符位置 --- */
796
940
  const io = buffer.indexOf('\r\n\r\n');
797
941
  if (io === -1) {
798
942
  return;
799
943
  }
944
+ // --- 头部已经读取完毕 ---
800
945
  const head = buffer.subarray(0, io).toString();
946
+ // --- 除头部外剩下的 buffer ---
801
947
  buffer = buffer.subarray(io + 4);
948
+ // --- 获取 name ---
802
949
  let match = /name="(.+?)"/.exec(head);
803
950
  name = match ? match[1] : '';
951
+ // --- 判断是 post 还是文件 ---
804
952
  match = /filename="(.+?)"/.exec(head);
805
953
  if (!match) {
954
+ // --- 普通 post ---
806
955
  state = EState.POST;
807
956
  }
808
957
  else {
958
+ // --- 文件 ---
809
959
  ++writeFileLength;
810
960
  state = EState.FILE;
811
961
  fileName = match[1];
812
962
  const fr = events.onfilestart?.(name);
813
963
  if (fr !== true) {
964
+ // --- 创建文件流 ---
814
965
  const date = new Date();
815
966
  ftmpName = date.getUTCFullYear().toString() +
816
967
  (date.getUTCMonth() + 1).toString().padStart(2, '0') +
@@ -827,10 +978,12 @@ function getFormData(req, events = {}) {
827
978
  break;
828
979
  }
829
980
  case EState.POST: {
981
+ // --- POST 模式 ---
830
982
  const io = buffer.indexOf('\r\n--' + boundary);
831
983
  if (io === -1) {
832
984
  return;
833
985
  }
986
+ // --- 找到结束标语,写入 POST ---
834
987
  const val = buffer.subarray(0, io).toString();
835
988
  if (rtn.post[name]) {
836
989
  if (Array.isArray(rtn.post[name])) {
@@ -843,13 +996,16 @@ function getFormData(req, events = {}) {
843
996
  else {
844
997
  rtn.post[name] = val;
845
998
  }
999
+ // --- 重置状态机 ---
846
1000
  state = EState.WAIT;
847
1001
  buffer = buffer.subarray(io + 4 + boundary.length);
848
1002
  break;
849
1003
  }
850
1004
  case EState.FILE: {
1005
+ // --- FILE 模式 ---
851
1006
  const io = buffer.indexOf('\r\n--' + boundary);
852
1007
  if (io === -1) {
1008
+ // --- 没找到结束标语,将预留 boundary 长度之前的写入到文件 ---
853
1009
  const writeBuffer = buffer.subarray(0, -boundary.length - 4);
854
1010
  if (ftmpName === '') {
855
1011
  events.onfiledata?.(writeBuffer);
@@ -861,6 +1017,7 @@ function getFormData(req, events = {}) {
861
1017
  buffer = buffer.subarray(-boundary.length - 4);
862
1018
  return;
863
1019
  }
1020
+ // --- 找到结束标语,结束标语之前的写入文件,之后的重新放回 buffer ---
864
1021
  const writeBuffer = buffer.subarray(0, io);
865
1022
  if (ftmpName === '') {
866
1023
  events.onfileend?.();
@@ -871,13 +1028,17 @@ function getFormData(req, events = {}) {
871
1028
  ftmpStream.end(() => {
872
1029
  --writeFileLength;
873
1030
  if (!readEnd) {
1031
+ // --- request 没读完,不管 ---
874
1032
  return;
875
1033
  }
876
1034
  if (writeFileLength) {
1035
+ // --- req 读完了但文件还没写完,不管 ---
877
1036
  return;
878
1037
  }
1038
+ // --- 文件也写完了 ---
879
1039
  resolve(rtn);
880
1040
  });
1041
+ // --- POST 部分 ---
881
1042
  let fname = fileName.replace(/\\/g, '/');
882
1043
  const nlio = fname.lastIndexOf('/');
883
1044
  if (nlio !== -1) {
@@ -901,6 +1062,7 @@ function getFormData(req, events = {}) {
901
1062
  rtn.files[name] = val;
902
1063
  }
903
1064
  }
1065
+ // --- 重置状态机 ---
904
1066
  state = EState.WAIT;
905
1067
  buffer = buffer.subarray(io + 4 + boundary.length);
906
1068
  break;
@@ -914,8 +1076,10 @@ function getFormData(req, events = {}) {
914
1076
  req.on('end', function () {
915
1077
  readEnd = true;
916
1078
  if (writeFileLength) {
1079
+ // --- 文件没写完 ---
917
1080
  return;
918
1081
  }
1082
+ // --- 文件写完了 ----
919
1083
  resolve(rtn);
920
1084
  });
921
1085
  });
@@ -90,5 +90,8 @@ export default class extends sCtr.Ctr {
90
90
  zip(): Promise<string>;
91
91
  buffer(): string;
92
92
  lan(): Promise<string>;
93
+ /**
94
+ * --- END ---
95
+ */
93
96
  private _getEnd;
94
97
  }