mm_os 4.2.6 → 4.2.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 (74) hide show
  1. package/README.md +1 -1
  2. package/README_EN.md +2 -2
  3. package/adapter/adapter.js +105 -52
  4. package/adapter/custom_persistence.js +32 -31
  5. package/adapter/mqtt.js +59 -79
  6. package/adapter/socket.js +188 -58
  7. package/adapter/web.js +92 -68
  8. package/adapter/websocket.js +43 -146
  9. package/com/api/drive.js +732 -708
  10. package/com/api/index.js +196 -198
  11. package/com/cmd/config.tpl.json +48 -122
  12. package/com/cmd/drive.js +176 -1548
  13. package/com/cmd/index.js +287 -1066
  14. package/com/cmd/job/config.json +66 -0
  15. package/com/cmd/job/index.js +241 -0
  16. package/com/cmd/job/test.js +188 -0
  17. package/com/cmd/script.tpl.js +12 -33
  18. package/com/db/drive.js +2206 -2019
  19. package/com/db/index.js +241 -242
  20. package/com/event/config.tpl.json +3 -1
  21. package/com/event/drive.js +3 -1
  22. package/com/event/index.js +73 -49
  23. package/com/event/script.tpl.js +1 -1
  24. package/com/mqtt/config.tpl.json +16 -8
  25. package/com/mqtt/drive.js +696 -676
  26. package/com/mqtt/index.js +1012 -822
  27. package/com/mqtt/index.test.js +503 -0
  28. package/com/mqtt/mm_mqtt.js +446 -420
  29. package/com/mqtt/script.tpl.js +3 -7
  30. package/com/nav/drive.js +7 -7
  31. package/com/nav/index.js +228 -231
  32. package/com/param/index.js +19 -17
  33. package/com/pendant/index.js +467 -441
  34. package/com/socket/drive.js +679 -616
  35. package/com/socket/index.js +94 -48
  36. package/com/sql/drive.js +1283 -1259
  37. package/com/sql/index.js +152 -150
  38. package/com/static/drive.js +1 -1
  39. package/com/static/index.js +232 -226
  40. package/com/task/drive.js +428 -405
  41. package/com/task/index.js +154 -148
  42. package/com/template/drive.js +1 -1
  43. package/com/template/index.js +141 -138
  44. package/com.js +87 -6
  45. package/common/middleware/web_after/index.js +4 -10
  46. package/common/middleware/web_base/index.js +1 -1
  47. package/common/middleware/web_cors/index.js +1 -1
  48. package/common/middleware/web_error/index.js +131 -119
  49. package/common/middleware/web_error/middleware.json +2 -1
  50. package/common/middleware/web_logger/index.js +1 -1
  51. package/common/middleware/web_main/index.js +2 -0
  52. package/common/middleware/web_static/index.js +2 -2
  53. package/common/middleware/web_waf/index.js +1 -1
  54. package/core/app/config.tpl.json +4 -4
  55. package/core/app/index.js +710 -534
  56. package/core/middleware/index.js +1 -9
  57. package/core/mod/config.tpl.json +1 -1
  58. package/core/model/config.tpl.json +1 -1
  59. package/core/plugin/config.tpl.json +15 -3
  60. package/core/plugin/index.js +31 -0
  61. package/core/pusher/index.js +158 -161
  62. package/core/scene/loop.js +1306 -1255
  63. package/core/store/index.js +1794 -1755
  64. package/core/store/sql.js +1488 -1464
  65. package/core/zone/bat/index.js +744 -728
  66. package/core/zone/config.tpl.json +3 -3
  67. package/core/zone/index.js +655 -616
  68. package/os.js +0 -4
  69. package/package.json +16 -16
  70. package/server.js +583 -427
  71. package/com/cmd/nlp.js +0 -525
  72. package/core/app/index copy.js +0 -611
  73. package/software.js +0 -194
  74. package/tcp.js +0 -279
