crabatool 1.0.410 → 1.0.415

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/apis/data.js CHANGED
@@ -10,13 +10,15 @@ class data {
10
10
  constructor() {
11
11
  this.dslMap = {};
12
12
  this.pagesDataChanged = {};
13
+ this.dsl = {};
14
+ this.dataRefs = [];
13
15
  }
14
16
 
15
17
  _init(req) {
16
18
  if (req.app.jsoncrud) { // 每个server只创建1个实例
17
19
  return;
18
20
  }
19
- req.app.jsoncrud = new JSONCRUD({ storagePath: path.join(req.app.webPath, '../data/data.json') });
21
+ req.app.jsoncrud = new JSONCRUD({ storagePath: path.join(req.app.webPath, '../data/entities'), separateFiles: true });
20
22
 
21
23
  //读取dsl数据
22
24
  var dslData = fs.readFileSync(path.join(req.app.webPath, '../data/dslData.json'));
@@ -25,9 +27,9 @@ class data {
25
27
  } else {
26
28
  dslData = JSON.parse(dslData)
27
29
  }
28
- var dsl = dslData.dsl;
30
+ this.dsl = dslData.dsl;
29
31
  var _this = this;
30
- dsl && dsl.forEach(item => {
32
+ _this.dsl && _this.dsl.forEach(item => {
31
33
  _this.dslMap[crabatool.stringUtils.getFullPinyinCode(item.name)] = JSON.parse(JSON.stringify(item));
32
34
  });
33
35
  var dataRefs = dslData.dataRefs;
@@ -56,7 +58,6 @@ class data {
56
58
  async get(req, res) {
57
59
  var body = utils.getReqBody(req);
58
60
  var that = this;
59
-
60
61
  var queryParams = body.queryParams;
61
62
  var filterParams = body.filter;
62
63
  var items = null;
@@ -84,8 +85,8 @@ class data {
84
85
  items.push({ dataField: key, type: 0, value: queryParams[key] });
85
86
  });
86
87
  }
87
-
88
- var info = await this._getDb(req).find(body.name, {
88
+ var db = this._getDb(req);
89
+ var info = await db.find(body.name, {
89
90
  page: body.pageIndex,
90
91
  pageSize: body.pageSize,
91
92
  filterFunc: function (item) {
@@ -95,7 +96,7 @@ class data {
95
96
  return true;
96
97
  }
97
98
  });
98
-
99
+ await this.fillData(body.name, info.data, db, true);
99
100
  crabatool.utils.end(res, {
100
101
  code: 200,
101
102
  msg: '操作成功',
@@ -104,8 +105,65 @@ class data {
104
105
  });
105
106
  }
106
107
 
108
+ async fillData(name, rows, db, searchDataRef) {
109
+ if (this.dslMap[name]) {
110
+ if (rows) {
111
+ for (var i = 0; i < rows.length; i++) {
112
+ var row = rows[i];
113
+ var dsl = this.dslMap[name];
114
+ for (var j = 0; j < dsl.fields.length; j++) { //join扩展表数据
115
+ var field = dsl.fields[j];
116
+ if (field.type != 'table') {
117
+ continue;
118
+ }
119
+ var temp = await db.getByField(crabatool.stringUtils.getFullPinyinCode(field.ref), crabatool.stringUtils.getFullPinyinCode(field.refField), row[crabatool.stringUtils.getFullPinyinCode(field.name)]);
120
+ if (!temp) {
121
+ continue;
122
+ }
123
+ var str = crabatool.stringUtils.getFullPinyinCode(field.refShowField ? field.refShowField : field.refField);
124
+ row[str] = temp[str];
125
+ }
126
+
127
+ if (searchDataRef) {
128
+ //查询关联明细数据
129
+ var dataRefs = this.dslMap[name].dataRefs;
130
+ if (dataRefs) {
131
+ for (var m = 0; m < dataRefs.length; m++) {
132
+ var dataRef = dataRefs[m];
133
+ var detailPy = crabatool.stringUtils.getFullPinyinCode(dataRef.detail);
134
+ var masterFieldPy = crabatool.stringUtils.getFullPinyinCode(dataRef.masterField);
135
+ var obj = row[masterFieldPy];
136
+ var detailFieldPy = crabatool.stringUtils.getFullPinyinCode(dataRef.detailField);
137
+ var size = 100;
138
+ var rtSize = 0;
139
+ var array = [];
140
+ do {
141
+ //循环取外表数据
142
+ var page = 1;
143
+ var t = await db.find(detailPy, {
144
+ page: page,
145
+ pageSize: size,
146
+ filterFunc: function (row) {
147
+ return obj && obj === row[detailFieldPy];
148
+ }
149
+ });
150
+ page++;
151
+ rtSize = t.pageData.count;
152
+ array = array.concat(t.data);
153
+ } while (rtSize == size);
154
+ row[detailPy] = array;
155
+ await this.fillData(detailPy, row[detailPy], db, false);//只查一级
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
163
+
107
164
  async reverseData(req, pageName, body) {
108
- var oldEntity = await this._getDb(req).get(body.name, body.data.id);
165
+ var db = this._getDb(req);
166
+ var oldEntity = await db.get(body.name, body.data.id);
109
167
  if (!oldEntity) {
110
168
  crabatool.utils.end(res, {
111
169
  code: 404,
@@ -113,6 +171,8 @@ class data {
113
171
  });
114
172
  return;
115
173
  }
174
+
175
+ await this.fillData(body.name, [oldEntity], db, true);
116
176
  //退回联动老数据
117
177
  var rt = await this.pageSaveToDataLinkage(req, body.name, pageName, oldEntity, null, true);
118
178
  return {
@@ -125,6 +185,8 @@ class data {
125
185
 
126
186
  var newData;
127
187
  var pageName = body.pageName;
188
+ var db = this._getDb(req);
189
+ var rtData = JSON.parse(JSON.stringify(body.data));
128
190
  if (body.data.id) {
129
191
  var oldData = await this.reverseData(req, pageName, body);
130
192
  if (!oldData) {
@@ -133,23 +195,46 @@ class data {
133
195
  await this.buildDataPrimaryAndForeignKey(body.name, body.data, true);
134
196
  //更新联动新数据
135
197
  await this.pageSaveToDataLinkage(req, body.name, pageName, body.data, oldData);
136
- await this._getDb(req).update(body.name, body.data.id, body.data);
198
+ var name = body.name;
199
+ await this._saveRefData(name, body.data, rtData, db);
200
+ await db.update(body.name, body.data.id, body.data);
137
201
  newData = body.data;
138
202
  } else {
139
203
  //提前制定ID
140
204
  body.data.id = cuid.newCuidString()
141
205
  await this.buildDataPrimaryAndForeignKey(body.name, body.data);
142
206
  await this.pageSaveToDataLinkage(req, body.name, pageName, body.data)
143
- newData = await this._getDb(req).create(body.name, body.data);
207
+ await this._saveRefData(name, body.data, rtData, db);
208
+ newData = await db.create(body.name, body.data);
144
209
  }
145
210
 
146
211
  crabatool.utils.end(res, {
147
212
  code: 200,
148
213
  msg: '操作成功',
149
- data: newData
214
+ data: rtData
150
215
  });
151
216
  }
152
217
 
218
+ async _saveRefData(name, data, rtData, db) {
219
+ var dataRefs = this.dslMap[name].dataRefs;
220
+ for (var i = 0; dataRefs && i < dataRefs.length; i++) {
221
+ var dataRef = dataRefs[i];
222
+ var detailPy = crabatool.stringUtils.getFullPinyinCode(dataRef.detail)
223
+ var details = data[detailPy] ? data[detailPy] : [];
224
+ delete data[detailPy];
225
+ for (var j = 0; j < details.length; j++) {
226
+ var detail = details[j];
227
+ if (detail.id) {
228
+ await db.update(detailPy, detail.id, detail)
229
+ } else {
230
+ await db.create(detailPy, detail)
231
+ rtData[detailPy][j].id = detail.id;//补充新增行的ID
232
+ }
233
+ await this._saveRefData(detailPy, detail, rtData[detailPy][j], db);
234
+ }
235
+ }
236
+ }
237
+
153
238
  findLayerData(data, items, layer) {
154
239
  if (!data) {
155
240
  return null;
@@ -205,9 +290,9 @@ class data {
205
290
  }
206
291
  //查找涉及的所有实体
207
292
  var _this = this;
208
- for(var i = 0 ; i < actions.length; i++){
293
+ for (var i = 0; i < actions.length; i++) {
209
294
  var action = actions[i];
210
- for(var j = 0 ; j < action.length ; j++){
295
+ for (var j = 0; j < action.length; j++) {
211
296
  var item = action[j];
212
297
  if (item.type == 'external') {//外表处理
213
298
  await _this.externalDataLinkage(req, name, data, item, reverseNumber);
@@ -215,7 +300,7 @@ class data {
215
300
  //从原始数据中获取对应字段的数据
216
301
  var targetRef = _this.findFormateItem(item.filter.refField)
217
302
  var targetRefField = _this.findFormateItem(`#[${item.ref}.${item.field}]`)
218
- var targetValue = _this.findItemMapData(name, crabatool.stringUtils.getFullPinyinCode(item.ref), data, new Set([...targetRef, ...targetRefField]));
303
+ var targetValue = _this.findItemMapData(name, crabatool.stringUtils.getFullPinyinCode(item.ref), data, new Set([...targetRef, ...targetRefField, `${item.ref}.id`]));
219
304
  var refFieldName = this.matchFields(item.filter.refField)[0];
220
305
  var refFieldNamePy = crabatool.stringUtils.getFullPinyinCode(refFieldName);
221
306
  var targetArray = refFieldNamePy.split('.')
@@ -232,18 +317,24 @@ class data {
232
317
  //根据refData和sourceData计算结果
233
318
  var refNamePy = crabatool.stringUtils.getFullPinyinCode(item.ref);
234
319
  var fieldNamePy = crabatool.stringUtils.getFullPinyinCode(item.field);
235
- Object.keys(refData).forEach(key => {
320
+ refData && Object.keys(refData).forEach(key => {
236
321
  var target = refData[key][0];
237
322
  //目标值为单一对象
238
323
  if (!target[refNamePy]) {
239
324
  return;
240
325
  }
241
326
  if (oldData) {
242
- if(name != refNamePy){
327
+ if (name != refNamePy) {
243
328
  //传入了老数据,当前计算的值可以直接取老数据的对应值,因为它已经排除本单
244
329
  var oldDataTemp = oldData[name][refNamePy];
245
- if(oldDataTemp instanceof Array){
246
- target[refNamePy][fieldNamePy] = oldDataTemp[0][fieldNamePy];//强制取第一行
330
+ if (oldDataTemp instanceof Array) {
331
+ target[refNamePy][fieldNamePy] = 0;
332
+ for(var m = 0 ; m < oldDataTemp.length; m++){
333
+ if(oldDataTemp[m].id == target[refNamePy].id){
334
+ target[refNamePy][fieldNamePy] = oldDataTemp[m][fieldNamePy];
335
+ break;
336
+ }
337
+ }
247
338
  } else {
248
339
  target[refNamePy][fieldNamePy] = oldDataTemp[fieldNamePy] ? oldDataTemp[fieldNamePy] : 0;
249
340
  }
@@ -271,10 +362,10 @@ class data {
271
362
  if (refNamePy != name) {
272
363
  targetObj = data[refNamePy];
273
364
  }
274
- if(targetObj instanceof Array){
275
- targetObj.forEach(titem => {
365
+ if (targetObj instanceof Array) {
366
+ targetObj.forEach(titem => {
276
367
  var primaryKey = titem[targetArray[1]];
277
- if(!refData[primaryKey]){//没找到对应行,直接返回
368
+ if (!refData[primaryKey]) {//没找到对应行,直接返回
278
369
  return;
279
370
  }
280
371
  var refDataTemp = refData[primaryKey][0][refNamePy];
@@ -289,7 +380,7 @@ class data {
289
380
  }
290
381
  //将结果赋值给实体对象
291
382
  var t = targetValue[refNamePy];
292
- Object.keys(t).forEach(key => {
383
+ t && Object.keys(t).forEach(key => {
293
384
  targetObj[key] = t[key];
294
385
  });
295
386
  }
@@ -324,7 +415,7 @@ class data {
324
415
  page: page,
325
416
  pageSize: size,
326
417
  filterFunc: function (row) {
327
- if (filterValue[row[refFieldPy]]) {
418
+ if (filterValue && filterValue[row[refFieldPy]]) {
328
419
  changeRefData.push({
329
420
  ref: row,
330
421
  formateValue: filterValue[row[refFieldPy]]
@@ -496,9 +587,9 @@ class data {
496
587
  }
497
588
  data[detailName].forEach(detail => {
498
589
  detail[crabatool.stringUtils.getFullPinyinCode(item.detailField)] = data[crabatool.stringUtils.getFullPinyinCode(item.masterField)];
499
- if (!detail.id) {//明细行ID为空时需要生成新值
500
- detail.id = cuid.newCuidString()
501
- }
590
+ // if (!detail.id) {//明细行ID为空时需要生成新值
591
+ // detail.id = cuid.newCuidString()
592
+ // }
502
593
  //嵌套处理明细中关联的主键和外键
503
594
  _this.buildDataPrimaryAndForeignKey(detailName, detail)
504
595
  });
package/lib/jsoncrud.js CHANGED
@@ -7,6 +7,7 @@ class JSONCRUD {
7
7
  * @param {Object} config - 配置项
8
8
  * @param {string} [config.storagePath] - 文件存储路径
9
9
  * @param {boolean} [config.useMemory] - 是否使用内存存储
10
+ * @param {boolean} [config.separateFiles] - 按实体分文件存储
10
11
  */
11
12
  constructor(config = {}) {
12
13
  this.config = config
@@ -33,29 +34,54 @@ class JSONCRUD {
33
34
  }
34
35
 
35
36
  // 保存到文件
36
- async saveToFile() {
37
+ async saveToFile(entityName) {
37
38
  if (!this.useMemory) {
38
- // 构建包含所有实体的存储对象
39
- const dataToSave = Object.entries(this.entities).reduce((acc, [entityName, entityData]) => {
40
- acc[entityName] = entityData.data
41
- return acc
42
- }, {})
39
+ var data = Object.entries(this.entities);
40
+ var tempStorageFile = this.storageFile;
41
+ var dataToSave = null;
42
+ if (this.config.separateFiles) {
43
+ //按文件拆分存储
44
+ data = entityName && this.entities[entityName] ? this.entities[entityName].data : {};
45
+ dataToSave = data;
46
+ tempStorageFile = path.join(tempStorageFile, entityName + '.json');
47
+ } else {
48
+ // 构建包含所有实体的存储对象
49
+ dataToSave = data.reduce((acc, [entityName, entityData]) => {
50
+ acc[entityName] = entityData.data
51
+ return acc
52
+ }, {})
53
+ }
43
54
 
44
55
  // 创建存储目录(如果不存在)
45
- await fs.mkdir(path.dirname(this.storageFile), { recursive: true })
56
+ await fs.mkdir(path.dirname(tempStorageFile), { recursive: true })
46
57
 
47
58
  // 原子化写入(避免写入过程中崩溃导致数据丢失)
48
- const tmpFile = `${this.storageFile}.tmp`
49
- try{
59
+ const tmpFile = `${tempStorageFile}.tmp`
60
+ try {
50
61
  //频繁写入时会导致数据错误
51
62
  await fs.writeFile(tmpFile, JSON.stringify(dataToSave, null, 2))
52
- await fs.rename(tmpFile, this.storageFile)
53
- }catch(e){
63
+ await fs.rename(tmpFile, tempStorageFile)
64
+ } catch (e) {
54
65
  console.log(e);
55
66
  }
56
67
  }
57
68
  }
58
69
 
70
+ async batchCreate(entityName, items) {
71
+ await this.ready()
72
+ this.verifyEntityExists(entityName)
73
+ var _this = this;
74
+ items.forEach(item=>{
75
+ const newItem = {
76
+ id: cuid.newCuidString(),//允许外部提前制定ID
77
+ ...item,
78
+ createdAt: new Date().toISOString()
79
+ }
80
+ _this.entities[entityName].data.push(newItem)
81
+ })
82
+ await this.persist(entityName)
83
+ }
84
+
59
85
  // 核心CRUD方法
60
86
  async create(entityName, item) {
61
87
  await this.ready()
@@ -67,7 +93,7 @@ class JSONCRUD {
67
93
  createdAt: new Date().toISOString()
68
94
  }
69
95
  this.entities[entityName].data.push(newItem)
70
- await this.persist()
96
+ await this.persist(entityName)
71
97
  return newItem
72
98
  }
73
99
 
@@ -101,7 +127,7 @@ class JSONCRUD {
101
127
  const end = start + pageSize
102
128
 
103
129
  return {
104
- data: sorted.slice(start, end),
130
+ data: JSON.parse(JSON.stringify(sorted.slice(start, end))),
105
131
  pageData: {
106
132
  count: allData.length,
107
133
  page,
@@ -132,7 +158,16 @@ class JSONCRUD {
132
158
 
133
159
  const index = this.entities[entityName].data.findIndex(item => item.id === id)
134
160
  if (index === -1) return null
135
- return this.entities[entityName].data[index]
161
+ return JSON.parse(JSON.stringify(this.entities[entityName].data[index]))
162
+ }
163
+
164
+ async getByField(entityName, field, value) {
165
+ await this.ready()
166
+ this.verifyEntityExists(entityName)
167
+
168
+ const index = this.entities[entityName].data.findIndex(item => item[field] === value)
169
+ if (index === -1) return null
170
+ return JSON.parse(JSON.stringify(this.entities[entityName].data[index]))
136
171
  }
137
172
 
138
173
  async update(entityName, id, updates) {
@@ -147,15 +182,15 @@ class JSONCRUD {
147
182
  ...updates,
148
183
  updatedAt: new Date().toISOString()
149
184
  }
150
- await this.persist()
151
- return this.entities[entityName].data[index]
185
+ await this.persist(entityName)
186
+ return JSON.parse(JSON.stringify(this.entities[entityName].data[index]))
152
187
  }
153
188
 
154
- async batchUpdate(entityName, items){
189
+ async batchUpdate(entityName, items) {
155
190
  await this.ready()
156
191
  this.verifyEntityExists(entityName)
157
192
 
158
- items.forEach(entity =>{
193
+ items.forEach(entity => {
159
194
  const index = this.entities[entityName].data.findIndex(item => item.id === entity.id)
160
195
  if (index === -1) return null
161
196
 
@@ -165,7 +200,7 @@ class JSONCRUD {
165
200
  updatedAt: new Date().toISOString()
166
201
  }
167
202
  })
168
- await this.persist()
203
+ await this.persist(entityName)
169
204
  }
170
205
 
171
206
  async delete(entityName, id) {
@@ -177,7 +212,7 @@ class JSONCRUD {
177
212
  item => item.id !== id
178
213
  )
179
214
  const success = this.entities[entityName].data.length < initialLength
180
- if (success) await this.persist()
215
+ if (success) await this.persist(entityName)
181
216
  return success
182
217
  }
183
218
 
@@ -194,14 +229,40 @@ class JSONCRUD {
194
229
  await new Promise(resolve => setTimeout(resolve, 50))
195
230
  }
196
231
  }
232
+ async readAllFilesInDirectory(dirPath) {
233
+ try {
234
+ var fileContent = {};
235
+ const files = await fs.readdir(dirPath);
236
+ for (const file of files) {
237
+ const filePath = path.join(dirPath, file);
238
+ var stats = await fs.stat(filePath);
239
+ if (stats.isFile()) {
240
+ var entityName = file.substring(0, file.lastIndexOf('.'));
241
+ var temp = await fs.readFile(filePath, 'utf-8')
242
+ fileContent[entityName] = temp?JSON.parse(temp):{};
243
+ } else if (stats.isDirectory()) {
244
+ // 如果需要递归处理子目录,可以在这里调用readAllFilesInDirectory(filePath)
245
+ }
246
+ }
247
+ return fileContent;
248
+ } catch (err) {
249
+ return {};
250
+ }
251
+ }
197
252
 
198
253
  // 初始化文件存储
199
254
  async initFileStorage() {
200
255
  try {
201
- // 1. 读取存储文件内容
202
- const fileContent = await fs.readFile(this.storageFile, 'utf-8')
203
- const storedData = JSON.parse(fileContent || '{}')
204
-
256
+ var fileContent = null;
257
+ var storedData = {};
258
+ if (this.config.separateFiles) {
259
+ var dir = this.storageFile;
260
+ storedData = await this.readAllFilesInDirectory(dir);//读取文件夹中的所有文件
261
+ } else {
262
+ // 1. 读取存储文件内容
263
+ fileContent = await fs.readFile(this.storageFile, 'utf-8')
264
+ storedData = JSON.parse(fileContent || '{}')
265
+ }
205
266
  // 2. 自动创建存储中的实体(如果尚未定义)
206
267
  Object.keys(storedData).forEach(entityName => {
207
268
  if (!this.entities[entityName]) {
@@ -221,7 +282,6 @@ class JSONCRUD {
221
282
  }))
222
283
  }
223
284
  })
224
-
225
285
  } catch (err) {
226
286
  if (err.code === 'ENOENT') {
227
287
  // 4. 文件不存在时初始化空文件
@@ -240,9 +300,9 @@ class JSONCRUD {
240
300
  }
241
301
 
242
302
  // 通用保存方法
243
- async persist() {
303
+ async persist(entityName) {
244
304
  if (this.useMemory) return
245
- await this.saveToFile()
305
+ await this.saveToFile(entityName)
246
306
  }
247
307
 
248
308
  }
package/lib/server.js CHANGED
@@ -92,6 +92,11 @@ function createServer(app, options) {
92
92
  app.use(utils.rewriteCrabaJs);
93
93
  }
94
94
 
95
+ // 统一拦截login.html
96
+ if (config.modName && !config.localLogin) {
97
+ app.use(utils.rewriteLoginHTML);
98
+ }
99
+
95
100
  // 指定静态文件目录
96
101
  var webPath = config.webPath;
97
102
  if (options) {
@@ -111,7 +116,7 @@ function createServer(app, options) {
111
116
  res.setHeader('Content-Type', 'text/html; charset=utf-8'); // 当做文本输出,才能显示到iframe中
112
117
  }
113
118
  var name = path.basename(pathname);
114
- if (name == 'index.html') {
119
+ if (name == 'index.html' || name == 'login.html') {
115
120
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
116
121
  res.setHeader('Pragma', 'no-cache');
117
122
  res.setHeader('Expires', '0');
package/lib/utils.js CHANGED
@@ -527,6 +527,43 @@ class Utils {
527
527
  res.end(content);
528
528
  }
529
529
 
530
+
531
+ rewriteLoginHTML(req, res, next) {
532
+ var url = req.originalUrl;
533
+ if (url.startsWith('/login.html')) {
534
+ var htmlPath = utils.join(__dirname, '../res/login.html');
535
+ var content = fs.readFileSync(htmlPath).toString();
536
+ content = content.replaceAll('${modName}', config.modName);
537
+
538
+ res.setHeader('Content-Type', 'text/html; charset=utf-8'); // 当做文本输出,才能显示到iframe中
539
+ res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
540
+ res.setHeader('Pragma', 'no-cache');
541
+ res.setHeader('Expires', '0');
542
+ res.end(content);
543
+ } else if (url.startsWith('/crabatool/Login.gspx')) {
544
+ var htmlPath = utils.join(__dirname, '../res/Login.gspx');
545
+ var content = fs.readFileSync(htmlPath).toString();
546
+ content = content.replaceAll('${modName}', config.modName);
547
+
548
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8'); // 当做文本输出,才能显示到iframe中
549
+ res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
550
+ res.setHeader('Pragma', 'no-cache');
551
+ res.setHeader('Expires', '0');
552
+ res.end(content);
553
+ } else if (url.startsWith('/crabatool/Login.js')) {
554
+ var htmlPath = utils.join(__dirname, '../res/Login.js');
555
+ var content = fs.readFileSync(htmlPath).toString();
556
+ content = content.replaceAll('${modName}', config.modName);
557
+
558
+ res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
559
+ res.setHeader('Pragma', 'no-cache');
560
+ res.setHeader('Expires', '0');
561
+ res.end(content);
562
+ } else {
563
+ next();
564
+ }
565
+ }
566
+
530
567
  getFiles(options, fileList) {
531
568
  var exts = options.exts;
532
569
  var source = options.source;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crabatool",
3
- "version": "1.0.410",
3
+ "version": "1.0.415",
4
4
  "description": "crabatool",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -28,7 +28,10 @@
28
28
  "makeHash2": "node run.js -makeHash -webPath ./test/ -modName craba -files ./test/ -exts .js -outJson ./hash2.json -hashName sha384",
29
29
  "pageHash": "node run.js -pageHash /Test/TestMasterDetail.gspx",
30
30
  "addVersion": "node run.js -addVersion -file F:\\basicweb\\www\\index.html -exp AUTOVERSION",
31
- "gspxHash": "node run.js -gspxHash -webPath F:\\Beefun\\srcs\\Beefun"
31
+ "gspxHash": "node run.js -gspxHash -webPath F:\\Beefun\\srcs\\Beefun",
32
+ "runShell": "node run.js -run -webPath F:\\shell-gerrit\\web\\src\\main\\resources\\static\\shell\\ -port 10000 -modName shell",
33
+ "runShellLocal": "node run.js -run -webPath F:\\shell-gerrit\\web\\src\\main\\resources\\static\\shell\\ -port 10000 -modName shell -localLogin true",
34
+ "runJxc": "node run.js -run -webPath F:\\jxc\\jxc-web\\src\\main\\resources\\static\\jxc\\ -port 10001 -modName jxc"
32
35
  },
33
36
  "author": "wssf",
34
37
  "dependencies": {
package/res/Login.gspx ADDED
@@ -0,0 +1,34 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Page xmlns="Craba.UI" Title="NGP开发环境统一登录入口" ActionType="crabatool.LoginAction, crabatool/Login.js" DataSource='${formData}'>
3
+ <FlexColumn CssClass='FlexCenter MainBg'>
4
+ <FlowPanel Caption='Ngp模块登录' CssClass='VertItem' LayoutDirection='Vert' ItemLabelWidth='70' ItemCssClass='FlexAuto'>
5
+ <TextEdit Label='公司名称:' DataField='companyName' Required="true" />
6
+ <TextEdit Label='职员名称:' DataField='employeeName' Required="true" />
7
+ <DropDownEdit Label='指定产品:' DataField='productId' DataSource='${formData.products}' DataTextField='name' DataValueField='id' Required="true" OnChange="doChangeProduct" />
8
+ <HBlock Label='服务版本:'>
9
+ <TextEdit DataField='deploy' CssClass='FlexAuto' NullDisplayText='不填写则使用默认账套账本' />
10
+ <DropDownEdit DataField='productDeploy' DataSource="${formData.productDeploys}" DataTextField='deployName' DataValueField='deployName' />
11
+ </HBlock>
12
+ <TextEdit Label='本地端口:' DataField='localPort' Required="true" />
13
+
14
+ <Grid ID='grid' DataField="routes" AutoMaxRowCount="8" AllowConfig='false' ReadOnly='false'>
15
+ <TextColumn Caption="模块名称" DataField="serverName" AllowStretch='true' NullDisplayText='微服务名称' />
16
+ <TextColumn Caption="Ip" DataField="serverIp" AllowStretch='true' NullDisplayText='微服务IP地址' />
17
+ <TextColumn Caption="端口" DataField="serverPort" AllowStretch='true' NullDisplayText='微服务端口号' />
18
+ <RowDeleteColumn Caption="操作" />
19
+ </Grid>
20
+
21
+ <FlexBlock CssClass='BottomBlock FlexRight'>
22
+ <Button Text='添加路由' OnClick='addRoute' />
23
+ <Button Text='登录' CssClass='SpecialButton' OnClick="doLogin" />
24
+ </FlexBlock>
25
+ </FlowPanel>
26
+ </FlexColumn>
27
+
28
+ <Style>
29
+ .MainBg{background-color: #374979;}
30
+ .MainBg .FlowPanel{border-radius:8px;box-shadow:0 0 18px #111;width:600px;}
31
+ .MainBg .FlowPanel .FlowItem{padding:10px 20px;margin:0;}
32
+ .MainBg .GridBlock{margin:10px 20px}
33
+ </Style>
34
+ </Page>
package/res/Login.js ADDED
@@ -0,0 +1,155 @@
1
+ Type.registerNamespace('crabatool');
2
+ crabatool.LoginAction = function() {
3
+ crabatool.LoginAction.initializeBase(this);
4
+ };
5
+
6
+ crabatool.LoginAction.prototype = {
7
+ context: function(cb) {
8
+ this.initData(cb);
9
+ },
10
+
11
+ initialize: function LoginAction$initialize() {
12
+ crabatool.LoginAction.callBaseMethod(this, 'initialize');
13
+ },
14
+
15
+ initData: function(cb) {
16
+ var data = {
17
+ companyName: "",
18
+ employeeName: "",
19
+ productId: "0",
20
+ deploy: "",
21
+ localPort: "",
22
+ routes: [],
23
+ products: [],
24
+ deploys: {},
25
+ productDeploys: [],
26
+ productDeploy: ""
27
+ };
28
+ var formData = { formData: data }; // 窗体数据
29
+
30
+ var routes = localStorage.getItem("routes");
31
+ if (routes) {
32
+ routes = JSON.parse(routes);
33
+ data.routes = routes;
34
+ }
35
+
36
+ var user = localStorage.getItem("user");
37
+ if (user) {
38
+ user = JSON.parse(user);
39
+ data.companyName = user.companyName;
40
+ data.employeeName = user.employeeName;
41
+ data.productId = user.productId;
42
+ data.deploy = user.deploy;
43
+ data.localPort = user.port || user.localPort || "";
44
+ data.productDeploy = user.productDeploy || "";
45
+ }
46
+
47
+ var route = localStorage.getItem("route");
48
+ if (route) {
49
+ route = JSON.parse(route);
50
+ this.setRoute(route, data);
51
+ this.login();
52
+ this.end(); // 不继续处理Login.gspx
53
+ return;
54
+ }
55
+
56
+ var that = this;
57
+ $common.ajax({
58
+ url: 'http://172.17.0.237:56789/ngp/auth/init',
59
+ type: 'GET',
60
+ success: function(res) {
61
+ if (res.code != "200") {
62
+ $common.alertError("初始化信息失败");
63
+ that.end();
64
+ return;
65
+ }
66
+
67
+ data.deploys = res.data.deploys;
68
+ data.products = res.data.products;
69
+ console.log(data.productId);
70
+ data.productDeploys = res.data.deploys[data.productId];
71
+ cb(formData);
72
+ },
73
+ error: function() {
74
+ $common.alertError("网络请求失败");
75
+ that.end();
76
+ }
77
+ });
78
+ },
79
+
80
+ dispose: function() {
81
+ crabatool.LoginAction.callBaseMethod(this, 'dispose');
82
+ },
83
+
84
+ setRoute: function(route, data) {
85
+ route.deploy = data.deploy || (data.productDeploy || route.deploy);
86
+ route.productId = (data.productId == "0") ? route.productId : data.productId;
87
+ var debugMs = {};
88
+ for (var key in route) {
89
+ debugMs[key] = route[key];
90
+ }
91
+ debugMs.aloneDeploy = '${modName}';
92
+ debugMs.gateway = "http://eshop.gateway.ngp.wsgjp.com.cn";
93
+ debugMs.aloneServer = "http://" + location.hostname + ":" + data.localPort;
94
+ $ms.router = {//设置请求路由
95
+ debugMs: debugMs,
96
+ 'ngp-authorization': 'jwt',
97
+ 'ngp-router': 'ngprt'
98
+ };
99
+ if (data.routes.length > 0) {
100
+ var server = {};
101
+ for (var i = 0; i < data.routes.length; i++) {
102
+ var r = data.routes[i];
103
+ server[r.serverName] = r.serverIp + ":" + r.serverPort;
104
+ }
105
+ $ms.server = server;
106
+ }
107
+ var cookieStr = JSON.stringify(route);
108
+ $common.setCookieNoEscape("ngp-route", cookieStr);
109
+ localStorage.setItem("route", JSON.stringify(route));
110
+ },
111
+ login: function() {
112
+ $common.setCookieNoEscape('ngp-authorization', 'jwt');
113
+ $common.setCookieNoEscape('ngp-router', 'ngprt');
114
+ $common.setCookie('debugMs', JSON.stringify($ms.router.debugMs));
115
+
116
+ location.reload();
117
+ },
118
+ doLogin: function(sender) {
119
+ var user = this.get_form().saveData({clear:true});
120
+ var data = {
121
+ companyName: user.companyName,
122
+ userName: user.employeeName
123
+ };
124
+ localStorage.setItem("user", JSON.stringify(user));
125
+ localStorage.setItem("routes", JSON.stringify(user.routes));
126
+
127
+ var that = this;
128
+ $common.ajax({
129
+ url: "http://172.17.0.237:56789/ngp/auth/login",
130
+ data: data,
131
+ success: function(res) {
132
+ if (res.code != "200") {
133
+ $common.alertError("登录失败,请检查公司名称和职员名称是否正确");
134
+ } else {
135
+ that.setRoute(res.data, user);
136
+ that.login();
137
+ }
138
+ },
139
+ error: function() {
140
+ $common.alertError("网络请求失败");
141
+ }
142
+
143
+ });
144
+ },
145
+ addRoute: function(sender) {
146
+ this.get_form().grid.appendRowData({});
147
+ },
148
+ doChangeProduct: function(sender) {
149
+ var pid = sender.get_value();
150
+ var deploys = this.get_context('formData').deploys;
151
+ var pdeploys = deploys[pid];
152
+ sender.get_form().productDeploy.set_items(pdeploys);
153
+ }
154
+ };
155
+ crabatool.LoginAction.registerClass('crabatool.LoginAction', Sys.UI.PageAction);
package/res/login.html ADDED
@@ -0,0 +1,102 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-cn" translate="no">
3
+
4
+ <head>
5
+ <title>NGP开发环境统一登录入口</title>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
7
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
8
+ <link rel="shortcut icon" type="image/x-icon" href="${modName}/favicon.ico" />
9
+ </head>
10
+
11
+ <body>
12
+ <script>
13
+ var modeName = '${modName}'; // 按各组的模块名称修改
14
+
15
+ function startPage() { // 供agency内部调用
16
+ var authorization = $common.getCookie('ngp-authorization');
17
+ var ngp_route = $common.getCookie('ngp-route');
18
+ if (authorization && ngp_route) {
19
+ $ms.router = {//设置请求路由
20
+ debugMs: JSON.parse($common.getCookie('debugMs')),
21
+ 'ngp-authorization': 'jwt',
22
+ 'ngp-router': 'ngprt'
23
+ };
24
+
25
+ var routes = localStorage.getItem("routes");
26
+ if (routes) {
27
+ routes = JSON.parse(routes);
28
+ if (routes.length > 0) {
29
+ var server = {};
30
+ for (var i = 0; i < routes.length; i++) {
31
+ var r = routes[i];
32
+ server[r.serverName] = r.serverIp + ":" + r.serverPort;
33
+ }
34
+ $ms.server = server;
35
+ }
36
+ }
37
+
38
+ var srcs = [];
39
+ srcs.push('shell/js/help.plug.js?rv=' + Math.random());
40
+ srcs.push('shell/js/init.js?rv=' + Math.random());
41
+ $common.loadScript(srcs, function() {
42
+ showMainPage();
43
+ });
44
+ return;
45
+ }
46
+ showLoginPage();
47
+ }
48
+
49
+ function showMainPage() {
50
+ $skin.loadCss(modeName + '/skins/craba.min.css,shell/skins/shell.css', 'login', function() {
51
+ $craba.run(modeName + '/Main.gspx', modeName);
52
+ createLoginBtn();
53
+ });
54
+ }
55
+
56
+ function showLoginPage() {
57
+ $skin.loadCss(modeName + '/skins/craba.min.css', 'login', function() {
58
+ $craba.run('crabatool/Login.gspx', modeName);
59
+ });
60
+ }
61
+
62
+ function createLoginBtn() {
63
+ var btn = $common.createClassDiv('Button');
64
+ btn.innerText = '重新登录';
65
+ btn.style.cssText = 'z-index: 9999;font-weight: bold;background-color:#03abf5;border:none;position:fixed;bottom:5px;right:5px;';
66
+ document.body.appendChild(btn);
67
+ $common.addClickHandler(btn, function() {
68
+ localStorage.removeItem("route");
69
+ $common.removeCookie('ngp-authorization');
70
+ location.reload();
71
+ });
72
+ }
73
+
74
+ function loadScript(src, cb) {
75
+ var script = document.createElement('script');
76
+ script.type = 'text/javascript';
77
+ script.onload = script.onreadystatechange = function() {
78
+ if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") {
79
+ script.onload = script.onreadystatechange = script.onerror = null;
80
+ cb();
81
+ }
82
+ };
83
+ script.onerror = function() {
84
+ script.onload = script.onreadystatechange = script.onerror = null;
85
+ cb();
86
+ };
87
+ script.src = src;
88
+ document.getElementsByTagName('HEAD')[0].appendChild(script);
89
+ }
90
+
91
+ var agencyUrl = 'js/agency.js?vc=' + (new Date()).getTime(); // 加时间戳防止浏览器缓存
92
+ loadScript(agencyUrl, function() {
93
+ $agency.load(['js/craba.min.js',
94
+ 'js/crabaEx.min.js',
95
+ 'js/math.min.js',
96
+ 'js/crabaNgp.js'
97
+ ], {}, startPage); // 这里写死首页脚本,agency内部使用。所有脚本由agency内部版本号防止浏览器缓存
98
+ });
99
+ </script>
100
+ </body>
101
+
102
+ </html>
package/tool/checkjs.js CHANGED
@@ -250,7 +250,8 @@ module.exports.start = async function() {
250
250
  }
251
251
 
252
252
  reportData.data = {
253
- errorCount: errKeys.length + undefKeys.length,
253
+ errorCount: errKeys.length,
254
+ undefCount: undefKeys.length, // 未定义总数
254
255
  warnCount: warnKeys.length,
255
256
  tipCount: infoKeys.length,
256
257
  fileSize: fileSize,
@@ -281,7 +282,7 @@ module.exports.start = async function() {
281
282
  webhookList.push(`1. js语法undefined:${undefKeys.length}个,占比${calc(undefKeys.length, jsFiles.length)}`);
282
283
 
283
284
  // 随机显示5个异常文件到钉钉消息
284
- var errCount = reportData.data.errCount;
285
+ var errCount = reportData.data.errCount + reportData.data.undefCount;
285
286
  /*
286
287
  if (errCount > 0) {
287
288
  var list0 = [];
package/tool/compress.js CHANGED
@@ -88,6 +88,11 @@ function doCompress() {
88
88
  inNames = ['grid.js', 'controlsEx.js', 'treeView.js', 'print.js', 'workflow.js', '../../Carpa.Dress/Dress.js', 'htmlEditor.js'];
89
89
  compressCrabaJs(inNames, 'crabaEx.min.js');
90
90
 
91
+ // workflow流程图
92
+ //inNames = ['workflow.js'];
93
+ //compressCrabaJs(inNames, 'workflow.js');
94
+
95
+
91
96
  // echarts
92
97
  inNames = ['echarts4-all.js'];
93
98
  compressCrabaJs(inNames, 'echarts-all.js');
package/tool/start.js CHANGED
@@ -87,6 +87,8 @@ class Start {
87
87
  { name: '-abortUrl', type: '' },
88
88
  { name: '-reportHost', type: '' },
89
89
  { name: '-aiHost', type: '' },
90
+ { name: '-defaultPage', type: '' },
91
+ { name: '-localLogin', type: 'boolean' },
90
92
 
91
93
  { name: '-ignoreCheck', type: 'array' },
92
94
  { name: '-childModList', type: 'array' },
package/tool/gspxHash.js DELETED
@@ -1,90 +0,0 @@
1
-
2
- var config = require('../lib/config.js');
3
- var path = require('path');
4
- var fs = require('fs');
5
- var utils = require('../lib/utils.js');
6
- var os = require('os');
7
- const crypto = require('crypto');
8
- var requestSync = require('sync-request');
9
-
10
-
11
- module.exports.start = function() {
12
- if (!config.hashName) {
13
- config.hashName = 'sha384';
14
- }
15
-
16
- if (!config.files) {
17
- config.files = [];
18
- }
19
-
20
- var results = {};
21
- buildFiles(results, config.files);
22
-
23
- utils.mkdirsSync(config.outJson);
24
- fs.writeFileSync(config.outJson, JSON.stringify(results));
25
- }
26
-
27
- function buildFiles(results, files) {
28
- files.forEach(function(f) {
29
- console.log(f);
30
-
31
- var content;
32
- var isHttp = f.startsWith('http');
33
- if (isHttp) {
34
- var res = requestSync("GET", f);
35
- content = res.getBody();
36
- } else if (fs.existsSync(f)) {
37
-
38
- // 磁盘目录
39
- var statFs = fs.statSync(f);
40
- if (!statFs.isFile()) {
41
- var files2 = fs.readdirSync(f);
42
- files2 = files2.map(function(s) {
43
- return utils.join(f, s);
44
- });
45
- buildFiles(results, files2);
46
- return;
47
- }
48
-
49
- if (config.exts && config.exts.length > 0) {
50
- var ext = path.extname(f).toLowerCase();
51
- if (!config.exts.includes(ext)) {
52
- return;
53
- }
54
- }
55
-
56
- content = fs.readFileSync(f);
57
- } else {
58
- console.error('文件不存在:' + f);
59
- return;
60
- }
61
-
62
- var index = f.indexOf('?');
63
- if (index > 0) {
64
- f = f.substr(0, index);
65
- }
66
-
67
- // 转为相对webPath的路径
68
- if (!isHttp) {
69
- f = path.resolve(f);
70
- var webPath = path.resolve(config.webPath);
71
- f = f.replace(webPath, '');
72
- if (config.modName) {
73
- f = path.join(config.modName, f);
74
- }
75
- f = f.replace(/\\/g, '/');
76
- }
77
-
78
- // SRI Hash Generator
79
- // https://www.srihash.org/
80
-
81
- var base64 = makeHash(content, config.hashName); // 将文件内容进行hash计算
82
- results[f] = config.hashName + '-' + base64; // 使用 base64 编码 sha384 算法计算出摘要后的 integrity 值的示例
83
- });
84
- }
85
-
86
- function makeHash(str, name) {
87
- const hash = crypto.createHash(name);
88
- hash.update(str);
89
- return hash.digest('base64');
90
- }