jj.js 0.20.0 → 1.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/db.js CHANGED
@@ -10,6 +10,7 @@ const Context = require('./context');
10
10
  * @typedef {import('../types').OkPacket} OkPacket
11
11
  * @typedef {import('../types').RowData} RowData
12
12
  * @typedef {import('../types').ListData} ListData
13
+ * @typedef {import('../types').PaginateResult} PaginateResult
13
14
  * @typedef {import('../types').FieldInfo} FieldInfo
14
15
  * @typedef {import('../types').Link} Link
15
16
  * @typedef {import('../types').Operator} Operator
@@ -50,10 +51,6 @@ class Db extends Context
50
51
  * @type {string}
51
52
  */
52
53
  this._queryStr = '';
53
- /**
54
- * @type {Map<string, string[]>}
55
- */
56
- this._tableField = new Map();
57
54
  /**
58
55
  * @type {SqlInstance} sql实例
59
56
  */
@@ -64,6 +61,8 @@ class Db extends Context
64
61
  */
65
62
  this.sql = '';
66
63
 
64
+ /** @type {Promise<*>|null} */
65
+ this._connectPromise = null;
67
66
  this.connect(options);
68
67
  }
69
68
 
@@ -99,17 +98,21 @@ class Db extends Context
99
98
  * @returns {this}
100
99
  */
101
100
  connect(config='default') {
102
- this._config = typeof config === 'string' ? cfg_db[config] : config;
101
+ this._config = typeof config === 'string' ? (cfg_db[config] || config) : config;
102
+ if(!this._config || typeof this._config !== 'object') {
103
+ throw new Error(`DbError: 数据库配置 "${config}" 不存在`);
104
+ }
103
105
  this.reset();
104
106
 
105
107
  if(this._config.connect) {
106
108
  this._sql = this._config.connect(this._config);
109
+ this._connectPromise = Promise.resolve(this);
107
110
  } else {
108
111
  const sqltype = this._config.type;
109
112
  /** @class {Sql} */
110
113
  const Sql = require(`./db/${sqltype}.js`);
111
114
  this._sql = new Sql(this.ctx, this._$logger);
112
- this._sql.connect(this._config);
115
+ this._connectPromise = this._sql.connect(this._config).then(() => this);
113
116
  }
114
117
 
115
118
  return this;
@@ -165,6 +168,7 @@ class Db extends Context
165
168
  params !== false && reset !== false && this.reset();
166
169
  params || (params = []);
167
170
 
171
+ await this._connectPromise;
168
172
  return await this._sql.query(sql, params);
169
173
  }
170
174
 
@@ -538,7 +542,7 @@ class Db extends Context
538
542
  * @param {number} [param0.page] - 页码,可通过page方法配置,默认从默认分页类获取
539
543
  * @param {number} [param0.page_size] - 每页行数,可通过page方法配置,默认从config配置获取
540
544
  * @param {PaginationInstance} [param0.pagination] - 分页类实例,为空时使用默认分页类
541
- * @returns {Promise<[ListData, PaginationInstance]>} - [ListData, PaginationInstance]
545
+ * @returns {Promise<PaginateResult>} - [ListData, PaginationInstance]
542
546
  */
543
547
  async paginate({page, page_size, pagination} = {}) {
544
548
  !page && (page = this._options.page.page);
@@ -698,9 +702,10 @@ class Db extends Context
698
702
  const cache_time = this._options.cache_time;
699
703
  const cache_key = md5(this._sql.format(this._queryStr, params));
700
704
  if(this._$cache.get(cache_key)) {
705
+ this.reset();
701
706
  return this._$cache.get(cache_key);
702
707
  }
703
- const result = await this.query(this._queryStr, params);
708
+ const result = await this.query(this._queryStr, params, reset);
704
709
  this._$cache.set(cache_key, result, cache_time);
705
710
  return result;
706
711
  }
@@ -714,21 +719,33 @@ class Db extends Context
714
719
  * @returns {Promise<FieldInfo[]>}