package/com/api/drive.js CHANGED
@@ -1,708 +1,732 @@
1
- const fs = require('fs');
2
- const Item = require('mm_machine').Drive;
3
- const Param = require('../param/drive.js'); // 是MM自带的参数机制,可以不使用
4
- const Sql = require('../sql/drive.js'); // 是MM自带的参数机制,可以不使用
5
- const Oauth = require('./oauth.js'); // 是MM自带的身份验证机制,基于Oauth2.0,可以不使用
6
-
7
- if (!$.dict) {
8
- if (!$.dict.session_id) {
9
- $.dict.session_id = 'mm:uuid';
10
- }
11
- }
12
-
13
- /**
14
- * Api接口驱动类
15
- * @augments {Item}
16
- * @class
17
- */
18
- class Drive extends Item {
19
- static config = {
20
- // 应用名称 例如:demo_app
21
- 'app': 'server',
22
- // 插件名称 例如:demo_plugin
23
- 'plugin': 'sys',
24
- // 路径 例如:/demo
25
- 'path': '',
26
- // 接口类型,api表示服务端数据类型,web表示网页类型,app表示第三方应用类型
27
- 'type': 'api',
28
- // 请求方式, POST或GET, 为空或ALL表示都可以
29
- 'method': 'ALL',
30
- // 缓存时长 (单位:秒) 默认:60秒,建议600秒
31
- 'cache': 0,
32
- // 是否用客户端缓存,即 http 304 状态
33
- 'client_cache': false,
34
- // param参数验证路径 例如: ./param.json
35
- 'param_path': '',
36
- // sql模板路径 例如: ./sql.json
37
- 'sql_path': '',
38
- // 是否验证参数
39
- 'check_param': true,
40
- // oauth身份验证模型, 参考oauth文件
41
- 'oauth': {
42
- 'scope': true,
43
- 'sign_in': false,
44
- 'vip': 0,
45
- 'gm': 0,
46
- 'mc': 0,
47
- 'user_admin': [],
48
- 'user_group': []
49
- }
50
- };
51
- /**
52
- * 构造函数
53
- * @param {object} config 配置项
54
- * @param {object} parent 父对象
55
- * @class
56
- */
57
- constructor(config, parent) {
58
- super({ ...Drive.config, ...config }, parent);
59
-
60
- /* 通用项 */
61
- // 默认启用热更新
62
- this.mode = 3;
63
- }
64
- }
65
-
66
- /**
67
- * 预设配置
68
- */
69
- Drive.prototype._preset = function () {
70
- this.save_dir = './static/file/';
71
- this.url_path = '/file/';
72
-
73
- // oauth身份验证配置 + 函数
74
- this.oauth = null;
75
- // param参数配置 + 函数
76
- this.param = null;
77
- // sql模板配置 + 函数
78
- this.sql = null;
79
-
80
- var cg = this.config;
81
- this._loadParam(cg.param_path);
82
- this._loadSql(cg.sql_path);
83
- this._loadOauth();
84
- };
85
-
86
- /**
87
- * 设置参数对象
88
- * @param {object} param 参数对象
89
- */
90
- Drive.prototype.updateParam = function (param) {
91
- if ($.param) {
92
- var lt = $.param.list;
93
- if (lt) {
94
- var has = false;
95
- var len = lt.length;
96
- for (var i = 0; i < len; i++) {
97
- var o = lt[i];
98
- if (param.config_file === o.config_file) {
99
- $.param.list[i] = param;
100
- has = true;
101
- break;
102
- }
103
- }
104
- if (!has) {
105
- $.param.list.push(param);
106
- }
107
- }
108
- }
109
- };
110
-
111
- /**
112
- * 获取现有参数
113
- * @param {string} file 文件名
114
- * @returns {object} 获取的对象
115
- */
116
- Drive.prototype.getParam = function (file) {
117
- if ($.param) {
118
- var obj = $.param.list.getObj({
119
- config_file: file
120
- });
121
- if (obj) {
122
- return obj;
123
- }
124
- }
125
- return null;
126
- };
127
-
128
- /**
129
- * 加载参数
130
- * @param {string} file_path 文件路径
131
- */
132
- Drive.prototype._loadParam = async function (file_path) {
133
- if (file_path) {
134
- var p = file_path.fullname(this.getDir());
135
- var param = this.getParam(p);
136
- if (param) {
137
- this.param = param;
138
- } else {
139
- this.param = new Param({}, this);
140
- await this.param.call('loadConfig', p);
141
- this.updateParam(this.param);
142
- this.param.do('load');
143
- }
144
- }
145
- };
146
-
147
- /**
148
- * 设置Sql
149
- * @param {object} sql sql对象
150
- * @param {string} sql.config_file 文件名
151
- * @param {object} sql.sql sql语句
152
- */
153
- Drive.prototype.updateSql = function (sql) {
154
- if ($.sql) {
155
- var lt = $.sql.list;
156
- if (lt) {
157
- var has = false;
158
- var len = lt.length;
159
- for (var i = 0; i < len; i++) {
160
- var o = lt[i];
161
- if (sql.config_file === o.config_file) {
162
- $.sql.list[i] = sql;
163
- has = true;
164
- break;
165
- }
166
- }
167
- if (!has) {
168
- $.sql.list.push(sql);
169
- }
170
- }
171
- }
172
- };
173
-
174
- /**
175
- * 获取现有sql模板
176
- * @param {string} file 文件名
177
- * @returns {object} 获取的对象
178
- */
179
- Drive.prototype.getSql = function (file) {
180
- if ($.sql) {
181
- var lt = $.sql.list;
182
- if (lt) {
183
- var obj = lt.getObj({
184
- config_file: file
185
- });
186
- if (obj) {
187
- return obj;
188
- }
189
- }
190
- }
191
- return null;
192
- };
193
-
194
- /**
195
- * 加载sql模板
196
- * @param {string} file_path 文件路径
197
- */
198
- Drive.prototype._loadSql = async function (file_path) {
199
- if (file_path) {
200
- var p = file_path.fullname(this.getDir());
201
- var sql = this.getSql(p);
202
- if (sql) {
203
- this.sql = sql;
204
- } else {
205
- this.sql = new Sql({}, this);
206
- await this.sql.call('loadConfig', p);
207
- this.updateSql(this.sql);
208
- this.sql.call('loadScript');
209
- }
210
- }
211
- };
212
-
213
- /**
214
- * 加载身份验证配置
215
- */
216
- Drive.prototype._loadOauth = function () {
217
- var cg = this.config.oauth;
218
- if (cg) {
219
- this.oauth = new Oauth(this.getDir());
220
- this.oauth.loadObj(cg);
221
- }
222
- };
223
-
224
- /**
225
- * 保存文件
226
- * @param {object} files 上传的文件
227
- * @param {string} files.file 上传的文件对象
228
- * @returns {object} 返回文件路径和URL
229
- */
230
- Drive.prototype.saveFile = function (files) {
231
- var file;
232
- var url;
233
- if (files.file) {
234
- var f = files.file;
235
- // 创建可读流
236
- var name = f.name || f.originalFilename;
237
- file = ('./' + name).fullname(this.save_dir);
238
- file.addDir();
239
- // 创建写入流
240
- var read_stream = fs.createReadStream(f.filepath);
241
-
242
- try {
243
- let write_stream = fs.createWriteStream(file);
244
- read_stream.pipe(write_stream);
245
- url = this.url_path + name;
246
- } catch (err) {
247
- this.log('error', '保存文件失败', err);
248
- }
249
- }
250
- return {
251
- file,
252
- url
253
- };
254
- };
255
-
256
- /* 回调函数集 */
257
- $.getState = async function (db, token) {
258
- var db1 = db.new('user_state', 'user_id');
259
- var state = await db1.getObj({
260
- token
261
- }, null, null, false);
262
- if (!state) {
263
- return null;
264
- }
265
- var now = new Date();
266
- if (state.time_sign.interval(now, 'day') <= 30) {
267
- db.table = 'user_account';
268
- var user = await db.getObj({
269
- user_id: state.user_id
270
- });
271
- var o = { ...user };
272
- o.password_pay = o.password_pay ? '******' : '';
273
- o.password = o.password ? '******' : '';
274
- delete o.salt;
275
- delete o.time_create;
276
- return {
277
- state,
278
- user: o
279
- };
280
- } else {
281
- return null;
282
- }
283
- };
284
-
285
- /**
286
- * 获取登录态
287
- * @param {object} ctx 请求上下文
288
- * @param {object} db 数据管理器
289
- * @returns {object} 执行结果
290
- */
291
- Drive.prototype.getState = async function (ctx, db) {
292
- var o = await this._getUserFromSession(ctx);
293
- if (!o) {
294
- o = await this._getUserFromToken(ctx, db);
295
- }
296
- return this._processUserInfo(o);
297
- };
298
-
299
- /**
300
- * 从 session 中获取用户信息
301
- * @param {object} ctx 请求上下文
302
- * @returns {object|null} 用户信息
303
- * @private
304
- */
305
- Drive.prototype._getUserFromSession = function (ctx) {
306
- var u = ctx.session?.user;
307
- if (u) {
308
- var o = { ...u };
309
- o.token = ctx.session.uuid;
310
- return o;
311
- }
312
- return null;
313
- };
314
-
315
- /**
316
- * 从 token 中获取用户信息
317
- * @param {object} ctx 请求上下文
318
- * @param {object} db 数据管理器
319
- * @returns {object|null} 用户信息
320
- * @private
321
- */
322
- Drive.prototype._getUserFromToken = async function (ctx, db) {
323
- var token = ctx.headers[$.dict.token];
324
- if (!token) {
325
- return null;
326
- }
327
- var u = await $.cache.get($.dict.session_id + '_' + token);
328
- if (u) {
329
- return await this._processUserFromCache(u, ctx, db, token);
330
- }
331
- return await this._getUserFromDb(token, ctx, db);
332
- };
333
-
334
- /**
335
- * 处理缓存中的用户信息
336
- * @param {object|string} u 缓存中的用户信息
337
- * @param {object} ctx 请求上下文
338
- * @param {object} db 数据管理器
339
- * @param {string} token token
340
- * @returns {object|null} 用户信息
341
- * @private
342
- */
343
- Drive.prototype._processUserFromCache = async function (u, ctx, db, token) {
344
- var o;
345
- if (typeof (u) === 'string') {
346
- o = { ...u.toJson() };
347
- } else if (Object.keys(u).length > 0) {
348
- o = { ...u };
349
- }
350
- if (!o) {
351
- return null;
352
- }
353
- if (!o.user_id) {
354
- return await this._getUserFromDb(token, ctx, db);
355
- }
356
- o.token = token;
357
- return o;
358
- };
359
-
360
- /**
361
- * 从数据库中获取用户信息
362
- * @param {string} token token
363
- * @param {object} ctx 请求上下文
364
- * @param {object} db 数据管理器
365
- * @returns {object|null} 用户信息
366
- * @private
367
- */
368
- Drive.prototype._getUserFromDb = async function (token, ctx, db) {
369
- var obj = await $.getState(db, token);
370
- if (obj) {
371
- var o = { ...obj.user };
372
- ctx.session.user = obj.user;
373
- o.token = ctx.session.uuid;
374
- obj.state.token = o.token;
375
- return o;
376
- }
377
- return null;
378
- };
379
-
380
- /**
381
- * 处理用户信息
382
- * @param {object|null} o 用户信息
383
- * @returns {object|null} 处理后的用户信息
384
- * @private
385
- */
386
- Drive.prototype._processUserInfo = function (o) {
387
- if (o) {
388
- o.password_pay = o.password_pay ? '******' : '';
389
- o.password = o.password ? '******' : '';
390
- delete o.salt;
391
- delete o.time_create;
392
- }
393
- return o;
394
- };
395
-
396
- /**
397
- * 获取登录态
398
- * @param {object} ctx 请求上下文
399
- * @param {object} db 数据管理器
400
- * @returns {object} 执行结果
401
- */
402
- Drive.prototype.before = async function (ctx, db) {
403
- if (this.config.oauth && this.config.oauth.sign_in) {
404
- db.user = await this.getState(ctx, db);
405
- }
406
- };
407
-
408
- /**
409
- * 主要函数
410
- * @param {object} ctx 请求上下文
411
- * @param {object} db 数据管理器
412
- * @returns {object} 执行结果
413
- */
414
- Drive.prototype.main = async function (ctx, db) {
415
- if (this.sql) {
416
- var req = ctx.request;
417
- // 获取文件
418
- if (req.files) {
419
- var fobj = this.saveFile(req.files);
420
- req.body.file = fobj.file;
421
- req.body.url = fobj.url;
422
- }
423
- var ret = await this.sql.run(req.query, req.body, db);
424
- return ret;
425
- } else {
426
- return $.ret.error(10000, '接口函数未定义');
427
- }
428
- };
429
-
430
- /**
431
- * 调用函数
432
- * @param {object} ctx 请求上下文
433
- * @param {object} db 数据管理器
434
- * @returns {object} 执行结果
435
- */
436
- Drive.prototype.run = async function (ctx, db) {
437
- try {
438
- var body = await this.getCache(ctx);
439
- if (!body) {
440
- var req = ctx.request;
441
- var md = this.config['method'].toLocaleUpperCase();
442
- if (md !== req.method && md !== 'ALL') {
443
- return null;
444
- }
445
- await this.before(ctx, db);
446
- var ret = await this.check(ctx, db);
447
- if (!ret) {
448
- try {
449
- ret = await this.main(ctx, db);
450
- } catch (err) {
451
- this.log('error', '脚本文件错误', req.path, err);
452
- ret = $.ret.error(10000, '脚本文件错误:' + err.toString());
453
- }
454
- }
455
- var res = ctx.response;
456
- body = this.body(ret, res, req.type);
457
- this.setCache(ctx, body, res.type);
458
- }
459
- } catch (err) {
460
- this.log('error', '接口错误', ctx.path, err);
461
- }
462
- return body;
463
- };
464
-
465
- /**
466
- * 获取缓存
467
- * @param {object} ctx 请求上下文
468
- * @returns {string} 响应内容
469
- */
470
- Drive.prototype.getCache = async function (ctx) {
471
- var cg = this.config;
472
- if (cg.cache) {
473
- var req = ctx.request;
474
- if (cg.client_cache) {
475
- var etag = req.headers['if-none-match'];
476
- if (etag) {
477
- var stamp = number(etag.replace('"', '').replace('"', ''));
478
- var cha = stamp * 1000 - Date.parse(new Date());
479
- if (cha > 0) {
480
- ctx.response.status = 304;
481
- return ' ';
482
- }
483
- }
484
- } else {
485
- var data = await $.cache.get('api_' + req.url);
486
- if (data) {
487
- var obj = JSON.parse(data);
488
- ctx.response.type = obj.type;
489
- return obj.body;
490
- }
491
- }
492
- }
493
- return null;
494
- };
495
-
496
- /**
497
- * 设置缓存
498
- * @param {object} ctx 请求上下文
499
- * @param {object} body 正文参数
500
- * @param {string} type 响应类型
501
- */
502
- Drive.prototype.setCache = async function (ctx, body, type = null) {
503
- var cg = this.config;
504
- if (cg.cache && ctx.method === 'GET') {
505
- if (cg.client_cache) {
506
- var age = cg.cache * 60;
507
- ctx.set('Cache-Control', 'max-age=' + age);
508
- ctx.etag = Date.parse(new Date()) / 1000 + age;
509
- } else {
510
- var req = ctx.request;
511
- var o = {};
512
- o.body = body;
513
- o.type = ctx.response.type;
514
- $.cache.set('api_' + req.url, JSON.stringify(o), cg.cache * 60);
515
- }
516
- }
517
- };
518
-
519
- /**
520
- * 调用函数
521
- * @param {object} result 设置响应结果
522
- * @param {object} res 响应器
523
- * @param {string} t 请求类型
524
- * @returns {string} 处理后的响应结果
525
- */
526
- Drive.prototype.body = function (result, res, t) {
527
- var ret = result;
528
- var type = res.type;
529
- if (!ret) {
530
- return ret;
531
- }
532
- var tp = typeof (ret);
533
- if (tp === 'object') {
534
- type = this._handleObjectType(res, type, t);
535
- return this._convertObject(ret, type);
536
- } else if (tp === 'string') {
537
- ret = ret.trim();
538
- this._handleStringType(res, type, ret);
539
- }
540
- return ret;
541
- };
542
-
543
- /**
544
- * 处理对象类型
545
- * @param {object} res 响应器
546
- * @param {string} type 类型
547
- * @param {string} t 请求类型
548
- * @returns {string} 处理后的类型
549
- * @private
550
- */
551
- Drive.prototype._handleObjectType = function (res, type, t) {
552
- var new_type = type;
553
- if (!new_type) {
554
- new_type = t.indexOf('xml') !== -1 ? t : 'application/json; charset=utf-8';
555
- res.type = new_type;
556
- } else {
557
- new_type = this._normType(new_type);
558
- res.type = new_type;
559
- }
560
- return new_type;
561
- };
562
-
563
- /**
564
- * 标准化类型
565
- * @param {string} type 类型
566
- * @returns {string} 标准化后的类型
567
- * @private
568
- */
569
- Drive.prototype._normType = function (type) {
570
- if (type.indexOf('json') !== -1) {
571
- return 'application/json; charset=utf-8';
572
- } else if (type.indexOf('html') !== -1) {
573
- return 'text/html';
574
- } else if (type.indexOf('xml') !== -1) {
575
- return 'text/xml; charset=utf-8';
576
- }
577
- return 'text/plain; charset=utf-8';
578
- };
579
-
580
- /**
581
- * 转换对象
582
- * @param {object} obj 对象
583
- * @param {string} type 类型
584
- * @returns {string} 转换后的字符串
585
- * @private
586
- */
587
- Drive.prototype._convertObject = function (obj, type) {
588
- return type.indexOf('/xml') !== -1 ? $.toXml(obj) : JSON.stringify(obj);
589
- };
590
-
591
- /**
592
- * 处理字符串类型
593
- * @param {object} res 响应器
594
- * @param {string} type 类型
595
- * @param {string} str 字符串
596
- * @private
597
- */
598
- Drive.prototype._handleStringType = function (res, type, str) {
599
- if (!type) {
600
- if ((str.startsWith('{') && str.endsWith('}')) || (str.startsWith('[') && str.endsWith(']'))) {
601
- res.type = 'application/json; charset=utf-8';
602
- } else if (str.endsWith('</html>')) {
603
- res.type = 'text/html';
604
- } else if (str.startsWith('<?xml')) {
605
- res.type = 'text/xml; charset=utf-8';
606
- } else {
607
- res.type = 'text/plain; charset=utf-8';
608
- }
609
- }
610
- };
611
-
612
- /**
613
- * 验证参数
614
- * @param {object} query url参数
615
- * @param {object} body 内容参数
616
- * @param {string} method 方法
617
- * @returns {object} 验证结果
618
- */
619
- Drive.prototype.checkParam = function (query, body, method) {
620
- if (this.param) {
621
- var msg = this.param.check(query, body, method);
622
- if (msg) {
623
- var code;
624
- if (msg.indexOf('必须') !== -1 || msg.indexOf('为空') !== -1) {
625
- code = 30001;
626
- } else {
627
- code = 30002;
628
- }
629
- return $.ret.error(code, msg);
630
- }
631
- }
632
- return null;
633
- };
634
-
635
- /**
636
- * 验证参数
637
- * @param {object} ctx 内容上下文
638
- * @returns {object} 验证结果
639
- */
640
- Drive.prototype.check = async function (ctx) {
641
- var error;
642
- if (this.config.check_param) {
643
- var req = ctx.request;
644
- error = this.checkParam(req.query, req.body, req.method);
645
- }
646
- if (!error) {
647
- error = await this.checkOauth(ctx);
648
- if (error) {
649
- error = {
650
- error
651
- };
652
- }
653
- }
654
- return error;
655
- };
656
-
657
- /**
658
- * 验证身份
659
- * @param {object} ctx 内容上下文
660
- * @returns {object} 验证结果
661
- */
662
- Drive.prototype.checkOauth = async function (ctx) {
663
- var aouth = this.oauth;
664
- if (aouth) {
665
- return await aouth.check(ctx);
666
- } else {
667
- return null;
668
- }
669
- };
670
-
671
- /**
672
- * 获取插件
673
- * @param {string} app 应用名称
674
- * @param {string} name 插件名称
675
- * @returns {object} 返回获取到的插件
676
- */
677
- Drive.prototype.plugin = function (app, name) {
678
- var l = $.slash;
679
- var app_name = app || this.config_file.between('app' + l, l);
680
- var plugin_name = name || this.config_file.between('plugin' + l, l);
681
- var plus;
682
- var plugins = $.pool.plugin[app_name];
683
- if (plugins) {
684
- plus = plugins.get(plugin_name);
685
- }
686
- return plus;
687
- };
688
-
689
- /**
690
- * 获取模型
691
- * @param {string} type 模型类型
692
- * @returns {object} 返回获取到的模型
693
- */
694
- Drive.prototype.getModel = function (type) {
695
- let model = { ...this.config };
696
- let dir = this.getDir();
697
- let l = $.slash;
698
- let app_name = dir.between('app' + l, l);
699
- let plugin_name = dir.between('plugin' + l, l);
700
- let name = dir.basename();
701
- model.app = app_name;
702
- model.plugin = plugin_name;
703
- model.name = model.name || name;
704
- model.path = '/' + app_name + '/' + name;
705
- return model;
706
- };
707
-
708
- module.exports = Drive;
1
+ const fs = require('fs');
2
+ const Item = require('mm_machine').Drive;
3
+ const Param = require('../param/drive.js'); // 是MM自带的参数机制,可以不使用
4
+ const Sql = require('../sql/drive.js'); // 是MM自带的参数机制,可以不使用
5
+ const Oauth = require('./oauth.js'); // 是MM自带的身份验证机制,基于Oauth2.0,可以不使用
6
+
7
+ if (!$.dict) {
8
+ if (!$.dict.session_id) {
9
+ $.dict.session_id = 'mm:uuid';
10
+ }
11
+ }
12
+
13
+ /**
14
+ * Api接口驱动类
15
+ * @augments {Item}
16
+ * @class
17
+ */
18
+ class Drive extends Item {
19
+ static config = {
20
+ // 应用名称 例如:demo_app
21
+ app: 'server',
22
+ // 插件名称 例如:demo_plugin
23
+ plugin: 'sys',
24
+ // 路径 例如:/demo
25
+ path: '',
26
+ // 接口类型,api表示服务端数据类型,web表示网页类型,app表示第三方应用类型
27
+ type: 'api',
28
+ // 请求方式, POST或GET, 为空或ALL表示都可以
29
+ method: 'ALL',
30
+ // 缓存时长 (单位:秒) 默认:60秒,建议600秒
31
+ cache: 0,
32
+ // 是否用客户端缓存,即 http 304 状态
33
+ client_cache: false,
34
+ // param参数验证路径 例如: ./param.json
35
+ param_path: '',
36
+ // sql模板路径 例如: ./sql.json
37
+ sql_path: '',
38
+ // 是否验证参数
39
+ check_param: true,
40
+ // oauth身份验证模型, 参考oauth文件
41
+ oauth: {
42
+ scope: true,
43
+ sign_in: false,
44
+ vip: 0,
45
+ gm: 0,
46
+ mc: 0,
47
+ user_admin: [],
48
+ user_group: [],
49
+ },
50
+ };
51
+ /**
52
+ * 构造函数
53
+ * @param {object} config 配置项
54
+ * @param {object} parent 父对象
55
+ * @class
56
+ */
57
+ constructor(config, parent) {
58
+ super({ ...Drive.config, ...config }, parent);
59
+
60
+ /* 通用项 */
61
+ // 默认启用热更新
62
+ this.mode = 3;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * 预设配置
68
+ */
69
+ Drive.prototype._preset = function () {
70
+ this.save_dir = './static/file/';
71
+ this.url_path = '/file/';
72
+
73
+ // oauth身份验证配置 + 函数
74
+ this.oauth = null;
75
+ // param参数配置 + 函数
76
+ this.param = null;
77
+ // sql模板配置 + 函数
78
+ this.sql = null;
79
+
80
+ var cg = this.config;
81
+ this._loadParam(cg.param_path);
82
+ this._loadSql(cg.sql_path);
83
+ this._loadOauth();
84
+ };
85
+
86
+ /**
87
+ * 设置参数对象
88
+ * @param {object} param 参数对象
89
+ */
90
+ Drive.prototype.updateParam = function (param) {
91
+ if ($.param) {
92
+ var lt = $.param.list;
93
+ if (lt) {
94
+ var has = false;
95
+ var len = lt.length;
96
+ for (var i = 0; i < len; i++) {
97
+ var o = lt[i];
98
+ if (param.config_file === o.config_file) {
99
+ $.param.list[i] = param;
100
+ has = true;
101
+ break;
102
+ }
103
+ }
104
+ if (!has) {
105
+ $.param.list.push(param);
106
+ }
107
+ }
108
+ }
109
+ };
110
+
111
+ /**
112
+ * 获取现有参数
113
+ * @param {string} file 文件名
114
+ * @returns {object} 获取的对象
115
+ */
116
+ Drive.prototype.getParam = function (file) {
117
+ if ($.param) {
118
+ var obj = $.param.list.getObj({
119
+ config_file: file,
120
+ });
121
+ if (obj) {
122
+ return obj;
123
+ }
124
+ }
125
+ return null;
126
+ };
127
+
128
+ /**
129
+ * 加载参数
130
+ * @param {string} file_path 文件路径
131
+ */
132
+ Drive.prototype._loadParam = async function (file_path) {
133
+ if (file_path) {
134
+ var p = file_path.fullname(this.getDir());
135
+ var param = this.getParam(p);
136
+ if (param) {
137
+ this.param = param;
138
+ } else {
139
+ this.param = new Param({}, this);
140
+ await this.param.call('loadConfig', p);
141
+ this.updateParam(this.param);
142
+ this.param.do('load');
143
+ }
144
+ }
145
+ };
146
+
147
+ /**
148
+ * 设置Sql
149
+ * @param {object} sql sql对象
150
+ * @param {string} sql.config_file 文件名
151
+ * @param {object} sql.sql sql语句
152
+ */
153
+ Drive.prototype.updateSql = function (sql) {
154
+ if ($.sql) {
155
+ var lt = $.sql.list;
156
+ if (lt) {
157
+ var has = false;
158
+ var len = lt.length;
159
+ for (var i = 0; i < len; i++) {
160
+ var o = lt[i];
161
+ if (sql.config_file === o.config_file) {
162
+ $.sql.list[i] = sql;
163
+ has = true;
164
+ break;
165
+ }
166
+ }
167
+ if (!has) {
168
+ $.sql.list.push(sql);
169
+ }
170
+ }
171
+ }
172
+ };
173
+
174
+ /**
175
+ * 获取现有sql模板
176
+ * @param {string} file 文件名
177
+ * @returns {object} 获取的对象
178
+ */
179
+ Drive.prototype.getSql = function (file) {
180
+ if ($.sql) {
181
+ var lt = $.sql.list;
182
+ if (lt) {
183
+ var obj = lt.getObj({
184
+ config_file: file,
185
+ });
186
+ if (obj) {
187
+ return obj;
188
+ }
189
+ }
190
+ }
191
+ return null;
192
+ };
193
+
194
+ /**
195
+ * 加载sql模板
196
+ * @param {string} file_path 文件路径
197
+ */
198
+ Drive.prototype._loadSql = async function (file_path) {
199
+ if (file_path) {
200
+ var p = file_path.fullname(this.getDir());
201
+ var sql = this.getSql(p);
202
+ if (sql) {
203
+ this.sql = sql;
204
+ } else {
205
+ this.sql = new Sql(
206
+ {
207
+ name: this.config.name,
208
+ },
209
+ this,
210
+ );
211
+ await this.sql.call('loadConfig', p);
212
+ this.updateSql(this.sql);
213
+ this.sql.call('loadScript');
214
+ }
215
+ }
216
+ };
217
+
218
+ /**
219
+ * 加载身份验证配置
220
+ */
221
+ Drive.prototype._loadOauth = function () {
222
+ var cg = this.config.oauth;
223
+ if (cg) {
224
+ this.oauth = new Oauth(this.getDir());
225
+ this.oauth.loadObj(cg);
226
+ }
227
+ };
228
+
229
+ /**
230
+ * 保存文件
231
+ * @param {object} files 上传的文件
232
+ * @param {string} files.file 上传的文件对象
233
+ * @returns {object} 返回文件路径和URL
234
+ */
235
+ Drive.prototype.saveFile = function (files) {
236
+ var file;
237
+ var url;
238
+ if (files.file) {
239
+ var f = files.file;
240
+ // 安全处理文件名: 移除路径穿越字符和特殊字符
241
+ var name = f.name || f.originalFilename;
242
+ name = name.replace(/[\\/]|\.\./g, '_');
243
+ file = ('./' + name).fullname(this.save_dir);
244
+ file.addDir();
245
+ // 创建写入流
246
+ var read_stream = fs.createReadStream(f.filepath);
247
+
248
+ try {
249
+ let write_stream = fs.createWriteStream(file);
250
+ read_stream.pipe(write_stream);
251
+
252
+ read_stream.on('error', (err) => {
253
+ this.log('error', '读取文件流失败', err);
254
+ write_stream.destroy();
255
+ });
256
+ write_stream.on('error', (err) => {
257
+ this.log('error', '写入文件流失败', err);
258
+ read_stream.destroy();
259
+ });
260
+
261
+ url = this.url_path + name;
262
+ } catch (err) {
263
+ this.log('error', '保存文件失败', err);
264
+ }
265
+ }
266
+ return {
267
+ file,
268
+ url,
269
+ };
270
+ };
271
+
272
+ /* 回调函数集 */
273
+ $.getState = async function (db, token) {
274
+ var db1 = db.new('user_state', 'user_id');
275
+ var state = await db1.getObj(
276
+ {
277
+ token,
278
+ },
279
+ null,
280
+ null,
281
+ false,
282
+ );
283
+ if (!state) {
284
+ return null;
285
+ }
286
+ var now = new Date();
287
+ if (state.time_sign.interval(now, 'day') <= 30) {
288
+ db.table = 'user_account';
289
+ var user = await db.getObj({
290
+ user_id: state.user_id,
291
+ });
292
+ var o = { ...user };
293
+ o.password_pay = o.password_pay ? '******' : '';
294
+ o.password = o.password ? '******' : '';
295
+ delete o.salt;
296
+ delete o.time_create;
297
+ return {
298
+ state,
299
+ user: o,
300
+ };
301
+ } else {
302
+ return null;
303
+ }
304
+ };
305
+
306
+ /**
307
+ * 获取登录态
308
+ * @param {object} ctx 请求上下文
309
+ * @param {object} db 数据管理器
310
+ * @returns {object} 执行结果
311
+ */
312
+ Drive.prototype.getState = async function (ctx, db) {
313
+ var o = await this._getUserFromSession(ctx);
314
+ if (!o) {
315
+ o = await this._getUserFromToken(ctx, db);
316
+ }
317
+ return this._processUserInfo(o);
318
+ };
319
+
320
+ /**
321
+ * 从 session 中获取用户信息
322
+ * @param {object} ctx 请求上下文
323
+ * @returns {object|null} 用户信息
324
+ * @private
325
+ */
326
+ Drive.prototype._getUserFromSession = function (ctx) {
327
+ var u = ctx.session?.user;
328
+ if (u) {
329
+ var o = { ...u };
330
+ o.token = ctx.session.uuid;
331
+ return o;
332
+ }
333
+ return null;
334
+ };
335
+
336
+ /**
337
+ * 从 token 中获取用户信息
338
+ * @param {object} ctx 请求上下文
339
+ * @param {object} db 数据管理器
340
+ * @returns {object|null} 用户信息
341
+ * @private
342
+ */
343
+ Drive.prototype._getUserFromToken = async function (ctx, db) {
344
+ var token = ctx.headers[$.dict.token];
345
+ if (!token) {
346
+ return null;
347
+ }
348
+ var u = await $.cache.get($.dict.session_id + '_' + token);
349
+ if (u) {
350
+ return await this._processUserFromCache(u, ctx, db, token);
351
+ }
352
+ return await this._getUserFromDb(token, ctx, db);
353
+ };
354
+
355
+ /**
356
+ * 处理缓存中的用户信息
357
+ * @param {object|string} u 缓存中的用户信息
358
+ * @param {object} ctx 请求上下文
359
+ * @param {object} db 数据管理器
360
+ * @param {string} token token
361
+ * @returns {object|null} 用户信息
362
+ * @private
363
+ */
364
+ Drive.prototype._processUserFromCache = async function (u, ctx, db, token) {
365
+ var o;
366
+ if (typeof u === 'string') {
367
+ o = { ...u.toJson() };
368
+ } else if (Object.keys(u).length > 0) {
369
+ o = { ...u };
370
+ }
371
+ if (!o) {
372
+ return null;
373
+ }
374
+ if (!o.user_id) {
375
+ return await this._getUserFromDb(token, ctx, db);
376
+ }
377
+ o.token = token;
378
+ return o;
379
+ };
380
+
381
+ /**
382
+ * 从数据库中获取用户信息
383
+ * @param {string} token token
384
+ * @param {object} ctx 请求上下文
385
+ * @param {object} db 数据管理器
386
+ * @returns {object|null} 用户信息
387
+ * @private
388
+ */
389
+ Drive.prototype._getUserFromDb = async function (token, ctx, db) {
390
+ var obj = await $.getState(db, token);
391
+ if (obj) {
392
+ var o = { ...obj.user };
393
+ ctx.session.user = obj.user;
394
+ o.token = ctx.session.uuid;
395
+ obj.state.token = o.token;
396
+ return o;
397
+ }
398
+ return null;
399
+ };
400
+
401
+ /**
402
+ * 处理用户信息
403
+ * @param {object|null} o 用户信息
404
+ * @returns {object|null} 处理后的用户信息
405
+ * @private
406
+ */
407
+ Drive.prototype._processUserInfo = function (o) {
408
+ if (o) {
409
+ o.password_pay = o.password_pay ? '******' : '';
410
+ o.password = o.password ? '******' : '';
411
+ delete o.salt;
412
+ delete o.time_create;
413
+ }
414
+ return o;
415
+ };
416
+
417
+ /**
418
+ * 获取登录态
419
+ * @param {object} ctx 请求上下文
420
+ * @param {object} db 数据管理器
421
+ * @returns {object} 执行结果
422
+ */
423
+ Drive.prototype.before = async function (ctx, db) {
424
+ if (this.config.oauth && this.config.oauth.sign_in) {
425
+ db.user = await this.getState(ctx, db);
426
+ }
427
+ };
428
+
429
+ /**
430
+ * 主要函数
431
+ * @param {object} ctx 请求上下文
432
+ * @param {object} db 数据管理器
433
+ * @returns {object} 执行结果
434
+ */
435
+ Drive.prototype.main = async function (ctx, db) {
436
+ if (this.sql) {
437
+ var req = ctx.request;
438
+ // 获取文件
439
+ if (req.files) {
440
+ var fobj = this.saveFile(req.files);
441
+ req.body.file = fobj.file;
442
+ req.body.url = fobj.url;
443
+ }
444
+ var ret = await this.sql.run(req.query, req.body, db);
445
+ return ret;
446
+ } else {
447
+ return $.ret.error(10000, '接口函数未定义');
448
+ }
449
+ };
450
+
451
+ /**
452
+ * 调用函数
453
+ * @param {object} ctx 请求上下文
454
+ * @param {object} db 数据管理器
455
+ * @returns {object} 执行结果
456
+ */
457
+ Drive.prototype.run = async function (ctx, db) {
458
+ try {
459
+ var body = await this.getCache(ctx);
460
+ if (!body) {
461
+ var req = ctx.request;
462
+ var md = this.config['method'].toLocaleUpperCase();
463
+ if (md !== req.method && md !== 'ALL') {
464
+ return null;
465
+ }
466
+ await this.before(ctx, db);
467
+ var ret = await this.check(ctx, db);
468
+ if (!ret) {
469
+ try {
470
+ ret = await this.main(ctx, db);
471
+ } catch (err) {
472
+ this.log('error', '脚本文件错误', req.path, err);
473
+ ret = $.ret.error(10000, '脚本文件错误:' + err.toString());
474
+ }
475
+ }
476
+ var res = ctx.response;
477
+ body = this.body(ret, res, req.type);
478
+ this.setCache(ctx, body, res.type);
479
+ }
480
+ } catch (err) {
481
+ this.log('error', '接口错误', ctx.path, err);
482
+ }
483
+ return body;
484
+ };
485
+
486
+ /**
487
+ * 获取缓存
488
+ * @param {object} ctx 请求上下文
489
+ * @returns {string} 响应内容
490
+ */
491
+ Drive.prototype.getCache = async function (ctx) {
492
+ var cg = this.config;
493
+ if (cg.cache) {
494
+ var req = ctx.request;
495
+ if (cg.client_cache) {
496
+ var etag = req.headers['if-none-match'];
497
+ if (etag) {
498
+ var stamp = number(etag.replace('"', '').replace('"', ''));
499
+ var cha = stamp * 1000 - Date.parse(new Date());
500
+ if (cha > 0) {
501
+ ctx.response.status = 304;
502
+ return ' ';
503
+ }
504
+ }
505
+ } else {
506
+ var data = await $.cache.get('api_' + req.url);
507
+ if (data) {
508
+ var obj = JSON.parse(data);
509
+ ctx.response.type = obj.type;
510
+ return obj.body;
511
+ }
512
+ }
513
+ }
514
+ return null;
515
+ };
516
+
517
+ /**
518
+ * 设置缓存
519
+ * @param {object} ctx 请求上下文
520
+ * @param {object} body 正文参数
521
+ * @param {string} type 响应类型
522
+ */
523
+ Drive.prototype.setCache = async function (ctx, body, type = null) {
524
+ var cg = this.config;
525
+ if (cg.cache && ctx.method === 'GET') {
526
+ if (cg.client_cache) {
527
+ var age = cg.cache * 60;
528
+ ctx.set('Cache-Control', 'max-age=' + age);
529
+ ctx.etag = Date.parse(new Date()) / 1000 + age;
530
+ } else {
531
+ var req = ctx.request;
532
+ var o = {};
533
+ o.body = body;
534
+ o.type = ctx.response.type;
535
+ $.cache.set('api_' + req.url, JSON.stringify(o), cg.cache * 60);
536
+ }
537
+ }
538
+ };
539
+
540
+ /**
541
+ * 调用函数
542
+ * @param {object} result 设置响应结果
543
+ * @param {object} res 响应器
544
+ * @param {string} t 请求类型
545
+ * @returns {string} 处理后的响应结果
546
+ */
547
+ Drive.prototype.body = function (result, res, t) {
548
+ var ret = result;
549
+ var type = res.type;
550
+ if (!ret) {
551
+ return ret;
552
+ }
553
+ var tp = typeof ret;
554
+ if (tp === 'object') {
555
+ type = this._handleObjectType(res, type, t);
556
+ return this._convertObject(ret, type);
557
+ } else if (tp === 'string') {
558
+ ret = ret.trim();
559
+ this._handleStringType(res, type, ret);
560
+ }
561
+ return ret;
562
+ };
563
+
564
+ /**
565
+ * 处理对象类型
566
+ * @param {object} res 响应器
567
+ * @param {string} type 类型
568
+ * @param {string} t 请求类型
569
+ * @returns {string} 处理后的类型
570
+ * @private
571
+ */
572
+ Drive.prototype._handleObjectType = function (res, type, t) {
573
+ var new_type = type;
574
+ if (!new_type) {
575
+ new_type = t.indexOf('xml') !== -1 ? t : 'application/json; charset=utf-8';
576
+ res.type = new_type;
577
+ } else {
578
+ new_type = this._normType(new_type);
579
+ res.type = new_type;
580
+ }
581
+ return new_type;
582
+ };
583
+
584
+ /**
585
+ * 标准化类型
586
+ * @param {string} type 类型
587
+ * @returns {string} 标准化后的类型
588
+ * @private
589
+ */
590
+ Drive.prototype._normType = function (type) {
591
+ if (type.indexOf('json') !== -1) {
592
+ return 'application/json; charset=utf-8';
593
+ } else if (type.indexOf('html') !== -1) {
594
+ return 'text/html';
595
+ } else if (type.indexOf('xml') !== -1) {
596
+ return 'text/xml; charset=utf-8';
597
+ }
598
+ return 'text/plain; charset=utf-8';
599
+ };
600
+
601
+ /**
602
+ * 转换对象
603
+ * @param {object} obj 对象
604
+ * @param {string} type 类型
605
+ * @returns {string} 转换后的字符串
606
+ * @private
607
+ */
608
+ Drive.prototype._convertObject = function (obj, type) {
609
+ return type.indexOf('/xml') !== -1 ? $.toXml(obj) : JSON.stringify(obj);
610
+ };
611
+
612
+ /**
613
+ * 处理字符串类型
614
+ * @param {object} res 响应器
615
+ * @param {string} type 类型
616
+ * @param {string} str 字符串
617
+ * @private
618
+ */
619
+ Drive.prototype._handleStringType = function (res, type, str) {
620
+ if (!type) {
621
+ if (
622
+ (str.startsWith('{') && str.endsWith('}')) ||
623
+ (str.startsWith('[') && str.endsWith(']'))
624
+ ) {
625
+ res.type = 'application/json; charset=utf-8';
626
+ } else if (str.endsWith('</html>')) {
627
+ res.type = 'text/html';
628
+ } else if (str.startsWith('<?xml')) {
629
+ res.type = 'text/xml; charset=utf-8';
630
+ } else {
631
+ res.type = 'text/plain; charset=utf-8';
632
+ }
633
+ }
634
+ };
635
+
636
+ /**
637
+ * 验证参数
638
+ * @param {object} query url参数
639
+ * @param {object} body 内容参数
640
+ * @param {string} method 方法
641
+ * @returns {object} 验证结果
642
+ */
643
+ Drive.prototype.checkParam = function (query, body, method) {
644
+ if (this.param) {
645
+ var msg = this.param.check(query, body, method);
646
+ if (msg) {
647
+ var code;
648
+ if (msg.indexOf('必须') !== -1 || msg.indexOf('为空') !== -1) {
649
+ code = 30001;
650
+ } else {
651
+ code = 30002;
652
+ }
653
+ return $.ret.error(code, msg);
654
+ }
655
+ }
656
+ return null;
657
+ };
658
+
659
+ /**
660
+ * 验证参数
661
+ * @param {object} ctx 内容上下文
662
+ * @returns {object} 验证结果
663
+ */
664
+ Drive.prototype.check = async function (ctx) {
665
+ var error;
666
+ if (this.config.check_param) {
667
+ var req = ctx.request;
668
+ error = this.checkParam(req.query, req.body, req.method);
669
+ }
670
+ if (!error) {
671
+ error = await this.checkOauth(ctx);
672
+ if (error) {
673
+ error = {
674
+ error,
675
+ };
676
+ }
677
+ }
678
+ return error;
679
+ };
680
+
681
+ /**
682
+ * 验证身份
683
+ * @param {object} ctx 内容上下文
684
+ * @returns {object} 验证结果
685
+ */
686
+ Drive.prototype.checkOauth = async function (ctx) {
687
+ var aouth = this.oauth;
688
+ if (aouth) {
689
+ return await aouth.check(ctx);
690
+ } else {
691
+ return null;
692
+ }
693
+ };
694
+
695
+ /**
696
+ * 获取插件
697
+ * @param {string} app 应用名称
698
+ * @param {string} name 插件名称
699
+ * @returns {object} 返回获取到的插件
700
+ */
701
+ Drive.prototype.plugin = function (app, name) {
702
+ var l = $.slash;
703
+ var app_name = app || this.config_file.between('app' + l, l);
704
+ var plugin_name = name || this.config_file.between('plugin' + l, l);
705
+ var plus;
706
+ var plugins = $.pool.plugin[app_name];
707
+ if (plugins) {
708
+ plus = plugins.get(plugin_name);
709
+ }
710
+ return plus;
711
+ };
712
+
713
+ /**
714
+ * 获取模型
715
+ * @param {string} type 模型类型
716
+ * @returns {object} 返回获取到的模型
717
+ */
718
+ Drive.prototype.getModel = function (type) {
719
+ let model = { ...this.config };
720
+ let dir = this.getDir();
721
+ let l = $.slash;
722
+ let app_name = dir.between('app' + l, l);
723
+ let plugin_name = dir.between('plugin' + l, l);
724
+ let name = dir.basename();
725
+ model.app = app_name;
726
+ model.plugin = plugin_name;
727
+ model.name = model.name || name;
728
+ model.path = '/' + app_name + '/' + name;
729
+ return model;
730
+ };
731
+
732
+ module.exports = Drive;