topbit 1.0.0 → 2.0.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.
Files changed (71) hide show
  1. package/LICENSE +128 -0
  2. package/README.cn.md +1562 -0
  3. package/README.md +1272 -0
  4. package/bin/app.js +17 -0
  5. package/bin/loadinfo.sh +18 -0
  6. package/bin/new-ctl.js +230 -0
  7. package/bin/newapp.js +22 -0
  8. package/cache/allow.js +130 -0
  9. package/cache/errserv.js +45 -0
  10. package/cache/minserv.js +167 -0
  11. package/cache/router.js +84 -0
  12. package/cache/rsa/localhost-cert.pem +19 -0
  13. package/cache/rsa/localhost-privkey.pem +28 -0
  14. package/cache/servsock.js +286 -0
  15. package/cache/sni.js +66 -0
  16. package/demo/allow.js +98 -0
  17. package/demo/group-api.js +161 -0
  18. package/demo/group-api2.js +109 -0
  19. package/demo/log.js +118 -0
  20. package/demo/memlimit.js +31 -0
  21. package/demo/min.js +7 -0
  22. package/demo/serv.js +15 -0
  23. package/images/middleware.jpg +0 -0
  24. package/images/titbit-middleware.png +0 -0
  25. package/images/titbit.png +0 -0
  26. package/package.json +38 -8
  27. package/src/bodyparser.js +420 -0
  28. package/src/connfilter.js +125 -0
  29. package/src/context1.js +166 -0
  30. package/src/context2.js +179 -0
  31. package/src/ctxpool.js +38 -0
  32. package/src/ext.js +318 -0
  33. package/src/fastParseUrl.js +426 -0
  34. package/src/headerLimit.js +18 -0
  35. package/src/http1.js +337 -0
  36. package/src/http2.js +337 -0
  37. package/src/httpc.js +251 -0
  38. package/src/loader/loader.js +999 -0
  39. package/src/logger.js +32 -0
  40. package/src/loggermsg.js +349 -0
  41. package/src/makeId.js +200 -0
  42. package/src/midcore.js +213 -0
  43. package/src/middleware1.js +104 -0
  44. package/src/middleware2.js +121 -0
  45. package/src/monitor.js +380 -0
  46. package/src/movefile.js +30 -0
  47. package/src/optionsCheck.js +54 -0
  48. package/src/randstring.js +23 -0
  49. package/src/router.js +682 -0
  50. package/src/sendmsg.js +27 -0
  51. package/src/strong.js +72 -0
  52. package/src/token/token.js +462 -0
  53. package/src/topbit.js +1291 -0
  54. package/src/versionCheck.js +31 -0
  55. package/test/test-bigctx.js +29 -0
  56. package/test/test-daemon-args.js +7 -0
  57. package/test/test-find.js +69 -0
  58. package/test/test-helper.js +81 -0
  59. package/test/test-route-sort.js +71 -0
  60. package/test/test-route.js +49 -0
  61. package/test/test-route2.js +51 -0
  62. package/test/test-run-args.js +7 -0
  63. package/test/test-url.js +52 -0
  64. package/tmp/buff-code +134 -0
  65. package/tmp/devplan +9 -0
  66. package/tmp/evt-test.js +34 -0
  67. package/tmp/fastParseUrl.js +302 -0
  68. package/tmp/router-rule.js +559 -0
  69. package/tmp/test-cdps.js +122 -0
  70. package/tmp/titbit.js +1286 -0
  71. package/main.js +0 -0