715
720
  */
716
721
  async tableInfo(table) {
717
- const get_sql = this._options.getSql;
718
- this._options.getSql = false;
719
- const table_info = await this.table(table)._sql.tableInfo(this._parseTable());
720
- this._options.getSql = get_sql;
722
+ table && this.table(table);
723
+ const tableName = this._parseTable();
724
+ if(this._options.cache_time) {
725
+ const cache_time = this._options.cache_time;
726
+ const cache_key = 'table_info:' + tableName;
727
+ if(this._$cache.get(cache_key)) {
728
+ this.reset();
729
+ return this._$cache.get(cache_key);
730
+ }
731
+ const result = await this._sql.tableInfo(tableName);
732
+ this._$cache.set(cache_key, result, cache_time);
733
+ return result;
734
+ }
735
+ const table_info = await this._sql.tableInfo(tableName)
736
+ this.reset();
721
737
  return table_info;
722
738
  }
723
739
 
724
740
  /**
725
741
  * 获取数据表字段
726
742
  * @public
727
- * @param {string} [table] - 表名字
743
+ * @param {string} [table] - 表名字,会替换通过table方法传入的表名字
744
+ * @param {number} [cache_time=60] - 缓存时间,默认60秒
728
745
  * @returns {Promise<string[]>}
729
746
  */
