doomiwork 4.1.1 → 4.1.3

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.
@@ -3,7 +3,9 @@ const fs = require('fs');
3
3
  class appsetting{
4
4
  constructor(){
5
5
  let config = process.env.CONFIGFILE || 'configuration.json';
6
+
6
7
  let configfile = path.join(process.cwd(), config);
8
+ console.log('configfile', configfile)
7
9
  if (!fs.existsSync(configfile)) {
8
10
  throw new Error('missing app configuration file')
9
11
  }
@@ -107,6 +107,7 @@ module.exports.loadController=(app, routes)=> {
107
107
  ///包含文件
108
108
  if (element.include) {
109
109
  let includeRouteJson = path.join(startupRoot, element.include);
110
+ console.log('includeRouteJson', includeRouteJson)
110
111
  ///如果包含文件存在,则加载这个文件里面的路由
111
112
  if (fs.existsSync(includeRouteJson)) {
112
113
  this.loadController(app, require(includeRouteJson));
@@ -127,7 +128,8 @@ module.exports.loadController=(app, routes)=> {
127
128
  ////单个文件的加载
128
129
  if (modules.length > 0) {
129
130
  for (const modulePath of modules) {
130
- if (app.logger) app.logger.trace('initlize controller [%s] for baseUrl [%s]', modulePath, baseUrl);
131
+ //if (app.logger) app.logger.trace('initlize controller [%s] for baseUrl [%s]', modulePath, baseUrl);
132
+ // console.log('initlize controller [%s] for baseUrl [%s]', modulePath, baseUrl);
131
133
  const controllModule = require(modulePath);
132
134
  const controller = new controllModule(app);
133
135
  app.use(baseUrl, controller.router);
@@ -42,7 +42,7 @@ class controller {
42
42
  /**
43
43
  * 权限的业务sql过滤
44
44
  */
45
- PermissonBusiness(req){
45
+ PermissonBusiness(_req){
46
46
  return null;
47
47
  }
48
48
  /**
@@ -182,11 +182,12 @@ class controller {
182
182
  */
183
183
  async getListData(req, dataKey, configFile = 0) {
184
184
  if (this.logger) this.logger.trace("准备获取dataconfig文件中对应的 %s list数据",dataKey)
185
- const listinfo = getListInfo(req, dataKey, configFile,this._daoModel);
185
+ const ignorefilter = await this._redisHelper.get('key:ignorefilter');
186
+ const listinfo = getListInfo(req, dataKey, configFile, this._daoModel, ignorefilter==1);
186
187
  if (!listinfo) return { successed: false, errcode: -10, errmsg:`缺失${dataKey}对应的查询语句`};
187
188
 
188
189
  ////直接操作数据库之前,可由子类再次Handler
189
- let beforeData = await this.beforeAccessDB(req, listinfo.sql,null, "list", this);
190
+ let beforeData = await this.beforeAccessDB(req, listinfo.sql, listinfo.params, "list", this);
190
191
  ////如果返回Null,则表示不加载数据了
191
192
  if (beforeData.canceled === true) return { successed: false, errorcode: 1, errmsg: "操作取消" };
192
193
  ////操作数据库
@@ -362,7 +363,7 @@ class controller {
362
363
  */
363
364
  async delete(req, id, sqlCommand) {
364
365
  if (this.logger) this.logger.trace("准备删除数据",id)
365
- let beforeData = await this.beforeAccessDB(req, sqlCommand || this._daoModel.deleteSql(), id, "delete", this);
366
+ let beforeData = await this.beforeAccessDB(req, sqlCommand || this._daoModel.deleteSql(id), id, "delete", this);
366
367
  ////如果返回Null,则表示不加载数据了
367
368
  if (beforeData.canceled) return { successed: false, errorcode: 1, errormessage: "操作取消" };
368
369
  /////处理SQL中的一些定义符
@@ -89,7 +89,7 @@ class mysqlDao extends dao{
89
89
  /**
90
90
  * 更改记录
91
91
  */
92
- updateSql() {
92
+ updateSql(id) {
93
93
  return `update ${this.tableoption.tableName} set ? where ${this.tableoption.primaryKey}=? ${this.tableoption.forcefilter}`;
94
94
  }
95
95
  /**
@@ -102,15 +102,16 @@ class mysqlDao extends dao{
102
102
  * 删除记录
103
103
  */
104
104
  deleteSql() {
105
+ // const ids = (id + '').trim().split(',').map(x => '\'' + x + '\'');
105
106
  ////如果是逻辑删除,则只将记录的删除状态设置为1
106
107
  if(this.tableoption.logicDelete) {
107
108
 
108
- let sqlDelete = `update ${this.tableoption.tableName} set #DELETEBY# #DELETEDATE# ${this.tableoption.logicDeleteField || 'rec_isdeleted'} =1 where ${this.tableoption.primaryKey}=? ${this.tableoption.forcefilter}`;
109
+ let sqlDelete = `update ${this.tableoption.tableName} set #DELETEBY# #DELETEDATE# ${this.tableoption.logicDeleteField || 'rec_isdeleted'} =1 where ${this.tableoption.primaryKey} =? ${this.tableoption.forcefilter}`;
109
110
  sqlDelete = sqlDelete.replace('#DELETEBY#', this.tableoption.logDeleteBy?`${this.tableoption.logDeleteBy}=?,`:'')
110
111
  sqlDelete = sqlDelete.replace('#DELETEDATE#', this.tableoption.logDeleteDate ? `${this.tableoption.logDeleteDate}=now(),` : '');
111
112
  return sqlDelete;
112
113
  }
113
- return `delete from ${this.tableoption.tableName} where ${this.tableoption.primaryKey}=? ${this.tableoption.forcefilter}`;
114
+ return `delete from ${this.tableoption.tableName} where ${this.tableoption.primaryKey} =? ${this.tableoption.forcefilter}`;
114
115
  }
115
116
 
116
117
  /**
@@ -128,10 +129,11 @@ class mysqlDao extends dao{
128
129
  async update(Sql, model, id) {return this.executeSql(Sql, [model, id]);}
129
130
  ///删除记录
130
131
  async delete(Sql, id,userid) {
132
+ const ids = (id + '').trim().split(',').map(x => '\'' + x + '\'');
131
133
  if (this.tableoption.logicDelete && this.tableoption.logDeleteBy){
132
- return this.executeSql(Sql, [userid,id]);
134
+ return this.executeSql(Sql, [userid, id]); //ids.join(',')]);
133
135
  }
134
- return this.executeSql(Sql, id);
136
+ return this.executeSql(Sql, id); //ids.join(','));
135
137
  ///
136
138
  //return this.executeSql(Sql, id);
137
139
  /*if (result.successed && this.tableoption.logicDelete && userid && (this.tableoption.logDeleteBy || this.tableoption.logDeleteDate)){
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doomiwork",
3
- "version": "4.1.1",
3
+ "version": "4.1.3",
4
4
  "description": "doomisoft nodejs web framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,226 @@
1
+ const { parseKeyValue, validatorParamsType } = require('./keywordparse');
2
+ const dataconfig = require('../configuration/dataconfig').getCurrent();
3
+ const mysql = require('mysql');
4
+ const ORDER_STRING = ['asc', 'desc']
5
+ const FORBID_SQL_KEYWORD = /;|(--)|(\bWHERE\b)|(\bCASE\b)|(\bWHEN\b)|(\bSLEEP\b)|(\bSHOW\b)|(\bCOUNT\(\b)|(\bCREATE\b)|(\bCALL\b)|(\bBY\b)|(\bORDER\b)|(\bJOIN\b)|(\bUNION\b)|(\bFROM\b)|(\bSELECT\b)|(\bDROP\b)|(\bTRUNCATE\b)|(\bDELETE\b)|(\bUPDATE\b)|(\bINSERT\b)|(\bEXEC\b)|(\bEXECUTE\b)/gi;
6
+
7
+ function appendSearchCondition2Count(originSql, searchCondition) {
8
+ ///如果包含了这个特定的字符,则替换
9
+ if (originSql.indexOf('#APPENDSEARCH#') >= 0)
10
+ return originSql.replace('#APPENDSEARCH#', searchCondition);
11
+ return originSql + ' ' + searchCondition;
12
+ }
13
+ /**
14
+ * 检查是否有Sql注入的风险
15
+ * @param {*} sql
16
+ */
17
+ function checkSqlInjection(sql) {
18
+ if (!sql) return sql;
19
+ //let sqlError = sql.match(FORBID_SQL_KEYWORD); //strict ? sql.match(FORBID_SQL_KEYWORD_STRICT):sql.match(FORBID_SQL_KEYWORD);
20
+ //if (sqlError && sqlError.length > 0) return '';
21
+ if (FORBID_SQL_KEYWORD.test(sql)) return '';
22
+ return sql;
23
+ }
24
+ /*
25
+ * 根据对应列表的配置(dataConfig.list.search),从请求上下文中获取用户进行搜索的参数信息
26
+ */
27
+ module.exports.getSearchCondition = (option) => {
28
+ let paraCopy = option || {};
29
+ if (!paraCopy.request || !paraCopy.refer) return '';
30
+ if (!paraCopy.valueFrom) paraCopy.valueFrom = "all";
31
+ const request = paraCopy.request;
32
+ let retSearch = [],params = [];
33
+ paraCopy.refer.forEach(element => {
34
+ //retSearch.push(this.parseTagInSql(request, element.pattern, false,true));
35
+ const paramSql=this.parseTagForParamtersSearch(request, element);
36
+ if (paramSql.sql) {
37
+ retSearch.push(paramSql.sql);
38
+ params = params.concat(paramSql.params||[]);
39
+ }
40
+ });
41
+ return {filter:retSearch.join(''),params}
42
+ //return retSearch.join('');
43
+ }
44
+
45
+ /**
46
+ * 解析sql字符串中特殊的占位符
47
+ * @param {*} req
48
+ * @param {*} sql
49
+ * @param {*} allowNull
50
+ * @returns
51
+ */
52
+ module.exports.parseTagInSql = (req, sql, allowNull = true,isSearch=false) => {
53
+ if (!sql) return '';
54
+ ///定义正则准备查找sql中的特定关键字
55
+ const matched = sql.match(/@.*?@/g);
56
+ if (!matched || matched.length <= 0) return sql;
57
+ let parseKeyWordIsNull = false;
58
+
59
+ const charReg = /\s+|:|=/gi
60
+ matched.forEach(ele => {
61
+ let matchValue = ele.substring(1, ele.length - 1);
62
+ ///不允许特殊字符出现
63
+ let notdo = matchValue.match(charReg);
64
+ if (notdo && notdo.length > 0) return;
65
+
66
+ let noQuoteProtect = matchValue[0] == '!';
67
+ if (noQuoteProtect) {
68
+ matchValue = matchValue.substring(1);
69
+ }
70
+ ///是否有格式要求
71
+ let validformat = matchValue.split('|');
72
+ matchValue = validformat[0];
73
+ let keyValue = (parseKeyValue(req, matchValue) || '')+''; //utility.ifNull(keyParse.parseKeyValue(req, matchValue), '')+'';
74
+ if (!keyValue) {
75
+ parseKeyWordIsNull = true;
76
+ } else if (typeof (keyValue) === 'string') {
77
+ keyValue = noQuoteProtect || isSearch ? checkSqlInjection(mysql.escape(keyValue)) : mysql.escape(keyValue)
78
+ keyValue = keyValue.substr(1, keyValue.length - 2);
79
+ ///验证参数的格式合法性
80
+ if (keyValue && validformat.length > 1) {
81
+ // console.log(`参数格式合法检查==>${validformat[1]}:${matchValue} = ${keyValue}`)
82
+ keyValue = validatorParamsType(keyValue, validformat[1], validformat[2])
83
+ }
84
+ if (!keyValue) {
85
+ parseKeyWordIsNull = true;
86
+ }
87
+ ///没有引号保护下发现了sql注入,则用1=0不返回任何结果
88
+ if (!keyValue && noQuoteProtect) {
89
+ keyValue = '1=0'
90
+ }
91
+ } else { ////数组形式的参数,目前框架不支持,统一认为为sql注入攻击,全部忽略
92
+ // console.log(`参数非法==>类型${typeof (keyValue)}:${matchValue} = ${keyValue}`)
93
+ parseKeyWordIsNull = true;
94
+ keyValue = ''
95
+ }
96
+ sql = sql.replace(ele, keyValue);
97
+ });
98
+ if (!allowNull && parseKeyWordIsNull) return '';
99
+ return sql;
100
+ }
101
+
102
+ /**
103
+ * 为列表查询定义参数化查询
104
+ * @param {*} req
105
+ * @param {*} sql
106
+ * @param {*} allowNull
107
+ * @param {*} isSearch
108
+ * @returns
109
+ */
110
+ module.exports.parseTagForParamtersSearch = (req, filterSetting={}) => {
111
+ let {pattern:sql,mode=''} = filterSetting;
112
+ if (!sql) return {sql:''};
113
+ ///定义正则准备查找sql中的特定关键字
114
+ const matched = sql.match(/@.*?@/g);
115
+ let params = [];
116
+ if (!matched || matched.length <= 0) return {sql};
117
+ for(const ele of matched){
118
+ let patternModel = mode;
119
+ let matchValue = ele.substring(1, ele.length - 1);
120
+ ///不允许特殊字符出现
121
+ let noQuoteProtect = matchValue[0] == '!';
122
+ if (noQuoteProtect) {
123
+ matchValue = matchValue.substring(1);
124
+ patternModel='';
125
+ }
126
+ ///是否有格式要求
127
+ let validformat = matchValue.split('|');
128
+ matchValue = validformat[0];
129
+ let keyValue = (parseKeyValue(req, matchValue) || '') + ''; //utility.ifNull(keyParse.parseKeyValue(req, matchValue), '')+'';
130
+ if (keyValue && typeof (keyValue) === 'string') {
131
+ ///验证参数的格式合法性
132
+ if (keyValue && validformat.length > 1) {
133
+ keyValue = validatorParamsType(keyValue, validformat[1], validformat[2])
134
+ }
135
+ }
136
+ ///如果解析不出这个KeyValue ,则认为当前这条SQL过滤无效
137
+ if (!keyValue) return {sql:'',params:[]};
138
+ if (keyValue) {
139
+ switch (patternModel.toLowerCase()) {
140
+ case 'like':
141
+ sql = sql.replace(ele, '?'); ///变成参数化查询
142
+ params.push('%' + keyValue + '%')
143
+ break;
144
+ case 'in':
145
+ sql = sql.replace(ele, '?'); ///变成参数化查询
146
+ params.push(keyValue.split(','))
147
+ break;
148
+ default:
149
+ sql = sql.replace(ele, '?'); ///变成参数化查询
150
+ params.push(keyValue)
151
+ break;
152
+ }
153
+ }
154
+ }
155
+ return {sql,params};
156
+ }
157
+ /*
158
+ * 列表请求上下文中获取需要的信息
159
+ * 如Page ,PageSize , Sort 等等
160
+ */
161
+ module.exports.getListInfo = (req, dataKey, cfgType = 0, dao) => {
162
+ if (!dataKey) return null;
163
+ ///确认是否是需要导出Excel
164
+ const export2Excel = (req.query.exportexcel + "").toLowerCase() === "true";
165
+ let { page = 1, rows: pageSize = 100, sort, order, clientFilter: filter } = req.query
166
+ ///防止sortSql 注入在排序的参数中
167
+ if (sort) sort = checkSqlInjection(sort);
168
+ // 防止页面数据传入非数字
169
+ if (isNaN(pageSize)) pageSize = 30;
170
+ // 防止页面数据传入非数字
171
+ if (!page || isNaN(page)) page = 1
172
+ ///最大允许获取100条数据
173
+ pageSize =Math.max(0,Math.min(pageSize, 10000));
174
+ ///拼接排序的语句
175
+ if (order && sort && ORDER_STRING.includes(order.toLowerCase())) sort = sort + ' ' + order
176
+ //req.order =req.sort? utility.ifNull((req.body.order || req.query.order), ""):'';
177
+ const dataConfig = dataconfig.getConfig(dataKey, cfgType);
178
+ if (dataConfig && dataConfig.list) {
179
+ req.dataConfig = dataConfig
180
+ let { sql, listsql, countsql, sqltype = 'sql', field, footer, search, sort: constsort } = dataConfig.list
181
+ /**解析查询条件 */
182
+ const searchCondition = this.getSearchCondition({ request: req, refer: search });
183
+ /**排序方式 *///
184
+ sort = sort || constsort;
185
+ /**来自req请求参数中的过滤条件 */
186
+ let clientfilter = checkSqlInjection(this.parseTagInSql(req, filter, false));
187
+ //如果配置文件中不是直接的sql语句,则从DAO对象中的指定方法来获取sql
188
+ if (sqltype !== 'sql' && listsql && typeof (dao[listsql]) === 'function') {
189
+ sql = dao[sql](req, { page, rows: pageSize, sort, filter: searchCondition.filter, client: clientfilter });
190
+ if (dao[countsql] && typeof (dao[countsql]) === 'function')
191
+ countsql = dao[countsql](req, { page, rows: pageSize, sort, filter: searchCondition.filter, client: clientfilter });
192
+ }
193
+
194
+ /**根据过滤条件、排序条件、分页获取的方式,和原始sql拼接成最终获取数据的sql */
195
+ if (sqltype==='sql'){
196
+ sql = this.parseTagInSql(req, sql) +
197
+ ' ' + searchCondition.filter + clientfilter +
198
+ (sort ? (' order by ' + sort) : '') +
199
+ (export2Excel ? '' : ' limit ' + Number(pageSize) + ' OFFSET ' + (Math.max(Number(page), 1) - 1) * Number(pageSize)) +
200
+ /////在Sql中再放入获取总记录数的语句
201
+ ';SELECT FOUND_ROWS() AS total;';
202
+ /*** 如果存在汇总列的sql,则把SQL放置在最末尾 */
203
+ if (countsql) {
204
+ sql += appendSearchCondition2Count(this.parseTagInSql(req, countsql), searchCondition.filter)
205
+ }
206
+ }
207
+ ///如果存在统计的SQL,则参数需要Double一次
208
+ const sqlParams = countsql ? [].concat(searchCondition.params, searchCondition.params):searchCondition.params;
209
+ return { sql, fields: field, params: sqlParams, footers: footer, page, hascounter: countsql?true:false };
210
+ }
211
+ }
212
+ /*
213
+ * 详细页面请求上下文中获取需要的信息
214
+ * 如Page ,PageSize , Sort 等等
215
+ */
216
+ module.exports.getDetailInfo = (req,dataKey) => {
217
+ if (dataKey) {
218
+ const dataConfig = dataconfig.getConfig(dataKey);
219
+ if (dataConfig && dataConfig.detail) {
220
+ req.dataConfig = dataConfig;
221
+ const { primary, field: fields, primaryIsAutoIncrease } = dataConfig.detail;
222
+ return { primary, fields, autoincrease: primaryIsAutoIncrease }
223
+ }
224
+ }
225
+ return null;
226
+ }
@@ -16,8 +16,9 @@ function appendSearchCondition2Count(originSql, searchCondition) {
16
16
  */
17
17
  function checkSqlInjection(sql) {
18
18
  if (!sql) return sql;
19
- let sqlError = sql.match(FORBID_SQL_KEYWORD); //strict ? sql.match(FORBID_SQL_KEYWORD_STRICT):sql.match(FORBID_SQL_KEYWORD);
20
- if (sqlError && sqlError.length > 0) return '';
19
+ if (FORBID_SQL_KEYWORD.test(sql)) return '';
20
+ //let sqlError = sql.match(FORBID_SQL_KEYWORD); //strict ? sql.match(FORBID_SQL_KEYWORD_STRICT):sql.match(FORBID_SQL_KEYWORD);
21
+ //if (sqlError && sqlError.length > 0) return '';
21
22
  return sql;
22
23
  }
23
24
  /*
@@ -96,7 +97,7 @@ module.exports.parseTagInSql = (req, sql, allowNull = true) => {
96
97
  * 列表请求上下文中获取需要的信息
97
98
  * 如Page ,PageSize , Sort 等等
98
99
  */
99
- module.exports.getListInfo = (req, dataKey, cfgType = 0, dao) => {
100
+ module.exports.getListInfo = (req, dataKey, cfgType = 0, dao,ignorefilter=false) => {
100
101
  if (!dataKey) return null;
101
102
  ///确认是否是需要导出Excel
102
103
  const export2Excel = (req.query.exportexcel + "").toLowerCase() === "true";
@@ -108,16 +109,16 @@ module.exports.getListInfo = (req, dataKey, cfgType = 0, dao) => {
108
109
  // 防止页面数据传入非数字
109
110
  if (!page || isNaN(page)) page = 1
110
111
  ///最大允许获取100条数据
111
- pageSize = Math.min(pageSize, 10000);
112
+ pageSize =Math.max(0,Math.min(pageSize, 10000));
112
113
  ///拼接排序的语句
113
114
  if (order && sort && ORDER_STRING.includes(order.toLowerCase())) sort = sort + ' ' + order
114
115
  //req.order =req.sort? utility.ifNull((req.body.order || req.query.order), ""):'';
115
116
  const dataConfig = dataconfig.getConfig(dataKey, cfgType);
116
117
  if (dataConfig && dataConfig.list) {
117
118
  req.dataConfig = dataConfig
118
- let { sql, countsql, sqltype = 'sql', field, footer, search, sort: constsort } = dataConfig.list
119
+ let { sql, listsql, countsql, sqltype = 'sql', field, footer, search, sort: constsort } = dataConfig.list
119
120
  /**解析查询条件 */
120
- const searchCondition = this.getSearchCondition({ request: req, refer: search });
121
+ const searchCondition = ignorefilter?'':this.getSearchCondition({ request: req, refer: search });
121
122
  /**排序方式 *///
122
123
  sort = sort || constsort;
123
124
  /**来自req请求参数中的过滤条件 */
@@ -134,7 +135,7 @@ module.exports.getListInfo = (req, dataKey, cfgType = 0, dao) => {
134
135
  sql = this.parseTagInSql(req, sql) +
135
136
  ' ' + searchCondition + clientfilter +
136
137
  (sort ? (' order by ' + sort) : '') +
137
- (export2Excel ? '' : ' limit ' + Number(pageSize) + ' OFFSET ' + (Number(page) - 1) * Number(pageSize)) +
138
+ (export2Excel ? '' : ' limit ' + Number(pageSize) + ' OFFSET ' + (Math.max(Number(page), 1) - 1) * Number(pageSize)) +
138
139
  /////在Sql中再放入获取总记录数的语句
139
140
  ';SELECT FOUND_ROWS() AS total;';
140
141
  /*** 如果存在汇总列的sql,则把SQL放置在最末尾 */