@@ -0,0 +1,999 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const process = require('node:process');
5
+
6
+ /**
7
+ * 全局中间件容易因为group选项引发问题,若要把加载的controller目录隔离出来,需要针对每个分组加载中间件。
8
+ * 就是在全局中间件不指定group的情况下,默认是所有group而不是全局。
9
+ * 这样对前缀支持也可以完全兼容。
10
+ */
11
+
12
+ let outWarning = (text, errname = 'Warning', color = '\x1b[2;31;47m') => {
13
+ setTimeout(() => {
14
+ console.error(`${color} ${errname}: ${text} \x1b[0m\n`);
15
+ }, 1280);
16
+ };
17
+
18
+ let nameErrorInfo = '名称不能有空格换行特殊字符等,仅支持 字母 数字 减号 下划线,字母开头。';
19
+
20
+ //获取模型文件列表
21
+ function getModelFiles(filepath) {
22
+ let mlist = fs.readdirSync(filepath, { withFileTypes: true });
23
+
24
+ let rlist = [];
25
+
26
+ for (let i=0; i < mlist.length; i++) {
27
+ if (!mlist[i].isFile()) continue;
28
+
29
+ if (mlist[i].name.substring(mlist[i].name.length-3) !== '.js')
30
+ continue;
31
+
32
+ if (mlist[i].name[0] === '!' || mlist[i].name[0] === '_')
33
+ continue;
34
+
35
+ rlist.push(mlist[i].name);
36
+ }
37
+
38
+ return rlist;
39
+ }
40
+
41
+ class TopbitLoader {
42
+
43
+ constructor(options = {}) {
44
+ //let appDir = __dirname + '.';
45
+ let appDir = '.';
46
+
47
+ this.globalMidTable = [];
48
+ this.groupMidTable = {};
49
+ this.fileMidTable = {};
50
+
51
+ if (typeof options !== 'object') {
52
+ options = {};
53
+ }
54
+
55
+ if (options.appPath !== undefined && typeof options.appPath === 'string') {
56
+ appDir = options.appPath;
57
+ }
58
+
59
+ if (appDir.length > 0 && appDir[0] !== '/') {
60
+ appDir = fs.realpathSync(appDir);
61
+ }
62
+
63
+ this.optionsCacheLog = null;
64
+
65
+ this.methodNumber = {
66
+ get: 1,
67
+ post: 2,
68
+ put: 3,
69
+ delete: 4,
70
+ _delete: 4,
71
+ options: 5,
72
+ head: 6,
73
+ trace: 7,
74
+ patch: 8,
75
+ list: 9
76
+ }
77
+
78
+ this.config = {
79
+ //当作为模块引入时,根据路径关系,
80
+ //可能的位置是node_modules/titbit-loader/loader.js,
81
+ //所以默认在父级目录两层和node_modules同级。
82
+ appPath : appDir,
83
+ controllerPath : appDir+'/controller',
84
+ modelPath : appDir+'/model',
85
+ midwarePath : appDir+'/middleware',
86
+ loadModel : true,
87
+
88
+ deep : 1,
89
+ mname : null,
90
+
91
+ modelNamePre: '',
92
+
93
+ //如果作为数组则会去加载指定的子目录
94
+ subgroup : null,
95
+
96
+ prePath: '',
97
+
98
+ homeFile: '',
99
+
100
+ initArgs: null,
101
+
102
+ multi: false,
103
+
104
+ optionsRoute: true,
105
+
106
+ fileAsGroup: true,
107
+
108
+ beforeController: null,
109
+ afterController: null
110
+ };
111
+
112
+ //用于在fileAsGroup模式,为options添加的分组避免和文件的分组冲突。
113
+ //考虑这样的情况:controller目录中存在 xyz.js文件和xyz目录。
114
+ this.groupTag = '@';
115
+
116
+ //组中间件的缓存,用于fileAsGroup的操作。
117
+ this.groupCache = {};
118
+ this.globalCache = [];
119
+
120
+ //在加载Model时可能需要传递参数
121
+ this.mdb = null;
122
+ this.mdbMap = null;
123
+
124
+ this.routepreg = /^[a-z\d\-\_]+$/i;
125
+
126
+ for (let k in options) {
127
+
128
+ if (k == 'appPath') continue;
129
+
130
+ if (k === 'subgroup') {
131
+ if (typeof options[k] === 'string') options[k] = [ options[k] ];
132
+
133
+ if (options[k] instanceof Array) {
134
+ this.config.subgroup = options[k];
135
+ }
136
+ this._fmtSubGroup();
137
+ continue;
138
+ } else if ( (k === 'prePath' || k === 'prepath') && typeof options[k] === 'string') {
139
+ this.config.prePath = options[k];
140
+ this.fmtPrePath();
141
+ continue;
142
+ }
143
+
144
+ switch (k) {
145
+ case 'beforeController':
146
+ case 'afterController':
147
+ if (typeof options[k] === 'function') this.config[k] = options[k];
148
+ break;
149
+
150
+ case 'initArgs':
151
+ this.config.initArgs = options[k];
152
+ break;
153
+
154
+ case 'homeFile':
155
+ case 'modelNamePre':
156
+ ;(typeof options[k] === 'string') && (this.config[k] = options[k]);
157
+ break;
158
+
159
+ case 'mname':
160
+ ;(typeof options[k] === 'string' || typeof options[k] === 'symbol') && (this.config[k] = options[k]);
161
+ break;
162
+
163
+ case 'loadModel':
164
+ case 'multi':
165
+ case 'optionsRoute':
166
+ case 'fileAsGroup':
167
+ this.config[k] = !!options[k];
168
+ break;
169
+
170
+ case 'controllerPath':
171
+ case 'modelPath':
172
+ case 'midwarePath':
173
+ if (options[k][0] !== '/') {
174
+ this.config[k] = `${this.config.appPath}/${options[k]}`;
175
+ }
176
+ break;
177
+
178
+ default:;
179
+ }
180
+ }
181
+
182
+ try {
183
+ fs.accessSync(this.config.controllerPath, fs.constants.F_OK);
184
+ } catch (err) {
185
+ if (this.config.controllerPath.length > 0) {
186
+ fs.mkdirSync(this.config.controllerPath);
187
+ }
188
+ }
189
+
190
+ try {
191
+ fs.accessSync(this.config.midwarePath, fs.constants.F_OK);
192
+ } catch (err) {
193
+ if (this.config.midwarePath.length > 0) {
194
+ fs.mkdirSync(this.config.midwarePath);
195
+ }
196
+ }
197
+
198
+ try {
199
+ fs.accessSync(this.config.modelPath, fs.constants.F_OK);
200
+ } catch (err) {
201
+ if (this.config.modelPath.length > 0) {
202
+ fs.mkdirSync(this.config.modelPath);
203
+ }
204
+ }
205
+
206
+ if (options.mdb !== undefined && this.config.loadModel) {
207
+ this.mdb = options.mdb;
208
+ }
209
+
210
+ options.mdbMap && (this.mdbMap = options.mdbMap);
211
+ }
212
+
213
+ fmtPrePath() {
214
+ let prepath = this.config.prePath.trim().replace(/\/+/g, '/');
215
+ if (prepath === '/')
216
+ prepath = '';
217
+ else if (prepath.length > 0) {
218
+ if (prepath[0] !== '/') {
219
+ prepath = `/${prepath}`;
220
+ }
221
+ if (prepath[ prepath.length - 1 ] === '/') {
222
+ prepath = prepath.substring(0, prepath.length - 1);
223
+ }
224
+ }
225
+
226
+ this.config.prePath = prepath;
227
+ }
228
+
229
+ _fmtSubGroup() {
230
+ if (!(this.config.subgroup instanceof Array)) {
231
+ return;
232
+ }
233
+ let a;
234
+ for (let i = 0; i < this.config.subgroup.length; i++) {
235
+ a = this.config.subgroup[i];
236
+ a = a.trim().replace(/\/+/g, '');
237
+ this.config.subgroup[i] = a;
238
+ }
239
+ }
240
+
241
+ init(app) {
242
+ this.config.loadModel && this.loadModel(app);
243
+ this.mdbMap && this.loadModelMap(app);
244
+ this.defineServiceFunction(app);
245
+
246
+ this.loadController(app);
247
+ this.loadMidware(app);
248
+ app.service.__titbit_loader__ = true;
249
+ }
250
+
251
+ loadController(app) {
252
+ if (app.service.__titbit_loader__ && !this.config.multi) {
253
+ outWarning('您已经使用titbit-loader加载过路由,多次加载容易导致路由冲突,重复操作将会被终止。');
254
+ outWarning('若有需要,可设置选项multi为true允许多次加载。', ' 提示');
255
+ return false;
256
+ }
257
+
258
+ this.optionsCacheLog = {};
259
+
260
+ let cfiles = {};
261
+ this.readControllers(this.config.controllerPath, cfiles);
262
+ let cob = null;
263
+ let Ctlr;
264
+ for (let k in cfiles) {
265
+ try {
266
+ Ctlr = require(k);
267
+ //不是函数或箭头函数无法进行new操作。
268
+ if (typeof Ctlr !== 'function' || !Ctlr.prototype) {
269
+ continue;
270
+ }
271
+
272
+ cob = new Ctlr();
273
+
274
+ if (cob.init && typeof cob.init === 'function') {
275
+ cob.init(this.config.initArgs || app.service);
276
+ }
277
+
278
+ if (this.config.beforeController) {
279
+ try {
280
+ this.config.beforeController(cob, cfiles[k], app);
281
+ } catch (err) {
282
+ outWarning(`beforeController: ${err.message}`);
283
+ }
284
+ }
285
+
286
+ this.setRouter(app, cob, cfiles[k]);
287
+
288
+ if (this.config.afterController) {
289
+ try {
290
+ this.config.afterController(cob, cfiles[k], app);
291
+ } catch (err) {
292
+ outWarning(`afterController: ${err.message}`);
293
+ }
294
+ }
295
+
296
+ cob = null;
297
+ } catch (err) {
298
+ outWarning(`load router file : ${k}\x1b[0m\n \x1b[1;33m${err.message}\n\x1b[1;36m${err.stack || ''}`,
299
+ 'Error', '\x1b[1;35m');
300
+ }
301
+ }
302
+
303
+ this.optionsCacheLog = null;
304
+
305
+ return cfiles;
306
+ }
307
+
308
+ _autoAddOptions(app, options_path, group) {
309
+ app.router.apiTable.OPTIONS[options_path] === undefined
310
+ &&
311
+ app.router.options(options_path, async c => {}, {group: group});
312
+ }
313
+
314
+ setRouter(app, cob, cf) {
315
+ if (cob.mode === undefined) {
316
+ cob.mode = 'restful';
317
+ }
318
+
319
+ let group = cf.dirgroup;
320
+
321
+ if (this.config.fileAsGroup) group = cf.filegroup;
322
+
323
+ let npre = cf.filegroup;
324
+ let prepath = this.config.prePath;
325
+
326
+ let route_path = `${prepath}${cf.filegroup}`;
327
+
328
+ npre = `${prepath}${npre}`;
329
+ group = `${prepath}${group}`;
330
+ //用于在fileAsGroup模式添加options路由。
331
+ let dirgroup = `${prepath}${cf.dirgroup}`;
332
+
333
+ let routeParam = '/:id';
334
+
335
+ if (cob.param !== undefined && cob.param !== null && typeof cob.param === 'string') {
336
+ routeParam = cob.param.trim()
337
+ .replace(/\s+/g, '')
338
+ .replace(/\/{2,}/g, '/');
339
+ if (routeParam.length > 0 && routeParam[0] !== '/') {
340
+ routeParam = `/${routeParam}`;
341
+ }
342
+ }
343
+
344
+ Object.defineProperty(cob, '__route__', {
345
+ configurable: false,
346
+ writable: false,
347
+ enumerable: false,
348
+ value: route_path
349
+ });
350
+
351
+ if (cob.post !== undefined && typeof cob.post === 'function') {
352
+ let postParam = (cob.postParam && typeof cob.postParam === 'string') ? cob.postParam : '';
353
+
354
+ postParam = postParam.replace(/\/+/g, '/');
355
+
356
+ if (postParam === '/') postParam = '';
357
+
358
+ if (postParam.length > 0 && postParam[0] !== '/') {
359
+ postParam = `/${postParam}`;
360
+ }
361
+
362
+ app.router.post(`${route_path}${postParam}`, cob.post.bind(cob),
363
+ {
364
+ name: `${npre}/${this.methodNumber.post}`,
365
+ group: group
366
+ }
367
+ );
368
+
369
+ }
370
+
371
+ let real_delete_method = '';
372
+ if (cob.delete && typeof cob.delete === 'function') {
373
+ real_delete_method = 'delete';
374
+ } else if (cob._delete && typeof cob._delete === 'function') {
375
+ real_delete_method = '_delete';
376
+ }
377
+
378
+ if (real_delete_method) {
379
+ app.router.delete(`${route_path}${routeParam}`,
380
+ cob[real_delete_method].bind(cob),
381
+ {
382
+ name: `${npre}/${this.methodNumber.delete}`,
383
+ group: group
384
+ }
385
+ );
386
+ }
387
+
388
+ if (cob.put !== undefined && typeof cob.put === 'function') {
389
+ app.router.put(`${route_path}${routeParam}`, cob.put.bind(cob),
390
+ {
391
+ name: `${npre}/${this.methodNumber.put}`,
392
+ group: group
393
+ }
394
+ );
395
+ }
396
+
397
+ if (cob.get !== undefined && typeof cob.get === 'function') {
398
+ app.router.get(`${route_path}${routeParam}`, cob.get.bind(cob),
399
+ {
400
+ name: `${npre}/${this.methodNumber.get}`,
401
+ group: group
402
+ }
403
+ );
404
+ //主页只支持GET请求
405
+ if (this.config.homeFile === cf.pathname) {
406
+ app.router.get('/', cob.get.bind(cob), {
407
+ name: 'home',
408
+ group: group
409
+ });
410
+ }
411
+ }
412
+
413
+ if (cob.list !== undefined && typeof cob.list === 'function') {
414
+ let listParam = (cob.listParam && typeof cob.listParam === 'string') ? cob.listParam : '';
415
+
416
+ listParam = listParam.replace(/\/+/g, '/');
417
+
418
+ if (listParam === '/') listParam = '';
419
+
420
+ if (listParam.length > 0 && listParam[0] !== '/') {
421
+ listParam = `/${listParam}`;
422
+ }
423
+
424
+ app.router.get(`${route_path}${listParam}`, cob.list.bind(cob),{
425
+ name: `${npre}/${this.methodNumber.list}`,
426
+ group: group
427
+ });
428
+ }
429
+
430
+ if (cob.patch !== undefined && typeof cob.patch === 'function') {
431
+ app.router.patch(`${route_path}${routeParam}`, cob.patch.bind(cob),{
432
+ name: `${npre}/${this.methodNumber.patch}`,
433
+ group: group
434
+ });
435
+ }
436
+
437
+ if (cob.options !== undefined && typeof cob.options === 'function') {
438
+ app.router.options(`${route_path}${routeParam}`, cob.options.bind(cob),{
439
+ name: `${npre}/${this.methodNumber.options}`,
440
+ group: group
441
+ });
442
+ } else if (this.config.optionsRoute) {
443
+ let real_group = this.config.fileAsGroup ? dirgroup : group;
444
+ let tag = this.config.fileAsGroup ? this.groupTag : '';
445
+
446
+ if (real_group === `${this.config.prePath}/`) {
447
+ this._autoAddOptions(app, `${route_path}/*`, tag + real_group);
448
+ } else if (this.optionsCacheLog[real_group] === undefined) {
449
+ this._autoAddOptions(app, `${real_group}/*`, tag + real_group);
450
+ this.optionsCacheLog[real_group] = true;
451
+ }
452
+ }
453
+
454
+ if (cob.head !== undefined && typeof cob.head === 'function') {
455
+ app.router.head(`${route_path}${routeParam}`, cob.head.bind(cob),{
456
+ name: `${npre}/${this.methodNumber.head}`,
457
+ group: group
458
+ });
459
+ }
460
+
461
+ this.fileMidTable[cf.filegroup] = {
462
+ //group已经是带有前缀的。
463
+ group : group,
464
+ dirgroup: cf.dirgroup,
465
+ mid : []
466
+ };
467
+
468
+ if (cob.__mid && typeof cob.__mid === 'function') {
469
+ let mid = cob.__mid();
470
+ if (mid && Array.isArray(mid) ) {
471
+ this.fileMidTable[cf.filegroup].mid = mid;
472
+ }
473
+ }
474
+ }
475
+
476
+ _getGroupList() {
477
+ this.groupList = [`${this.config.prePath}/`];
478
+ this.orgGroupList = ['/'];
479
+
480
+ try {
481
+ let flist = fs.readdirSync(this.config.controllerPath, {withFileTypes: true});
482
+ for (let f of flist) {
483
+ if (!f.isDirectory()) continue;
484
+ if (f.name[0] === '!' || f.name[0] === '.') continue;
485
+
486
+ this.groupList.push(`${this.config.prePath}/${f.name}`);
487
+ this.orgGroupList.push(`/${f.name}`);
488
+ }
489
+ } catch (err) {
490
+ console.error('获取全局所有分组失败,若获取失败会导致程序运行错误,故进程退出,请检查错误。');
491
+ console.error(err);
492
+ process.exit(1);
493
+ }
494
+
495
+ return this.groupList;
496
+ }
497
+
498
+ /**
499
+ * 加载中间件,仅仅是通过一个js文件,
500
+ * 中间件不宜过度使用,否则容易混乱。
501
+ */
502
+ loadMidware(app) {
503
+ if (app.service.__titbit_loader__ && !this.config.multi) {
504
+ outWarning('您已经使用titbit-loader加载过中间件,多个实例容易会导致中间件多次加载或冲突,重复操作将会被终止。');
505
+ outWarning('若有需要,可设置选项multi为true允许多次加载。', ' 提示');
506
+ return false;
507
+ }
508
+
509
+ this._getGroupList();
510
+
511
+ for (let i = 0; i < this.globalMidTable.length; i++) {
512
+ this.loadGlobalMidware(app, this.globalMidTable[i]);
513
+ }
514
+ //加载组,此时组已经确定
515
+ for (let k in this.groupMidTable) {
516
+ for (let i=0; i < this.groupMidTable[k].length; i++) {
517
+ this.loadGroupMidware(app, this.groupMidTable[k][i], k);
518
+ }
519
+ }
520
+
521
+ if (this.config.fileAsGroup && this.config.optionsRoute) {
522
+ for (let g of this.orgGroupList) {
523
+ this._loadMidForFileAsGroup(app, `${this.groupTag}${this.config.prePath}${g}`, g);
524
+ }
525
+ }
526
+
527
+ for (let k in this.fileMidTable) {
528
+ this._loadMidForFileAsGroup(app,
529
+ this.fileMidTable[k].group,
530
+ this.fileMidTable[k].dirgroup);
531
+
532
+ for (let i = 0; i < this.fileMidTable[k].mid.length; i++) {
533
+ this.loadFileMidware(app,
534
+ this.fileMidTable[k].mid[i],
535
+ k,
536
+ this.fileMidTable[k].group,
537
+ this.fileMidTable[k].dirgroup
538
+ );
539
+ }
540
+ }
541
+
542
+ }
543
+
544
+ _loadMidForFileAsGroup(app, group, dirgroup) {
545
+ //此时文件作为分组,从groupCache中取出中间件,并添加到分组。
546
+ if (this.config.fileAsGroup) {
547
+ let tmp_opts;
548
+ for (let g of this.globalCache) {
549
+ tmp_opts = {...g[1]};
550
+ tmp_opts.group = group;
551
+ app.use(g[0], tmp_opts);
552
+ }
553
+
554
+ let ggp = this.groupCache[dirgroup];
555
+ if (ggp && Array.isArray(ggp)) {
556
+ for (let g of ggp) {
557
+ tmp_opts = {...g[1]};
558
+ tmp_opts.group = group;
559
+ app.use(g[0], tmp_opts);
560
+ }
561
+ }
562
+ }
563
+ }
564
+
565
+ checkMiddleware(m) {
566
+ if (m.middleware === undefined) return false;
567
+
568
+ if (typeof m.middleware === 'function' && m.middleware.constructor.name === 'AsyncFunction') {
569
+ return true;
570
+ }
571
+
572
+ if (m.middleware.mid && typeof m.middleware.mid === 'function') {
573
+ return true;
574
+ }
575
+
576
+ if (m.middleware.middleware && typeof m.middleware.middleware === 'function') {
577
+ return true;
578
+ }
579
+
580
+ return false;
581
+ }
582
+
583
+ getMidwareInstance(m) {
584
+ if ( this.checkMiddleware(m) ) {
585
+ return m.middleware;
586
+ }
587
+
588
+ if (typeof m.name !== 'string' || m.name.trim() === '') {
589
+ console.error(`--Middleware Error--: less name.`, m);
590
+ return null;
591
+ }
592
+
593
+ let mt = null;
594
+ let tmp = null;
595
+ if (m.name[0] == '@') {
596
+ tmp = require(this.config.midwarePath+'/'+m.name.substring(1));
597
+ if (m.args === undefined) {
598
+ mt = new tmp();
599
+ } else {
600
+ mt = new tmp(m.args);
601
+ }
602
+
603
+ if (mt.middleware && typeof mt.middleware === 'function') {
604
+ return mt.middleware.bind(mt);
605
+ } else if (mt.mid && typeof mt.mid === 'function') {
606
+ return mt.mid()
607
+ }
608
+ } else {
609
+ mt = require(this.config.midwarePath+'/'+m.name);
610
+ }
611
+ return mt;
612
+ }
613
+
614
+ _checkMidwareMode(app, m) {
615
+ if (m.mode !== undefined) {
616
+ if (m.mode === 'test' || m.mode === 'dev') {
617
+ if (app.service.TEST || app.service.DEV) {
618
+ return true;
619
+ }
620
+ return false;
621
+ } else if (m.mode === 'online' || m.mode === 'product') {
622
+ //只在正式环境加载
623
+ if (app.service.TEST || app.service.DEV) {
624
+ //console.log(`测试环境不加载中间件`, m);
625
+ return false;
626
+ }
627
+ return true;
628
+ }
629
+ }
630
+ //console.log('加载···', m);
631
+ return true;
632
+ }
633
+
634
+ loadGlobalMidware(app, m) {
635
+ //检测是否是开发环境,并确定是否加载中间件。
636
+ if (this._checkMidwareMode(app, m) === false) {
637
+ return;
638
+ }
639
+
640
+ let opts = null;
641
+
642
+ let makeOpts = (groupname = null) => {
643
+ let op = {};
644
+ if (m.method !== undefined) {
645
+ op.method = m.method;
646
+ }
647
+ if (groupname) {
648
+ op.group = groupname[0] === '/' ? groupname : `/${groupname}`;
649
+ }
650
+
651
+ if (m.pre) {
652
+ op.pre = true;
653
+ }
654
+ return op;
655
+ };
656
+
657
+ let mobj;
658
+
659
+ let group = this.groupList;
660
+
661
+ if (group) {
662
+ mobj = this.getMidwareInstance(m);
663
+ if (this.config.fileAsGroup) {
664
+ mobj && this.globalCache.push([mobj, makeOpts()]);
665
+ return;
666
+ }
667
+
668
+ for (let g of group) {
669
+ mobj && app.use(mobj, makeOpts(g));
670
+ }
671
+
672
+ return;
673
+ }
674
+
675
+ }
676
+
677
+ loadGroupMidware(app, m, group) {
678
+
679
+ if (this._checkMidwareMode(app, m) === false) {
680
+ return;
681
+ }
682
+
683
+ if ((!m.name || m.name === '') && !m.middleware) {
684
+ return;
685
+ }
686
+ let opts = {
687
+ group: `${this.config.prePath}${group}`,
688
+ };
689
+ if (m.method !== undefined) {
690
+ opts.method = m.method;
691
+ }
692
+ if (m.pre) {
693
+ opts.pre = true;
694
+ }
695
+
696
+ let mobj = this.getMidwareInstance(m);
697
+ if (mobj) {
698
+ if (!this.config.fileAsGroup) {
699
+ app.use(mobj, opts);
700
+ } else {
701
+ if (!this.groupCache[group]) { this.groupCache[group] = [[mobj, opts]]; }
702
+ else { this.groupCache[group].push([mobj, opts]); }
703
+ }
704
+ }
705
+ }
706
+
707
+ /**
708
+ *
709
+ * @param {object} app
710
+ * @param {object} m
711
+ * @param {string} f
712
+ * @param {string} group 包括前缀的分组,若开启fileAsGroup则为filegroup
713
+ * @param {string} dirgroup 文件名,不包括前缀
714
+ * @returns
715
+ */
716
+ loadFileMidware(app, m, f, group, dirgroup) {
717
+ if (this._checkMidwareMode(app, m) === false) {
718
+ return;
719
+ }
720
+
721
+ //group已经带有prepath前缀。
722
+ let opts = { group };
723
+
724
+ f = `${this.config.prePath}${f}`;
725
+
726
+ if (!this.fileAsGroup && m.path === undefined) {
727
+ m.path = [
728
+ 'get', 'list', 'post', 'put', 'delete',
729
+ 'options', 'patch', 'head', 'trace'
730
+ ];
731
+ }
732
+
733
+ if (m.path && typeof m.path === 'string') {
734
+ m.path = [ m.path ];
735
+ }
736
+
737
+ if (m.path && Array.isArray(m.path)) {
738
+ opts.name = [];
739
+ let path_num;
740
+ for (let p of m.path) {
741
+ path_num = this.methodNumber[p.toLowerCase()];
742
+ if (path_num === undefined) continue;
743
+
744
+ opts.name.push(`${f}/${path_num}`);
745
+ }
746
+ }
747
+
748
+ if (m.pre) {
749
+ opts.pre = true;
750
+ }
751
+
752
+ let mobj = this.getMidwareInstance(m);
753
+ if (mobj) {
754
+ app.use(mobj, opts);
755
+ }
756
+ }
757
+
758
+ loadModelMap(app) {
759
+ if (typeof this.mdbMap !== 'object')
760
+ throw new Error('mdbMap必须是object类型。');
761
+
762
+ let defpath = this.config.modelPath;
763
+ let sk;
764
+ let curpath;
765
+ let obj;
766
+
767
+ let loadObj = (serv, odb, fpath) => {
768
+ let mlist = getModelFiles(fpath);
769
+ let mname;
770
+ mlist.forEach(m => {
771
+ mname = m.substring(0, m.length - 3);
772
+ serv[mname] = this.getModelInstance(fpath + '/' + m, odb);
773
+ });
774
+ };
775
+ for (let k in this.mdbMap) {
776
+ obj = this.mdbMap[k];
777
+ if (!obj || typeof obj !== 'object') continue;
778
+
779
+ sk = '@' + k;
780
+ if (!app.service[sk]) app.service[sk] = {};
781
+ curpath = obj.path || defpath;
782
+
783
+ loadObj(app.service[sk], obj.mdb || null, curpath);
784
+ }
785
+
786
+ }
787
+
788
+ defineServiceFunction(app) {
789
+ if (app.service.getModel) return;
790
+
791
+ let sfe = function (mk, mpre, name, key = '') {
792
+ let m;
793
+ if (!key) {
794
+ m = mk ? this[mk] : this;
795
+ let oo = m[mpre + name];
796
+ if (!oo) throw new Error(`无法获取模型实例${name}`);
797
+ return oo;
798
+ }
799
+
800
+ let k = '@' + key;
801
+ if (!this[k] || !this[k][name]) throw new Error(`无法获取模型实例${name}`);
802
+ return this[k][name];
803
+ };
804
+
805
+ app.service.getModel = sfe.bind(app.service, this.config.mname, this.config.modelNamePre);
806
+
807
+ let fm = function (key) {
808
+ let k = '@' + key;
809
+ if (!this[k]) throw new Error('无法获取服务容器 ' + key);
810
+ return this[k];
811
+ };
812
+
813
+ app.service.modelMap = fm.bind(app.service);
814
+
815
+ Object.defineProperties(app.service, {
816
+ __prepath__: {
817
+ value: this.config.prePath,
818
+ configurable: false,
819
+ writable: false,
820
+ enumerable: false
821
+ },
822
+
823
+ __appdir__: {
824
+ value: this.config.appPath,
825
+ configurable: false,
826
+ writable: false,
827
+ enumerable: false
828
+ }
829
+ });
830
+
831
+ }
832
+
833
+ bindModelAttr(app) {
834
+ if (this.config.mname) {
835
+ if (app.service[this.config.mname] === undefined) app.service[this.config.mname] = {};
836
+ app.service.__model__ = app.service[this.config.mname];
837
+ }
838
+ }
839
+
840
+ /**
841
+ * 加载数据库操作接口,一个表要对应一个js文件,
842
+ */
843
+ loadModel(app) {
844
+ if (this.config.mname && app.service[this.config.mname] === undefined) {
845
+ app.service[this.config.mname] = {};
846
+ }
847
+
848
+ this.bindModelAttr(app);
849
+
850
+ try {
851
+
852
+ let mlist = getModelFiles(this.config.modelPath);
853
+
854
+ mlist.forEach( m => {
855
+ this.requireModel(app, m);
856
+ });
857
+
858
+ } catch (err) {
859
+ console.error(err);
860
+ }
861
+ }
862
+
863
+ getModelInstance(mfile, db) {
864
+ let m = require(mfile);
865
+
866
+ // Arrow Function has no prototype
867
+ if (typeof m !== 'function' || m.prototype === undefined) {
868
+ if (m.init && typeof m.init === 'function') {
869
+ m.init(db);
870
+ }
871
+
872
+ return m;
873
+ }
874
+
875
+ return db ? new m(db) : new m();
876
+ }
877
+
878
+ requireModel(app, mfile) {
879
+ try {
880
+ let mobj = this.getModelInstance(this.config.modelPath + '/' + mfile, this.mdb);
881
+
882
+ let mname = mfile.substring(0, mfile.length-3);
883
+
884
+ mname = `${this.config.modelNamePre}${mname}`;
885
+
886
+ if (!this.config.mname) {
887
+ if (app.service[mname] !== undefined) {
888
+ outWarning(`model 冲突 ---- ${mfile}{${mname}} 已经设置。`);
889
+ return false;
890
+ }
891
+ app.service[mname] = mobj;
892
+ } else {
893
+ if (app.service[this.config.mname][mname] !== undefined) {
894
+ outWarning(`model 冲突 ---- ${mfile}{${mname}} 已经设置。`);
895
+ return false;
896
+ }
897
+ app.service[this.config.mname][mname] = mobj;
898
+ }
899
+ } catch (err) {
900
+ console.error(err.message, ' -- ', mfile);
901
+ return false;
902
+ }
903
+
904
+ return true;
905
+ }
906
+
907
+ stripExtName(filename) {
908
+ let sf = filename.split('.js');
909
+ return `${sf[0]}`;
910
+ }
911
+
912
+ /**
913
+ * 读取控制器目录中的文件
914
+ * @param {string} cdir
915
+ * @param {object} cfiles
916
+ * @param {number} deep
917
+ * @param {string} dirgroup
918
+ */
919
+ readControllers(cdir, cfiles, deep = 0, dirgroup = '') {
920
+ let files = fs.readdirSync(cdir, {withFileTypes:true});
921
+
922
+ let tmp = '';
923
+ for (let i = 0; i < files.length; i++) {
924
+
925
+ if (files[i].isDirectory() && deep < 1) {
926
+
927
+ if (files[i].name[0] == '!') {
928
+ continue;
929
+ }
930
+
931
+ //检测是否启用了分组控制
932
+ //这时候,只有在subgroup之内的才会去加载
933
+ if (this.config.subgroup instanceof Array) {
934
+ if (this.config.subgroup.indexOf(files[i].name) < 0) {
935
+ continue;
936
+ }
937
+ }
938
+
939
+ if (this.routepreg.test(files[i].name) === false) {
940
+ outWarning(`${files[i].name}/ ${nameErrorInfo}`, 'Error');
941
+ continue;
942
+ }
943
+
944
+ this.readControllers(cdir+'/'+files[i].name,
945
+ cfiles, deep+1,
946
+ `${dirgroup}/${files[i].name}`
947
+ );
948
+
949
+ } else if (files[i].isFile()) {
950
+ if (files[i].name[0] === '!') {
951
+ continue;
952
+ }
953
+
954
+ if (files[i].name.length < 4) {
955
+ continue;
956
+ }
957
+
958
+ if (files[i].name.substring(files[i].name.length-3) !== '.js') {
959
+ continue;
960
+ }
961
+
962
+ if (this.config.subgroup instanceof Array && deep < 1) {
963
+ if (this.config.subgroup.indexOf('') < 0 && this.config.subgroup.indexOf('/') < 0) {
964
+ continue;
965
+ }
966
+ }
967
+
968
+ if (files[i].name == '__mid.js') {
969
+ //顶层并且忽略全局中间件选项为false则加载全局中间件。
970
+ if (deep == 0) {
971
+ this.globalMidTable = require(cdir+'/'+files[i].name);
972
+ } else {
973
+ this.groupMidTable[dirgroup] = require(cdir+'/'+files[i].name);
974
+ }
975
+ continue;
976
+ }
977
+
978
+ tmp = this.stripExtName(files[i].name);
979
+
980
+ if (this.routepreg.test(tmp) === false) {
981
+ outWarning(`${files[i].name} ${nameErrorInfo}`, 'Error');
982
+ continue;
983
+ }
984
+
985
+ cfiles[cdir+'/'+files[i].name] = {
986
+ filegroup: dirgroup + '/' + tmp,
987
+ dirgroup: dirgroup || '/',
988
+ name: files[i].name,
989
+ modname: tmp,
990
+ pathname : `${dirgroup}${dirgroup ? '/' : ''}${files[i].name}`
991
+ };
992
+ }
993
+ }
994
+
995
+ }
996
+
997
+ }
998
+
999
+ module.exports = TopbitLoader;