730
- async tableField(table) {
731
- const table_info = await this.tableInfo(table);
747
+ async tableField(table, cache_time = 60) {
748
+ const table_info = await this.withCache(cache_time).tableInfo(table);
732
749
  return table_info.map(item => item.Field);
733
750
  }
734
751
 
@@ -796,33 +813,46 @@ class Db extends Context
796
813
  /** @type {any[]} */
797
814
  const params = [];
798
815
  this._options.where.forEach(item => {
799
- /** @type {Object.<string, any>} */
816
+ /** @type {Where} */
800
817
  const whereList = item[0];
801
818
  const whereLink = (item[1] && item[1].toLowerCase()) === 'or' ? 'or' : 'and';
802
819
  let whereChild = '';
803
- Object.keys(whereList).forEach(field => {
804
- const value = whereList[field];
820
+ Object.entries(whereList).forEach(([field, value]) => {
805
821
  if(!~field.indexOf(')')) {
806
822
  field = '`' + field.replace(/\./g, '`.`') + '`';
807
823
  }
824
+ /** @type {Operator} */
808
825
  let fieldLogic = '=';
809
826
  let fieldValue = null;
810
827
  let fieldLink = 'and';
811
828
  if(value instanceof Array) {
812
- value[0] = value[0].toLowerCase();
813
- fieldLogic = ~logic.indexOf(value[0]) ? value[0] : '=';
829
+ // @ts-ignore
830
+ fieldLogic = value[0].toLowerCase();
831
+ if(!~logic.indexOf(fieldLogic)) {
832
+ const message = 'DbError: 无效的where操作符 ' + value[0];
833
+ throw new Error(message);
834
+ }
814
835
  fieldValue = value[1];
815
- fieldLink = (value[2] && value[2].toLowerCase()) === 'or' ? 'or' : 'and';
836
+ fieldLink = (value[2] && value[2].toLowerCase()) === 'or' ? 'or' : 'and'; // Fixed: value[2] to fieldLink
816
837
  } else {
817
838
  fieldValue = value;
818
839
  }
819
840
  if(fieldLogic === 'exp') {
820
841
  whereChild += (whereChild ? ` ${fieldLink} ` : '') + `${fieldValue}`;
842
+ const expData = value[3] || (Array.isArray(value[2]) && value[2]) || (typeof value[2] == 'string' && !['or', 'and'].includes(value[2].toLowerCase()) ? value[2] : undefined);
843
+ if(Array.isArray(expData)) {
844
+ expData.forEach(data => {
845
+ params.push(data);
846
+ });
847
+ } else if(expData !== undefined) {
848
+ params.push(expData);
849
+ }
821
850
  } else {
822
851
  whereChild += (whereChild ? ` ${fieldLink} ` : '') + `${field} ${fieldLogic} `;
823
852
  if(~['in', 'not in'].indexOf(fieldLogic)) {
824
- whereChild += `(?)`;
825
- params.push(fieldValue);
853
+ const inValues = Array.isArray(fieldValue) ? fieldValue : [fieldValue];
854
+ whereChild += `(${inValues.map(() => '?').join(',')})`;
855
+ inValues.forEach(v => params.push(v));
826
856
  } else if(~['between', 'not between'].indexOf(fieldLogic)) {
827
857
  whereChild += `? and ?`;
828
858
  typeof fieldValue === 'string' && (fieldValue = fieldValue.split(','));
@@ -932,11 +962,9 @@ class Db extends Context
932
962
 
933
963
  let allowField = this._options.allowField;
934
964
  if(allowField === true) {
935
- const table_name = this._options.prefix + this._table;
936
- if(!this._tableField.get(table_name)) {
937
- this._tableField.set(table_name, await this.tableField());
938
- }
939
- allowField = this._tableField.get(table_name) || [];
965
+ const _options = this._options;
966
+ allowField = await this.tableField();
967
+ this._options = _options;
940
968
  }
941
969
 
942
970
  Object.keys(this._options.data).forEach(key => {
@@ -997,12 +1025,14 @@ class Db extends Context
997
1025
  * @returns {PaginationInstance}
998
1026
  */
999
1027
  get _$pagination() {
1000
- if(this.$pagination && this.$pagination.__ISCLASS__) {
1001
- this.__pagination = this.$pagination;
1002
- } else if(this.$ && this.$.pagination) {
1003
- this. __pagination = this.$.pagination;
1004
- } else {
1005
- this.__pagination = new (require('./pagination'))(this.ctx);
1028
+ if(this.__pagination === null) {
1029
+ if(this.$pagination && this.$pagination.__ISCLASS__) {
1030
+ this.__pagination = this.$pagination;
1031
+ } else if(this.$ && this.$.pagination) {
1032
+ this.__pagination = this.$.pagination;
1033
+ } else {
1034
+ this.__pagination = new (require('./pagination'))(this.ctx);
1035
+ }
1006
1036
  }
1007
1037
  return this.__pagination;
1008
1038
  }
package/lib/loader.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const pt = require('path');
2
2
  /** @type {function} */
3
3
  // @ts-ignore
4
- const isClass = require('is-class');
4
+ const isClass = require('./utils/is-class');
5
5
  const {isFileSync, isDirSync} = require('./utils/fs');
6
6
 
7
7
  /**
@@ -13,10 +13,42 @@ const {isFileSync, isDirSync} = require('./utils/fs');
13
13
  */
14
14
 
15
15
  /**
16
+ * 节点类型缓存
16
17
  * @type {Map<string, NodeType>}
17
18
  */
18
19
  const typeStore = new Map();
19
20
 
21
+ /**
22
+ * 获取节点类型
23
+ * @param {string} path - 节点路径,最后不带斜杠
24
+ * @returns {NodeType}
25
+ */
26
+ const getNodeType = path => {
27
+ const _nodePath = path + pt.sep;
28
+ const _nodeFile = path + '.js';
29
+ const _nodeJson = path + '.json';
30
+ let _nodeType = typeStore.get(_nodePath);
31
+ if(_nodeType === undefined) {
32
+ _nodeType = isFileSync(_nodeFile) ? 'file' : isFileSync(_nodeJson) ? 'json' : isDirSync(_nodePath) ? 'dir' : '';
33
+ if(_nodeType == 'file' && isClass(require(_nodeFile))) {
34
+ _nodeType = 'class';
35
+ }
36
+ typeStore.set(_nodePath, _nodeType);
37
+ }
38
+ return _nodeType;
39
+ }
40
+
41
+ /**
42
+ *
43
+ * @param {NodeType} type - 节点类型
44
+ * @param {string} path - 节点路径,最后不带斜杠
45
+ * @returns {object} - 节点
46
+ */
47
+ const getNode = (type, path) => {
48
+ if(!type) return undefined;
49
+ return type == 'file' || type == 'class' ? require(path + '.js') : type == 'json' ? require(path + '.json') : {};
50
+ }
51
+
20
52
  /**
21
53
  * nodejs模块(‌CommonJS)自动加载器
22
54
  * @param {string} path 根目录,需为绝对路径,最后不带斜杠
@@ -24,19 +56,15 @@ const typeStore = new Map();
24
56
  * @return {Node}
25
57
  */
26
58
  function loader(path, ...args) {
27
- const dir = pt.normalize(path).replace(/[\/\\]+$/, '');
28
- const dirPath = pt.join(dir, './');
29
- /** @type {NodeType} */
30
- let dirType = isFileSync(dir + '.js') ? 'file' : isFileSync(dir + '.json') ? 'json' : 'dir';
31
- const node = dirType == 'dir' ? {} : require(dir);
32
-
33
- if(dirType == 'file' && isClass(node)) {
34
- dirType = 'class';
35
- }
59
+ const dirPath = pt.normalize(path).replace(/[\/\\]+$/, '');
60
+ const dirType = getNodeType(dirPath);
61
+ if(dirType == '') return undefined;
62
+ const node = getNode(dirType, dirPath);
63
+
36
64
  /** @type {Map<object, NodeInfo>} */
37
65
  const infoStore = new Map();
38
66
  infoStore.set(node, {
39
- path: dirPath,
67
+ path: dirPath + pt.sep,
40
68
  type: dirType
41
69
  });
42
70
  return creatLoader(node);
@@ -54,8 +82,6 @@ function loader(path, ...args) {
54
82
  return Reflect.get(node, prop, receiver);
55
83
  }
56
84
 
57
- /** @type {NodeInfo} */
58
- // @ts-ignore
59
85
  const nodeInfo = infoStore.get(node);
60
86
  if(nodeInfo.type == 'class') {
61
87
  if(!nodeInfo.instance) {
@@ -69,28 +95,13 @@ function loader(path, ...args) {
69
95
  return false;
70
96
  }
71
97
 
72
- const _nodePath = nodeInfo.path + prop + pt.sep;
73
- const _nodeFile = nodeInfo.path + prop + '.js';
74
- const _nodeJson = nodeInfo.path + prop + '.json';
75
- let _nodeType = typeStore.get(_nodePath);
76
- if(_nodeType === undefined) {
77
- _nodeType = isFileSync(_nodeFile) ? 'file' : isFileSync(_nodeJson) ? 'json' : isDirSync(_nodePath) ? 'dir' : '';
78
- if(_nodeType == 'file' && isClass(require(_nodeFile))) {
79
- _nodeType = 'class';
80
- }
81
- typeStore.set(_nodePath, _nodeType);
82
- }
83
- let _node = {};
84
- if(_nodeType == '') {
85
- return undefined; // 直接返回
86
- } else if(_nodeType == 'file' || _nodeType == 'class') {
87
- _node = require(_nodeFile);
88
- } else if(_nodeType == 'json') {
89
- _node = require(_nodeJson);
90
- }
98
+ const _childPath = nodeInfo.path + prop;
99
+ const _childType = getNodeType(_childPath);
100
+ if(_childType == '') return undefined; // 直接返回
101
+ const _node = getNode(_childType, _childPath);
91
102
  infoStore.set(_node, {
92
- path: _nodePath,
93
- type: _nodeType
103
+ path: _childPath + pt.sep,
104
+ type: _childType
94
105
  });
95
106
  // @ts-ignore
96
107
  return node[prop] = creatLoader(_node); // 代理,并缓存
@@ -100,8 +111,6 @@ function loader(path, ...args) {
100
111
  return Reflect.set(node, prop, value, receiver);
101
112
  }
102
113
 
103
- /** @type {NodeInfo} */
104
- // @ts-ignore
105
114
  const nodeInfo = infoStore.get(node);
106
115
  if(nodeInfo.type == 'class') {
107
116
  if(!nodeInfo.instance) {
@@ -112,9 +121,30 @@ function loader(path, ...args) {
112
121
  }
113
122
 
114
123
  return Reflect.set(node, prop, value, receiver);
115
- }
124
+ },
125
+ has: (node, prop) => {
126
+ if(prop in node || typeof prop == 'symbol' || prop == 'inspect') {
127
+ return prop in node;
128
+ }
129
+
130
+ const nodeInfo = infoStore.get(node);
131
+ if(nodeInfo.type == 'class') {
132
+ if(!nodeInfo.instance) {
133
+ nodeInfo.instance = new node(...args);
134
+ }
135
+ return prop in nodeInfo.instance;
136
+ } else if(nodeInfo.type == 'dir') {
137
+ const _childPath = nodeInfo.path + prop;
138
+ const _childType = getNodeType(_childPath);
139
+ return _childType !== '';
140
+ }
141
+
142
+ return prop in node;
143
+ },
116
144
  });
117
145
  }
118
146
  }
119
147
 
148
+ loader.clearPathCache = () => typeStore.clear();
149
+
120
150
  module.exports = loader;
package/lib/model.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const Context = require('./context');
2
- const {toLine} = require('./utils/str');
2
+ const {toUnderScore} = require('./utils/internal');
3
3
 
4
4
  /**
5
5
  * @typedef {import('../types').DbConfigItem} DbConfigItem
@@ -29,7 +29,7 @@ class Model extends Context
29
29
  */
30
30
  // @ts-ignore
31
31
  this.connection = null;
32
- this.table = toLine(this.constructor.name);
32
+ this.table = toUnderScore(this.constructor.name);
33
33
  this.pk = 'id';
34
34
  this._db;
35
35
  }
package/lib/mvc.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const path = require('path');
2
2
  const {readFile} = require('fs').promises;
3
- const {toHump} = require('./utils/str');
4
- const {validatePath} = require('./utils/validate');
3
+ const {toCamelCase, pathToMiddlewareInfo} = require('./utils/internal');
5
4
  const config = require('./config');
6
5
  const {app: cfg_app, view: cfg_view} = config;
7
6
  const compose = require('koa-compose');
@@ -15,66 +14,40 @@ const storage = require('./storage');
15
14
  * @returns {Promise<void>}
16
15
  */
17
16
  async function mvc(ctx, next) {
18
- const controller_folder = ctx.FOLDER || cfg_app.controller_folder;
17
+ const controller_folder = ctx.CONTROLLER_FOLDER || cfg_app.controller_folder;
19
18
 
20
19
  if(cfg_app.app_debug) {
21
- Logger.http({Router: controller_folder, APP: ctx.APP, CONTROLLER: ctx.CONTROLLER, ACTION: ctx.ACTION});
20
+ Logger.http({Router: controller_folder, DEEP: ctx.DEEP, CONTROLLER: ctx.CONTROLLER, ACTION: ctx.ACTION});
22
21
  Object.keys(ctx.params).length && Logger.http('Params:', ctx.params);
23
22
  Object.keys(ctx.query).length && Logger.http('Get:', ctx.query);
24
23
  Object.keys(ctx.request.body || {}).length && Logger.http('Post:', ctx.request.body);
25
24
  }
26
-
27
- // 设置store
28
- const store = storage.getStore();
29
- store.APP = ctx.APP;
30
- store.CONTROLLER = ctx.CONTROLLER;
31
- store.ACTION = ctx.ACTION;
32
- store.COMMON = cfg_app.common_app;
33
25
 
34
- // 配置特殊处理
35
- store._.config === undefined && (store._.config = {});
36
- Object.entries(config).forEach(([key, value]) => {
37
- store._.config[key] = value;
38
- });
39
-
40
- // 应用、控制器、方法名字验证
41
- if(!validatePath(ctx.APP) || !validatePath(ctx.CONTROLLER) || !validatePath(ctx.ACTION)) {
42
- // throw new Error(`RunError: 应用、控制器或方法名字不合法!`);
43
- return;
44
- }
45
-
46
- if(ctx.APP[0] == '_' || ctx.APP[0] == '$' || !store._[ctx.APP]) {
47
- if(cfg_app.app_debug) {
48
- throw new Error(`RunError: 应用:${ctx.APP}不存在!`);
49
- } else {
50
- return;
26
+ const {$$} = storage.getStore();
27
+ const controllerPaths = [...ctx.DEEP.split('/').filter(Boolean), controller_folder];
28
+ const $node = controllerPaths.reduce((node, deep) => {
29
+ if(!node[deep]) {
30
+ throw new Error(`RunError: 目录:/app/${controllerPaths.join('/')}不存在!`);
51
31
  }
52
- }
53
-
54
- if(!store._[ctx.APP][controller_folder]) {
55
- if(cfg_app.app_debug) {
56
- throw new Error(`RunError: 目录:${ctx.APP}/${controller_folder}不存在!`);
57
- } else {
58
- return;
59
- }
60
- }
32
+ return node[deep];
33
+ }, $$);
61
34
 
62
35
  // 模版文件内容直接输出
63
36
  if(controller_folder == cfg_view.view_folder) {
64
- const content = await readFile(path.join(cfg_app.base_dir, `${ctx.APP}/${controller_folder}`, ctx.CONTROLLER + cfg_view.view_depr + ctx.ACTION + cfg_view.view_ext));
37
+ const content = await readFile(path.join(cfg_app.base_dir, 'app', controllerPaths.join('/'), ctx.CONTROLLER + cfg_view.view_depr + ctx.ACTION + cfg_view.view_ext));
65
38
  if(!content) {
66
- throw new Error(`RunError: 模板文件:${ctx.APP}/${controller_folder}/${ctx.CONTROLLER + cfg_view.view_depr + ctx.ACTION + cfg_view.view_ext}不存在!`);
39
+ throw new Error(`RunError: 模板文件:${controllerPaths.join('/')}/${ctx.CONTROLLER + cfg_view.view_depr + ctx.ACTION + cfg_view.view_ext}不存在!`);
67
40
  }
68
41
  ctx.body = content;
69
42
  return;
70
43
  }
71
44
 
72
45
  // 控制器
73
- const Controller = store._[ctx.APP][controller_folder][ctx.CONTROLLER] || store._[ctx.APP][controller_folder]['_empty'];
46
+ const Controller = $node[ctx.CONTROLLER] || $node['_empty'];
74
47
 
75
- if(ctx.CONTROLLER[0] == '_' || ctx.CONTROLLER[0] == '$' || typeof Controller != 'function') {
48
+ if(!Controller || typeof Controller != 'function') {
76
49
  if(cfg_app.app_debug) {
77
- throw new Error(`RunError: class文件:${ctx.APP}/${controller_folder}/${ctx.CONTROLLER}不存在!`);
50
+ throw new Error(`RunError: 控制器文件:${controllerPaths.join('/')}/${ctx.CONTROLLER}不存在!`);
78
51
  } else {
79
52
  return;
80
53
  }
@@ -82,12 +55,12 @@ async function mvc(ctx, next) {
82
55
 
83
56
  // 控制器实例
84
57
  const $controller = new Controller(ctx, next);
85
- const humpAction = toHump(ctx.ACTION);
86
- const action = typeof $controller[humpAction] == 'function' && !$controller[humpAction].__ISCLASS__ ? humpAction : '_empty';
58
+ const camelCaseAction = toCamelCase(ctx.ACTION);
59
+ const action = typeof $controller[camelCaseAction] == 'function' && !$controller[camelCaseAction].__ISCLASS__ ? camelCaseAction : '_empty';
87
60
 
88
- if(ctx.ACTION[0] == '_' || ctx.ACTION[0] == '$' || typeof $controller[action] != 'function') {
61
+ if(!$controller[action] || typeof $controller[action] != 'function') {
89
62
  if(cfg_app.app_debug) {
90
- throw new Error(`RunError: class方法:${ctx.APP}/${controller_folder}/${ctx.CONTROLLER}/${humpAction}不存在!`);
63
+ throw new Error(`RunError: 控制器方法:${controllerPaths.join('/')}/${camelCaseAction}不存在!`);
91
64
  } else {
92
65
  return;
93
66
  }
@@ -98,32 +71,33 @@ async function mvc(ctx, next) {
98
71
  && $controller['middleware'].length > 0
99
72
  && $controller['middleware'].reduce((stack, middle) => {
100
73
  typeof middle == 'string' && (middle = {middleware: middle});
101
- if(Array.isArray(middle.except) ? ~middle.except.indexOf(action) : ~[middle.except].indexOf(action)) {
74
+ if(middle.except && (Array.isArray(middle.except) ? ~middle.except.indexOf(action) : ~middle.except.split(',').indexOf(action))) {
102
75
  return stack;
103
76
  }
104
- if(middle.accept && (Array.isArray(middle.accept) ? !~middle.accept.indexOf(action) : !~[middle.accept].indexOf(action))) {
77
+ if(middle.accept && (Array.isArray(middle.accept) ? !~middle.accept.indexOf(action) : !~middle.accept.split(',').indexOf(action))) {
105
78
  return stack;
106
79
  }
107
80
  // @ts-ignore
108
81
  return stack.concat(async (ctx, next) => {
109
- const [METHOD = action, MIDDLEWARE = ctx.CONTROLLER, APP = ctx.APP] = middle.middleware.split('/').reverse();
110
- if(!store._[APP]) {
111
- throw new Error(`RunError: 中间件应用:${APP}不存在!`);
112
- }
113
- if(!store._[APP]['middleware']) {
114
- throw new Error(`RunError: 中间件目录:${APP}/middleware不存在!`);
115
- }
116
- const Middleware = store._[APP]['middleware'][MIDDLEWARE];
82
+ const {DEEPS, MIDDLEWARE, ACTION} = pathToMiddlewareInfo(middle.middleware, ctx.DEEP, ctx.CONTROLLER, action);
83
+ const middlewarePaths = [...DEEPS, cfg_app.middleware_folder];
84
+ const $node = middlewarePaths.reduce((node, deep) => {
85
+ if(!node[deep]) {
86
+ throw new Error(`RunError: 中间件目录:/app/${middlewarePaths.join('/')}不存在!`);
87
+ }
88
+ return node[deep];
89
+ }, $$);
90
+ const Middleware = $node[MIDDLEWARE];
117
91
  if(!Middleware || typeof Middleware != 'function') {
118
- throw new Error(`RunError: 中间件文件:${APP}/middleware/${MIDDLEWARE}不存在!`);
92
+ throw new Error(`RunError: 中间件文件:/app/${middlewarePaths.join('/')}/${MIDDLEWARE}不存在!`);
119
93
  }
120
94
  const $middleware = new Middleware(ctx, next);
121
- if(!$middleware[METHOD] || typeof $middleware[METHOD] != 'function') {
122
- throw new Error(`RunError: 中间件方法:${APP}/middleware/${MIDDLEWARE}/${METHOD}不存在!`);
95
+ if(!$middleware[ACTION] || typeof $middleware[ACTION] != 'function') {
96
+ throw new Error(`RunError: 中间件方法:${middlewarePaths.join('/')}/${ACTION}不存在!`);
123
97
  }
124
98
 
125
99
  // 执行中间件方法
126
- await $middleware[METHOD]();
100
+ await $middleware[ACTION]();
127
101
  });
128
102
  }, [])
129
103
  || [];
package/lib/request.js CHANGED
@@ -25,13 +25,14 @@ class Request extends Context
25
25
  }
26
26
 
27
27
  /**
28
- * 智能获取请求数据
28
+ * 智能获取请求数据,优先级为:post > get > param
29
29
  * @param {string} key
30
30
  * @param {*} [default_value='']
31
31
  * @returns {*}
32
32
  */
33
33
  query(key, default_value='') {
34
- return this.post(key) || this.get(key) || this.param(key) || default_value;
34
+ const value = this.post(key, undefined) || this.get(key, undefined) || this.param(key, undefined);
35
+ return this._getValue(value, default_value);
35
36
  }
36
37
 
37
38
  /**
@@ -41,7 +42,7 @@ class Request extends Context
41
42
  * @returns {*}
42
43
  */
43
44
  param(key, default_value='') {
44
- return this._param[key] || default_value;
45
+ return this._getValue(this._param[key], default_value);
45
46
  }
46
47
 
47
48
  /**
@@ -51,7 +52,7 @@ class Request extends Context
51
52
  * @returns {*}
52
53
  */
53
54
  get(key, default_value='') {
54
- return Array.isArray(this._get[key]) ? this._get[key][0] : this._get[key] || default_value;
55
+ return this._getValue(Array.isArray(this._get[key]) ? this._get[key].at(-1) : this._get[key], default_value);
55
56
  }
56
57
 
57
58
  /**
@@ -64,7 +65,7 @@ class Request extends Context
64
65
  if(!this.$config.app.koa_body && key !== 'is_ajax') {
65
66
  throw new Error('请先在app配置中开启koa_body');
66
67
  }
67
- return this._post[key] || default_value;
68
+ return this._getValue(this._post[key], default_value);
68
69
  }
69
70
 
70
71
 
@@ -117,14 +118,6 @@ class Request extends Context
117
118
  return this.ctx.request.files || {};
118
119
  }
119
120
 
120
- /**
121
- * 获取请求应用名
122
- * @returns {string}
123
- */
124
- app() {
125
- return this.ctx.APP;
126
- }
127
-
128
121
  /**
129
122
  * 获取请求控制器名
130
123
  * @returns {string}
@@ -141,6 +134,15 @@ class Request extends Context
141
134
  return this.ctx.ACTION;
142
135
  }
143
136
 
137
+ /**
138
+ * 获取客户端IP
139
+ * @returns {string}
140
+ */
141
+ ip() {
142
+ const forwarded = this.ctx.req.headers['x-forwarded-for'];
143
+ return forwarded ? (Array.isArray(forwarded) ? forwarded[0] : forwarded) : this.ctx.req.socket.remoteAddress || this.ctx.ip;
144
+ }
145
+
144
146
  /**
145
147
  * 获取请求url
146
148
  * @returns {string}
@@ -202,6 +204,16 @@ class Request extends Context
202
204
  headerAll() {
203
205
  return this.ctx.headers;
204
206
  }
207
+
208
+ /**
209
+ * @private
210
+ * @param {*} value
211
+ * @param {*} default_value
212
+ * @returns {*}
213
+ */
214
+ _getValue(value, default_value) {
215
+ return value === undefined ? default_value : typeof value === 'number' ? Number(value) : value;
216
+ }
205
217
  }
206
218
 
207
219
  module.exports = Request;
package/lib/response.js CHANGED
@@ -101,7 +101,7 @@ class Response extends Context
101
101
  * @param {Error} err
102
102
  */
103
103
  exception(err) {
104
- const escapeHtml = require('escape-html');
104
+ const escapeHtml = require('./utils/escape-html');
105
105
  const tplData = parseError(err);
106
106
  tplData.msg = escapeHtml(tplData.msg);
107
107
  // @ts-ignore