abler-api 0.1.41 → 0.1.43

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.
@@ -1,2017 +0,0 @@
1
- import require$$0 from 'crypto';
2
- import require$$1 from 'abler-util';
3
- import require$$2 from 'abler-db';
4
- import require$$3 from 'abler-net';
5
- import require$$4 from 'fs';
6
- import require$$1$1 from 'path';
7
- import require$$1$2 from 'abler-messenger';
8
- import require$$0$1 from 'node-cron';
9
-
10
- const crypto = require$$0;
11
- const ppUtil$4 = require$$1.ppUtil;
12
- const {
13
- dbUtil,
14
- kvStorage
15
- } = require$$2;
16
- const {
17
- netUtil: netUtil$1
18
- } = require$$3;
19
- const fs$1 = require$$4;
20
- let conf$3, appSetting$1, err$2, dbSql;
21
- const pnToken = "access_token",
22
- hnToken = pnToken,
23
- pnApiKey = "apiKey",
24
- hnApiKey = pnApiKey.toLowerCase(),
25
- // 我们接收到的 headers 中的字段名总是全小写的
26
- pnApiSecret = "apiSecret",
27
- hnApiSecret = pnApiSecret.toLowerCase();
28
- const MD5 = ppUtil$4.MD5,
29
- moveProperty = ppUtil$4.moveProperty;
30
-
31
- class apiUtil$2 {
32
- static debugFlag = ppUtil$4.newGuid(); //应用必须设置,否则谁也不知道是啥
33
-
34
- static testFlag = ppUtil$4.newGuid();
35
- static envId_dev = "?"; // static apiCallRecSaver;
36
-
37
- static config(appConfig, appErrCfg, appDbSql) {
38
- ppUtil$4.config(appConfig, appErrCfg);
39
- conf$3 = appConfig;
40
- appSetting$1 = conf$3?.appSetting;
41
- err$2 = appErrCfg, dbSql = appDbSql;
42
- apiUtil$2.debugFlag = appSetting$1?.debugFlag || apiUtil$2.debugFlag;
43
- apiUtil$2.testFlag = appSetting$1?.testFlag || apiUtil$2.testFlag; // apiUtil.apiCallRecSaver = apiCallRecSaver;
44
- } //#region ===== 需要应用系统重写的方法
45
-
46
- /**
47
- * 根据机构标识获取机构的 ApiSecret
48
- * @param companyId
49
- * @returns {*}
50
- */
51
-
52
-
53
- static _getApiSecret(companyId) {
54
- return ppUtil$4.newGuid();
55
- }
56
- /**
57
- * 保存 API 调用记录,应用系统必须重写此方法
58
- * @param rec
59
- * @param responseData
60
- * @returns {Promise<unknown>}
61
- */
62
-
63
-
64
- static async _saveApiCallRec(rec, responseData) {
65
- return false;
66
- }
67
- /**
68
- * 检查API调用签名是否已经完成了验证
69
- * @param req
70
- */
71
-
72
-
73
- static signatureVerified(req) {
74
- return req.signatureVerified;
75
- } //#endregion
76
- //#region ===== API服务相关工具
77
-
78
- /**
79
- * 从请求中解出数据,包括路径参数、查询参数和Body
80
- * @param req
81
- * @returns {*}
82
- */
83
-
84
-
85
- static extractParams(req) {
86
- if (!req._requestParams) {
87
- if (req.method === "POST" || req.method === "PUT") {
88
- let contentType = req.headers["content-type"] || "application/json";
89
-
90
- if (contentType.startsWith("multipart/form-data")) {
91
- req._requestParams = new Object();
92
-
93
- for (let k in req.body) {
94
- req._requestParams[k] = req.body[k];
95
- }
96
- } else {
97
- req._requestParams = req.body;
98
- }
99
- } else {
100
- req._requestParams = req.query;
101
- }
102
-
103
- if (req.headers["content-type"] !== "application/json") {
104
- const obj = req._requestParams;
105
-
106
- for (let key in obj) {
107
- if (!obj.hasOwnProperty(key)) continue;
108
- let value = obj[key];
109
-
110
- if (typeof value === "string" && value.length > 0) {
111
- if (value[0] === "{" || value[0] === "[") {
112
- try {
113
- obj[key] = JSON.parse(value);
114
- continue;
115
- } catch (e) {}
116
- }
117
-
118
- let x = JSON.hbDateConv(key, value);
119
-
120
- if (x !== value) {
121
- obj[key] = x;
122
- }
123
- }
124
- }
125
- }
126
-
127
- if (req.params) {
128
- req._requestParams = Object.assign(req.params, req._requestParams);
129
- }
130
-
131
- apiUtil$2.setParamsFunctions(req._requestParams);
132
- }
133
-
134
- return req._requestParams;
135
- }
136
-
137
- static extractTokenData(req) {
138
- if (req.tokenData) {
139
- return req.tokenData;
140
- }
141
-
142
- let params = apiUtil$2.extractParams(req);
143
- let tokenData = {
144
- apiKey: req.headers[hnApiKey] || params[pnApiKey],
145
- apiSecret: req.headers[hnApiSecret] || params[pnApiSecret],
146
- clientIp: apiUtil$2.getClientIp(req)
147
- };
148
- return tokenData;
149
- }
150
- /**
151
- * 为从传入请求中解出的参数对象设置一些函数属性,包括 asNumber, asBool
152
- * @param params
153
- */
154
-
155
-
156
- static setParamsFunctions(params) {
157
- ppUtil$4.defUnenumProp(params, "asNumber", function (name, minValue, maxValue, defaultValue) {
158
- // if (!this.hasOwnProperty(name)) // {} 定义的对象没有 hasOwnProperty 函数
159
- if (this[name] === undefined) return defaultValue;
160
- let result = +this[name];
161
-
162
- if (typeof result !== "number" || result < minValue || result > maxValue) {
163
- throw [err$2.INVALID_PARAM, `参数 ${name} 必须是 ${minValue} ~ ${maxValue} 之间的数字`];
164
- }
165
-
166
- return result;
167
- });
168
- ppUtil$4.defUnenumProp(params, "asBool", function (name, defaultValue) {
169
- if (this[name] === undefined) return defaultValue;
170
- let result = JSON.parse(this[name]);
171
- return !!result;
172
- });
173
- }
174
- /**
175
- * 将返回结果转向到指定地址或者通过response发送返回结果
176
- * @param res
177
- * @param redirectUrl
178
- * @param data
179
- */
180
-
181
-
182
- static sendOrRedirect(res, redirectUrl, data) {
183
- if (redirectUrl) {
184
- redirectUrl = netUtil$1.urlAppendParams(redirectUrl, data, true);
185
- res.redirect(redirectUrl);
186
- } else {
187
- res.send(data);
188
- }
189
- }
190
- /**
191
- * 生成 API 成功返回结果
192
- * @param data
193
- * @param req
194
- * @returns {{datetime: Date, success: boolean, stateCode: number, message: string}}
195
- */
196
-
197
-
198
- static apiSuccess(data, req) {
199
- let response = {
200
- success: true,
201
- stateCode: 0,
202
- message: '',
203
- datetime: new Date()
204
- };
205
-
206
- if (req && req.headers) {
207
- let userAgent = req.headers["user-agent"];
208
-
209
- if (userAgent && userAgent.startsWith("Postman")) {
210
- response._elapse = (new Date().valueOf() - req.headers.timestamp) / 1000;
211
- }
212
- }
213
-
214
- if (data && data._message_) {
215
- response.message = data._message_;
216
- delete data._message_;
217
- }
218
-
219
- response.data = data;
220
- if (Array.isArray(data) && typeof data.recordCount != 'undefined') response.recordCount = data.recordCount;
221
- return response;
222
- }
223
- /**
224
- * 生成 API 失败返回结果
225
- * @param error
226
- * @param req
227
- * @returns {*}
228
- */
229
-
230
-
231
- static apiFail(error, req) {
232
- configNeeded();
233
- let response = err$2.ERROR(error, err$2.errorLangParamFlag + ppUtil$4.getMsgLang(req));
234
- response.datetime = new Date();
235
-
236
- if (req && req.headers) {
237
- let userAgent = req.headers["user-agent"];
238
-
239
- if (userAgent && userAgent.startsWith("Postman")) {
240
- response._elapse = (new Date().valueOf() - req.headers.timestamp) / 1000;
241
- }
242
- }
243
-
244
- return response;
245
- }
246
- /**
247
- * 生成面向服务商的API成功返回结果
248
- * @param data
249
- * @param req
250
- * @returns {*}
251
- */
252
-
253
-
254
- static spoApiSucc(data, req) {
255
- let params = apiUtil$2.extractParams(req);
256
- return apiUtil$2.apiSuccess(data, req, params.spOrderNum);
257
- }
258
- /**
259
- * 生成面向服务商的API失败返回结果
260
- * @param error
261
- * @param req
262
- * @returns {*}
263
- */
264
-
265
-
266
- static spoApiFail(error, req) {
267
- let params = apiUtil$2.extractParams(req);
268
- return apiUtil$2.apiFail(error, req, params.spOrderNum);
269
- }
270
- /**
271
- * API 服务结束,发送响应
272
- * @param response
273
- * @param promise
274
- */
275
-
276
-
277
- static sendResponse(response, promise) {
278
- promise.then(function (data) {
279
- if (!response.finished) {
280
- try {
281
- response.send(data);
282
- } catch (e) {
283
- console.log(`response.send 出错了,req.url: ${response.req.url}, response:`, response);
284
- console.log("异常信息", e);
285
- }
286
- }
287
- }, function (error) {
288
- console.log('ERROR:', error);
289
-
290
- if (!response.finished) {
291
- response.send(error);
292
- }
293
- });
294
- }
295
- /**
296
- * API 服务结束,包装数据并发送响应
297
- * @param response
298
- * @param promise
299
- */
300
-
301
-
302
- static async responseOf(response, promise) {
303
- promise.then(function (data) {
304
- // console.log('RES:', apiSuccess(data));
305
- if (!response.finished) response.send(apiUtil$2.apiSuccess(data, response.req));
306
- }, function (error) {
307
- console.log('ERROR:', error);
308
- if (!response.finished) response.send(apiUtil$2.apiFail(error, response.req));
309
- });
310
- }
311
- /**
312
- * 面向服务商的API结束,发送响应
313
- * @param response
314
- * @param promise
315
- */
316
-
317
-
318
- static async spoApiResponse(response, promise) {
319
- promise.then(function (result) {
320
- let responseData = apiUtil$2.spoApiSucc(result, response.req);
321
-
322
- apiUtil$2._saveApiCallRec(response.req.apiCallRec, responseData);
323
-
324
- if (!response.finished) response.send(responseData);
325
- }, function (error) {
326
- console.log('ERROR:', error);
327
- let responseData = apiUtil$2.spoApiFail(error, response.req);
328
-
329
- apiUtil$2._saveApiCallRec(response.req.apiCallRec, responseData);
330
-
331
- if (!response.finished) response.send(responseData);
332
- });
333
- }
334
- /**
335
- * 从请求参数中获取查询参数
336
- * @param req
337
- * @param res
338
- * @returns {*}
339
- */
340
-
341
-
342
- static getQueryOptions(req, res) {
343
- req.headers.timestamp = new Date().valueOf(); // let options = {replacements: {}};
344
-
345
- let options = {};
346
- let parameters = apiUtil$2.extractParams(req);
347
-
348
- for (let qryParam in parameters) {
349
- if (qryParam !== 'token') {
350
- if (['start', 'limit', 'orderby', 'orderBy'].indexOf(qryParam) >= 0) {
351
- options[qryParam] = parameters[qryParam];
352
- delete parameters[qryParam];
353
- } // else
354
- // options.replacements[qryParam] = parameters[qryParam];
355
-
356
- }
357
- }
358
-
359
- if (options.limit && !options.start) {
360
- options.start = 0;
361
- }
362
-
363
- if (options.orderby) {
364
- options.orderBy = options.orderby;
365
- } // options.__debug__ = req.headers['__debug__'] && req.headers['__debug__'] == "true";
366
-
367
-
368
- options.replacements = parameters; // if (arguments.length > 1) //其后的每个参数是要求必须存在的参数名称
369
- // {
370
- // for(i=1; i<arguments.length; i++){
371
- // var paramName = arguments[i];
372
- // if (!qryOptions.replacements[paramName])
373
- // error();
374
- // }
375
- // }
376
-
377
- options.userInfo = req.userInfo;
378
- return apiUtil$2.setOptionsPropFuncions(options, res);
379
- }
380
- /**
381
- * 为API请求 options 设置属性
382
- * @param options
383
- * @param res
384
- * @returns {{identifyLimit}|*}
385
- */
386
-
387
-
388
- static setOptionsPropFuncions(options, res) {
389
- // if (!options.identifyLimit) {
390
- // // 根据企业标识获取身份认证限制相关设置,如果没有传递企业标识,
391
- // // 则尝试从options.companyInfo或options.contractOrder中获取
392
- // Object.defineProperty(options, "companyId", {
393
- // get: function () {
394
- // var result;
395
- // if (this.companyInfo) {
396
- // result = this.companyInfo.companyId;
397
- // } else if (this.contractOrder) {
398
- // result = this.contractOrder.companyId;
399
- // } else if (this._companyId) {
400
- // result = this._companyId;
401
- // }
402
- // return result;
403
- // }
404
- // });
405
- // Object.defineProperty(options, "identifyLimit", {
406
- // get: function () {
407
- // if (!this._identifyLimit) {
408
- // // this._identifyLimit = ppUtil.waitPromise(_getIdentifyLimit(this.companyId));
409
- // throw "options._identifyLimit 没有赋值";
410
- // }
411
- //
412
- // return this._identifyLimit;
413
- // },
414
- // // configurable : true
415
- // });
416
- // }
417
- if (res) {
418
- Object.defineProperty(options, '_res', {
419
- value: res,
420
- enumerable: false
421
- });
422
- }
423
-
424
- return options;
425
- }
426
- /**
427
- * 从请求头获取token
428
- * @param req
429
- * @param res
430
- * @param noErr
431
- * @returns {Promise<*>}
432
- */
433
-
434
-
435
- static extractToken(req, res, noErr) {
436
- configNeeded();
437
-
438
- if (!req.accessToken) {
439
- //检查post的信息或者url查询参数或者头信息
440
- let token = req.headers[hnToken] || req.headers['x-access-token']; //向后兼容
441
-
442
- if (!token) {
443
- let params = apiUtil$2.extractParams(req);
444
- token = params[pnToken];
445
-
446
- if (token) {
447
- delete params[pnToken];
448
- } else if (params.token) {
449
- token = params.token; //向后兼容
450
-
451
- delete params.token;
452
- } else {
453
- // 如果没有token,则返回错误
454
- console.log(err$2.TOKEN_NEEDED);
455
-
456
- if (res) {
457
- res.send(err$2.ERROR(err$2.TOKEN_NEEDED));
458
- } else if (!noErr) {
459
- throw err$2.TOKEN_NEEDED;
460
- }
461
- }
462
- }
463
-
464
- req.accessToken = token;
465
- }
466
-
467
- return req.accessToken;
468
- }
469
- /**
470
- * 检查是不是内部调用,应用系统可替换此函数实施更隐秘的检查
471
- * @param req
472
- * @returns {Promise<boolean|undefined>}
473
- */
474
-
475
-
476
- static async checkInternalToken(req) {
477
- // return await checkInternalToken(req);
478
- const params = apiUtil$2.extractParams(req);
479
- req.accessToken = req.accessToken || apiUtil$2.extractToken(req);
480
-
481
- if (req.accessToken === MD5(ppUtil$4.idNumDisturbing)) {
482
- //todo: 检查IP
483
- params.__internal__ = true;
484
- req.ignoreToken = true; // if (params.idNum || params.personId) {
485
- // return queryPersonUser(params.idNum, params.personId)
486
- // .then(async userInfo => {
487
- // if (!userInfo) {
488
- // // console.log('queryPersonUser error.', e);
489
- // throw err.TOKEN_INVALID;
490
- // }
491
- // if (req.tokenValidater) await req.tokenValidater(userInfo, req)
492
- // req.userInfo = userInfo;
493
- // return true
494
- // })
495
- // } else {
496
- // return true
497
- // }
498
-
499
- return true;
500
- } else {
501
- // console.log('token error.', req.accessToken);
502
- throw err$2.TOKEN_INVALID;
503
- }
504
- }
505
- /**
506
- * 检查请求是否存在合法的token,若不存在则抛出异常
507
- * @param req
508
- * @returns {Promise<void>}
509
- */
510
-
511
-
512
- static async checkRequestToken(req) {
513
- req.accessToken = req.accessToken || apiUtil$2.extractToken(req);
514
- const tokenKey = apiUtil$2.userTokenStoreKey(req.accessToken);
515
- const expireTime = apiUtil$2.tokenExpireTime(req);
516
- return await apiUtil$2.restoreTokenData(tokenKey, expireTime).then(async function (data) {
517
- req.userInfo = apiUtil$2.setUserIdNo(data, data._idNo);
518
- if (req.tokenValidater) await req.tokenValidater(req.userInfo, req);
519
- return true;
520
- }, async function (e) {
521
- // 如果给定的token不存在,看看是不是平台内部调用
522
- return await apiUtil$2.checkInternalToken(req);
523
- });
524
- }
525
- /**
526
- * 检查是不是调试模式
527
- * @param req
528
- * @returns {*|boolean|boolean}
529
- */
530
-
531
-
532
- static isDebugMode(req) {
533
- if (req.__debug__ === undefined) {
534
- let dbgToken = req.headers['__debug__'] || req.headers[apiUtil$2.debugFlag];
535
-
536
- if (dbgToken) {
537
- let envId = process.env.ECS_DEPLOY_ID;
538
- req.__debug__ = dbgToken && dbgToken === MD5(req.headers.timestamp, ppUtil$4.commonHashDisturbing) && (envId === "florist_longdan" || envId === "shuzi");
539
- } else {
540
- req.__debug__ = false;
541
- } // console.log("__debug__:", req.__debug__, "dbgToken:", dbgToken);
542
-
543
- }
544
-
545
- return req.__debug__; // return req.headers["postman-token"] || req.headers['__debug__'] && req.headers['__debug__'] == "true";
546
- }
547
- /**
548
- * 检查是不是测试模式
549
- * @param req
550
- * @returns {*|boolean}
551
- */
552
-
553
-
554
- static isTestMode(req) {
555
- if (req.__postman__ === undefined) {
556
- let dbgToken = req.headers["__postman__"] || req.headers[apiUtil$2.testFlag];
557
-
558
- if (dbgToken) {
559
- let envId = process.env.ECS_DEPLOY_ID;
560
- let testToken = MD5(req.headers.timestamp, apiUtil$2.extractToken(req, null, true) || req.headers.apikey);
561
- req.__postman__ = dbgToken === testToken && (envId.indexOf("myfacesign.com") < 0 || envId.indexOf(".dev.") > 0);
562
- } else {
563
- req.__postman__ = false;
564
- }
565
- }
566
-
567
- return req.__postman__;
568
- }
569
- /**
570
- * 获取令牌过期时长
571
- * @param req
572
- * @returns {any}
573
- */
574
-
575
-
576
- static tokenExpireTime(req) {
577
- return apiUtil$2.isDebugMode(req) ? 30 * 24 * 60 * 60 : apiUtil$2.isTestMode(req) ? 4 * 60 * 60 : appSetting$1.tokenExpireTime;
578
- }
579
- /**
580
- * 获取用户令牌保存键
581
- * @param token
582
- * @returns {string}
583
- */
584
-
585
-
586
- static userTokenStoreKey(token) {
587
- return "ut_" + token;
588
- }
589
- /**
590
- * 获取机构令牌保存键
591
- * @param token
592
- * @returns {string}
593
- */
594
-
595
-
596
- static apiTokenStoreKey(token) {
597
- return "api_" + token;
598
- }
599
- /**
600
- * 判断是不是香港身份证
601
- * @param idNum
602
- * @returns {boolean}
603
- */
604
-
605
-
606
- static checkIsHKIC(idNum) {
607
- return idNum.length === 8;
608
- }
609
- /**
610
- * 将身份证号码暂存到用户信息中
611
- * @param userInfo
612
- * @param idNum
613
- * @returns {*}
614
- */
615
-
616
-
617
- static setUserIdNo(userInfo, idNum) {
618
- if (userInfo && idNum) {
619
- userInfo._idNo = idNum;
620
- userInfo.isHKIC = apiUtil$2.checkIsHKIC(idNum);
621
- }
622
-
623
- return userInfo;
624
- }
625
- /**
626
- * 暂存令牌
627
- * @param token
628
- * @param tokenData
629
- * @param dataId
630
- * @param req
631
- * @returns {Promise<void>}
632
- */
633
-
634
-
635
- static async storeToken(token, tokenData, dataId, req) {
636
- // let timeout = tokenData.clientIp.substring(0, 8) == "192.168." ? 90 * 24 : 1;
637
- let timeout = apiUtil$2.tokenExpireTime(req);
638
-
639
- if (req.certPublicKey) {
640
- tokenData.certPublicKey = req.certPublicKey;
641
- }
642
-
643
- kvStorage.storeObj(token, tokenData, timeout);
644
-
645
- if (dataId) {
646
- kvStorage.restoreValue("token_" + dataId).then(function (oldToken) {
647
- if (oldToken !== token) {
648
- kvStorage.unstoreValue(oldToken);
649
- } else {
650
- dataId = "";
651
- }
652
- }, function (e) {}).then(() => {
653
- if (dataId !== "") kvStorage.storeValue("token_" + dataId, token, timeout);
654
- });
655
- }
656
- }
657
- /**
658
- * 恢复令牌数据
659
- * @param accessToken
660
- * @param expireTime
661
- * @returns {Promise<any>}
662
- */
663
-
664
-
665
- static async restoreTokenData(accessToken, expireTime) {
666
- return await kvStorage.restoreObj(accessToken, expireTime);
667
- }
668
- /**
669
- * 创建 API 调用记录
670
- * @param tokenData
671
- * @param req
672
- * @returns {any}
673
- */
674
-
675
-
676
- static createApiCallRec(tokenData, req) {
677
- if (!req.apiCallRec) {
678
- if (req.apiCallRecId) {
679
- return dbQueryOne(dbSql.loadApiUsage, {
680
- replacements: {
681
- id: req.apiCallRecId
682
- }
683
- }).then(rec => {
684
- req.apiCallRec = rec;
685
- return req.apiCallRec;
686
- });
687
- } else {
688
- let params = extractParams(req); // let contentLength = parseInt(req.headers["content-length"]) || 0;
689
-
690
- let rec = {
691
- id: newGuid(),
692
- companyId: tokenData.apiKey || '',
693
- orderId: params.orderId || '',
694
- signerId: params.signerId || '',
695
- userId: params.userId || '',
696
- contractName: params.contractName || '',
697
- spOrderNum: params.spOrderNum || '',
698
- notifyUri: params.notifyUri || '',
699
- fileName: params.fileName || '',
700
- fileHash: params.fileHash || '',
701
- fileKeyHash: params.fileKeyHash || '',
702
- fileSignedHash: params.fileSignedHash || '',
703
- createTime: new Date(),
704
- secret: tokenData.apiSecret || '',
705
- fromIp: tokenData.clientIp || '',
706
- method: req.method,
707
- uri: req.route.path,
708
- urlParams: req.url.split("?")[1] || "",
709
- contentType: (req.headers["content-type"] || '').substring(0, 45),
710
- // body: contentLength < 500 ? req.rawBody || '' : 'lob data, size:' + contentLength,
711
- body: _getBodyLogStr(req),
712
- resultCode: params.resultCode || '0',
713
- resultInfo: params.resultInfo || '',
714
- resultData: params.resultData || ''
715
- };
716
-
717
- if (rec.secret !== "") {
718
- rec.secret = ppUtil$4.getEncAse192(rec.secret);
719
- }
720
-
721
- req.apiCallRec = rec;
722
- }
723
- }
724
-
725
- return req.apiCallRec;
726
- }
727
- /**
728
- * 查询机构信息
729
- * @param companyId
730
- * @param nullable
731
- * @returns {Promise<null|*>}
732
- */
733
-
734
-
735
- static async queryCompanyInfo(companyId, nullable) {
736
- return await dbUtil.dbQueryOne(dbSql.queryCompanyInfo, {
737
- companyId
738
- }, nullable);
739
- }
740
- /**
741
- * 获取企业有效证书
742
- * @param companyId: 企业标识
743
- */
744
-
745
-
746
- static async getCompanyCertificate(companyId) {
747
- let certInfo = {
748
- // 正式启用的证书
749
- certLiving: null,
750
- certLivingId: '',
751
- // 备用证书
752
- certSpare: null,
753
- certSpareId: ''
754
- };
755
- return await dbUtil.dbQueryOne(dbSql.queryCertDataByOwner, {
756
- ownerId: companyId,
757
- applyId: "company"
758
- }, false).then(certRec => {
759
- // console.log('当前证书:\n', certRec);
760
- // checkCertStatus(certRec);
761
- let certData = JSON.parse(certRec.certBody);
762
- let pubKeyData = Buffer.from(certData.cert, "base64").toString(); // 公司正式证书
763
-
764
- certInfo.certLiving = pubKeyData;
765
- certInfo.certLivingId = certRec.id;
766
- return dbUtil.dbQueryOne(dbSql.queryCertDataByOwner, {
767
- ownerId: companyId,
768
- applyId: "company_spare"
769
- }, false).then(certRec1 => {
770
- // console.log('备用证书:\n', certRec1);
771
- // checkCertStatus(certRec1);
772
- let certData1 = JSON.parse(certRec1.certBody);
773
- let pubKeyData1 = Buffer.from(certData1.cert, "base64").toString(); // 公司备用证书
774
-
775
- certInfo.certSpare = pubKeyData1;
776
- certInfo.certSpareId = certRec1.id;
777
- }).catch(e => {
778
- // console.log('获取备用证书失败。', e);
779
- certInfo.certSpare = null;
780
- });
781
- }).then(() => {
782
- return certInfo;
783
- }).catch(e => {
784
- console.log('获取企业证书失败:\n', e);
785
- throw [err$2.ACCESS_REFUSED, `获取企业证书失败:${err$2.ERROR(e).message}`];
786
- });
787
- }
788
- /**
789
- * 验证API调用签名
790
- * @param tokenData
791
- * @param req
792
- * @returns {Promise<*|boolean>}
793
- */
794
-
795
-
796
- static async verifyApiSignature(tokenData, req) {
797
- try {
798
- if (!req.headers.timestamp) {
799
- throw [err$2.ACCESS_REFUSED, `必须在请求头中设置时戳`];
800
- }
801
-
802
- let timestamp = parseInt(req.headers.timestamp);
803
- let currentTime = new Date().valueOf();
804
- let maxTimeDiff = apiUtil$2.isDebugMode(req) ? 7 * 24 * 3600 : apiUtil$2.isTestMode(req) ? 4 * 3600 : 100; // 100
805
-
806
- if (timestamp.toString() == "NaN" || currentTime < timestamp - 5000 || currentTime - timestamp > maxTimeDiff * 1000) {
807
- throw [err$2.ACCESS_REFUSED, `时戳(${timestamp})无效,当前时戳:${currentTime},时差 ${(currentTime - timestamp) / 1000}秒`];
808
- }
809
-
810
- let signature = req.headers.signature;
811
-
812
- if (!signature) {
813
- throw [err$2.ACCESS_REFUSED, `必须在请求头中提供签名`];
814
- }
815
-
816
- let signData = apiUtil$2.extractToken(req, null, true) || tokenData.apiKey;
817
- signData += timestamp;
818
-
819
- if (apiUtil$2.isDebugMode(req) || apiUtil$2.isTestMode(req)) {
820
- let publicKey = req.headers.publickey || req.body.publicKey || req.query.publicKey;
821
-
822
- if (publicKey) {
823
- publicKey = decodeURIComponent(publicKey);
824
- tokenData.certPublicKey = publicKey;
825
- req.certPublicKey = publicKey;
826
- }
827
- }
828
-
829
- if (tokenData.certPublicKey) {
830
- let verifyResult = doVerify(signData, signature, tokenData.certPublicKey);
831
- return await verifyResult;
832
- }
833
-
834
- return apiUtil$2.getCompanyCertificate(tokenData.apiKey).then(info => {
835
- tokenData.certPublicKey = info.certLiving;
836
- tokenData.certPublicKeySpare = info.certSpare;
837
- tokenData.certLivingId = info.certLivingId;
838
- tokenData.certSpareId = info.certSpareId;
839
- req.certPublicKey = info.certLiving;
840
- let token = apiUtil$2.extractToken(req, null, true);
841
-
842
- if (token) {
843
- apiUtil$2.storeToken(apiUtil$2.apiTokenStoreKey(token), tokenData, tokenData.apiKey, req);
844
- }
845
- }).then(() => {
846
- return doVerify(signData, signature, tokenData.certPublicKey);
847
- });
848
- } catch (e) {
849
- // return Promise.reject(e);
850
- throw e;
851
- }
852
-
853
- function doVerify(signData, signature) {
854
- let verify = crypto.createVerify('SHA256');
855
- verify.write(signData);
856
- verify.end();
857
- let verifyOK = false;
858
-
859
- try {
860
- // 先用正式证书验证
861
- verifyOK = verify.verify(tokenData.certPublicKey, signature, 'base64');
862
- } catch (e) {
863
- console.log("SPO服务签名验证异常(正式证书), ", e);
864
- verifyOK = false;
865
-
866
- if (!tokenData.certPublicKeySpare) {
867
- throw {
868
- eo: err$2.ACCESS_REFUSED,
869
- msgArgv: "签名验证异常",
870
- data: e
871
- };
872
- }
873
- }
874
-
875
- if (!verifyOK && tokenData.certPublicKeySpare) {
876
- try {
877
- // 再用备用证书验证
878
- let verifySpare = crypto.createVerify('SHA256');
879
- verifySpare.write(signData);
880
- verifySpare.end();
881
- verifyOK = verifySpare.verify(tokenData.certPublicKeySpare, signature, 'base64');
882
-
883
- if (verifyOK) {
884
- // 备用证书验证成功了,则将备用证书替换为正式证书
885
- console.log(`公司(${tokenData.apiKey})备用证书验签成功,正式启用...`);
886
- return dbUtil.dbTrans(trans => {
887
- let p1 = dbUtil.dbExec(dbSql.deleteCertRecById, {
888
- replacements: {
889
- id: tokenData.certLivingId
890
- },
891
- transaction: trans
892
- });
893
- let p2 = dbUtil.dbExec(dbSql.spareCertToLiving, {
894
- replacements: {
895
- id: tokenData.certSpareId
896
- },
897
- transaction: trans
898
- });
899
- return Promise.all([p1, p2]);
900
- }).then(() => {
901
- tokenData.certPublicKey = tokenData.certPublicKeySpare;
902
- delete tokenData.certLivingId;
903
- delete tokenData.certSpareId;
904
- delete tokenData.certPublicKeySpare;
905
- let token = apiUtil$2.extractToken(req);
906
-
907
- if (token) {
908
- apiUtil$2.storeToken(apiTokenStoreKey(token), tokenData, tokenData.apiKey, req);
909
- }
910
-
911
- console.log(`公司(${tokenData.apiKey})备用证书成功转换为正式证书`);
912
- });
913
- }
914
- } catch (e) {
915
- console.log("SPO服务签名验证异常(备用证书), ", e);
916
- throw {
917
- eo: err$2.ACCESS_REFUSED,
918
- msgArgv: "签名验证异常(spare)",
919
- data: e
920
- };
921
- }
922
- }
923
-
924
- if (!verifyOK) {
925
- console.log("SPO服务签名验证失败, tokenData: ", tokenData);
926
- throw [err$2.ACCESS_REFUSED, "签名验证未通过"];
927
- }
928
-
929
- return verifyOK;
930
- }
931
- }
932
- /**
933
- * 检查API调用之合法性
934
- * @param tokenData
935
- * @param req
936
- * @returns {Promise<any>}
937
- */
938
-
939
-
940
- static async checkApiCallValid(tokenData, req) {
941
- if (!tokenData.companyInfo) {
942
- tokenData.companyInfo = await apiUtil$2.queryCompanyInfo(tokenData.apiKey, true).catch(e => {
943
- console.log("执行企业信息查询发生异常", e);
944
- throw [err$2.API_KEY_INVALID, "执行企业信息查询发生异常:" + e.message || ""];
945
- });
946
- }
947
-
948
- const companyInfo = tokenData.companyInfo;
949
-
950
- if (companyInfo != null) {
951
- let secret = apiUtil$2._getApiSecret(tokenData.apiKey);
952
-
953
- if (secret !== tokenData.apiSecret) throw err$2.API_SCREPT_INVALID; // 不再检查 IP 白名单,改为验证证书签名
954
- // console.log("fromIp:", tokenData.clientIp, "ipWhiteList:", companyInfo.ipWhiteList);
955
- // let whiteList = companyInfo.ipWhiteList || "";
956
- // // 没有设置ip白名单的就不检查了
957
- // if (whiteList == "" || ipMatched(tokenData.clientIp, whiteList + ",127.0.0.1,1"))
958
-
959
- if (apiUtil$2.signatureVerified()) return companyInfo;
960
- return apiUtil$2.verifyApiSignature(tokenData, req).then(x => {
961
- return companyInfo;
962
- }, e => {
963
- throw e;
964
- });
965
- }
966
-
967
- throw [err$2.API_KEY_INVALID, tokenData.apiKey];
968
- }
969
- /**
970
- * 检查令牌数据是否存在
971
- * @param token
972
- * @returns {*}
973
- */
974
-
975
-
976
- static tokenDataExist(token) {
977
- return kvStorage.stored(token);
978
- }
979
-
980
- static parametersOK(params) {
981
- configNeeded();
982
- let paramSufix = params._param_sufix || "";
983
-
984
- for (let i = 1; i < arguments.length; i++) {
985
- let paramName = arguments[i];
986
- let paramExists = false;
987
-
988
- if (paramName.includes("/")) {
989
- let names = paramName.split("/"); // if (params[names[0]]) {
990
- // paramExists = true;
991
- // } else {
992
- // if (params[names[1]]) {
993
- // paramExists = true;
994
- // params[names[0]] = params[names[1]];
995
- // delete params[names[1]];
996
- // }
997
- // }
998
-
999
- for (let ind in names) {
1000
- let name = names[ind];
1001
-
1002
- if (typeof params[name] != 'undefined') {
1003
- paramExists = true;
1004
- break;
1005
- }
1006
-
1007
- if (paramSufix != "" && typeof params[name + paramSufix] != 'undefined') {
1008
- paramExists = true;
1009
- break;
1010
- }
1011
- }
1012
-
1013
- paramName = `${paramName} 之一`;
1014
- } else {
1015
- paramExists = typeof params[paramName] != 'undefined' || typeof params[paramName + paramSufix] != 'undefined';
1016
- }
1017
-
1018
- if (!paramExists) return ppUtil$4.errorPormise(err$2.ERROR(err$2.PARAMETER_NEEDED, paramName));
1019
- }
1020
-
1021
- return Promise.resolve(paramSufix);
1022
- }
1023
- /**
1024
- * 获取客户端IP地址
1025
- * @param req
1026
- * @returns {*|string|string}
1027
- */
1028
-
1029
-
1030
- static getClientIp(req) {
1031
- let ip = req.headers["x-real-ip"] || req.headers["x-true-ip"] || req.headers['x-forwarded-for'] || req.headers['x-forward-for'] || req.connection.remoteAddress || req.socket.remoteAddress || req.headers['host'] || ""; // console.log("Request IP:", ip, "req: ", req);
1032
-
1033
- if (ip.indexOf(",") >= 0) {
1034
- ip = ip.split(',')[0];
1035
- }
1036
-
1037
- if (ip.indexOf(":") >= 0) {
1038
- ip = ip.substring(ip.lastIndexOf(':') + 1);
1039
- }
1040
-
1041
- return ip;
1042
- }
1043
- /**
1044
- * 获取与本机一起提供服务的所有主机
1045
- * @param params
1046
- * @returns {{port: (number|number), hosts: [string]}}
1047
- */
1048
-
1049
-
1050
- static getEnvHosts(params) {
1051
- configNeeded();
1052
- return {
1053
- hosts: appSetting$1.serverHosts || ["localhost"],
1054
- port: appSetting$1.listenPort || +params.port || 8443
1055
- };
1056
- }
1057
- /**
1058
- * 检查指定的host和发送请求的ip是不是自己的ip
1059
- * @param host
1060
- * @param req
1061
- * @returns {Promise<*>}
1062
- */
1063
-
1064
-
1065
- static async hostIsMySelf(host, req) {
1066
- let clientIp = apiUtil$2.getClientIp(req);
1067
-
1068
- try {
1069
- return !!ppUtil$4.getMyIp({
1070
- hosts: [host, clientIp]
1071
- });
1072
- } catch (e) {
1073
- return false;
1074
- }
1075
- }
1076
- /**
1077
- * 检查是否应该把请求转发到其它服务器,若是则转发
1078
- * @param options
1079
- * @returns {Promise<boolean>}
1080
- */
1081
-
1082
-
1083
- static async checkForwardsHost(options) {
1084
- configNeeded();
1085
- const params = options.replacements;
1086
- const myEnvHosts = apiUtil$2.getEnvHosts(params);
1087
- const myIp = ppUtil$4.getMyIp();
1088
- const host = params.host || myIp;
1089
- if (!myEnvHosts.hosts.contains(host)) throw [err$2.ACCESS_REFUSED, `环境${conf$3.envId}貌似无此主机:${params.host}`];
1090
-
1091
- if (!apiUtil$2.hostIsMySelf(host, options._res.req)) {
1092
- let result = await apiUtil$2.forwardsTo(host, myEnvHosts.port, params, options._res.req, myIp);
1093
-
1094
- options._res.send(result);
1095
-
1096
- return true;
1097
- }
1098
-
1099
- return false;
1100
- }
1101
- /**
1102
- * 将收到的请求转发到另一台主机
1103
- * @param anotherIp
1104
- * @param port
1105
- * @param params
1106
- * @param req
1107
- * @param lanClient
1108
- * @returns {Promise<*>}
1109
- */
1110
-
1111
-
1112
- static async forwardsTo(anotherIp, port, params, req, lanClient) {
1113
- let url = anotherIp.indexOf("://") > 0 ? anotherIp : `${req.headers.scheme}://${anotherIp}:${port}${req.originalUrl}`;
1114
-
1115
- if (url.indexOf("?") > 0) {
1116
- url = url.substring(0, url.indexOf("?"));
1117
- }
1118
-
1119
- let reqOptions = {
1120
- method: req.method,
1121
- url: url,
1122
- data: params
1123
- };
1124
- let result = await netUtil$1.apiRequest(reqOptions);
1125
-
1126
- if (result.status !== 200) {
1127
- console.log(`访问 ${reqOptions.url} 出错`, result.status, result.res.statusMessage);
1128
- throw [err$2.EXCEPTION, `[${result.status}] - ${result.res.statusMessage}`];
1129
- }
1130
-
1131
- return result.data;
1132
- }
1133
- /**
1134
- * 解析配置项路径
1135
- * @param keyPath
1136
- * @returns {{parent: null, parentKey: string, value, key: string}}
1137
- */
1138
-
1139
-
1140
- static parseCfgPath(keyPath) {
1141
- let keys = Array.isArray(keyPath) ? keyPath : keyPath.split(".");
1142
- let parentKey = "";
1143
- let key = "config";
1144
- let value = conf$3;
1145
- let parent = null;
1146
-
1147
- for (let i = 0; i < keys.length; i++) {
1148
- parent = value;
1149
- parentKey += (parentKey ? "." : "") + key;
1150
- key = keys[i];
1151
- value = parent[key];
1152
-
1153
- if (value === undefined) {
1154
- throw [err$2.INVALID_PARAM, `无此配置项: ${key}`];
1155
- }
1156
- }
1157
-
1158
- return {
1159
- parent,
1160
- parentKey,
1161
- key,
1162
- value
1163
- };
1164
- } //#endregion
1165
- //region ====中间件
1166
-
1167
- /**
1168
- * 检查令牌,若存在则将其的用户信息置入req,否则返回错误信息
1169
- */
1170
-
1171
-
1172
- static async $checkToken(req, res, next) {
1173
- try {
1174
- await apiUtil$2.checkRequestToken(req);
1175
- await next();
1176
- } catch (e) {
1177
- res.send(err$2.ERROR(e));
1178
- }
1179
-
1180
- return false;
1181
- }
1182
- /**
1183
- * 检查令牌,若存在则将其用户信息置入req
1184
- */
1185
-
1186
-
1187
- static async $getToken(req, res, next) {
1188
- function extractToken(req, res) {
1189
- if (!req.accessToken) {
1190
- //检查post的信息或者url查询参数或者头信息
1191
- let token = req.headers["access_token"] || req.headers['x-access-token']; //向后兼容
1192
-
1193
- if (!token) {
1194
- let params = apiUtil$2.extractParams(req);
1195
- token = params["access_token"];
1196
-
1197
- if (token) {
1198
- delete params["access_token"];
1199
- } else if (params.token) {
1200
- token = params.token; //向后兼容
1201
-
1202
- delete params.token; // } else if (res) {
1203
- // // 如果没有token,则返回错误
1204
- // console.log(err.TOKEN_NEEDED);
1205
- // res.send(err.ERROR(err.TOKEN_NEEDED));
1206
- }
1207
- }
1208
-
1209
- req.accessToken = token;
1210
- }
1211
-
1212
- return req.accessToken;
1213
- }
1214
-
1215
- try {
1216
- if (extractToken(req, res)) {
1217
- await apiUtil$2.restoreTokenData(apiUtil$2.userTokenStoreKey(req.accessToken), apiUtil$2.tokenExpireTime(req)).then(function (data) {
1218
- req.userInfo = apiUtil$2.setUserIdNo(data, data._idNo);
1219
- }, function (e) {//no token
1220
- });
1221
- }
1222
-
1223
- return true;
1224
- } catch (e) {
1225
- res.send(err$2.ERROR(e));
1226
- return false;
1227
- }
1228
- }
1229
- /**
1230
- * 标识此请求已经完成了签名验证或者无需进行签名验证
1231
- */
1232
-
1233
-
1234
- static $signatureVerified(req, res, next) {
1235
- req.signatureVerified = true;
1236
- return true;
1237
- }
1238
- /**
1239
- * 标识此请求是从前端发过来的,比如移动端、PC浏览器等,区别于SPO一般的服务端后台请求。
1240
- * 如果在请求参数中包含有签名参数,则将其移到请求头中
1241
- */
1242
-
1243
-
1244
- static $isMobile(req, res, next) {
1245
- req.isMobile = true;
1246
-
1247
- if (!req.signatureVerified) {
1248
- let params = apiUtil$2.extractParams(req);
1249
- moveProperty(params, req.headers, "access_token");
1250
- moveProperty(params, req.headers, "timestamp");
1251
-
1252
- if (moveProperty(params, req.headers, "signature")) {
1253
- req.headers.signature = decodeURIComponent(req.headers.signature);
1254
- }
1255
- }
1256
-
1257
- next();
1258
- }
1259
- /**
1260
- * 检查企业 API 调用合法性 (apiKey, apiSecret)
1261
- * @param req
1262
- * @param res
1263
- * @param next
1264
- */
1265
-
1266
-
1267
- static $checkApiKeyOld(req, res, next) {
1268
- //检查post的信息或者url查询参数或者头信息
1269
- let tokenData = apiUtil$2.extractTokenData(req);
1270
- let params = apiUtil$2.extractParams(req);
1271
- apiUtil$2.createApiCallRec(tokenData, req);
1272
- let errResponse = null;
1273
-
1274
- if (!tokenData.clientIp) {
1275
- errResponse = apiUtil$2.spoApiFail(err$2.GET_CLIENTIP_FAIL, req);
1276
- } else if (!tokenData.apiKey) {
1277
- errResponse = apiUtil$2.spoApiFail([err$2.PARAMETER_NEEDED, "apiKey"], req);
1278
- } else if (!tokenData.apiSecret) {
1279
- errResponse = apiUtil$2.spoApiFail([err$2.PARAMETER_NEEDED, "apiSecret"], req);
1280
- }
1281
-
1282
- if (errResponse != null) {
1283
- apiUtil$2._saveApiCallRec(req.apiCallRec, errResponse);
1284
-
1285
- apiUtil$2.sendOrRedirect(req, res, errResponse);
1286
- } else {
1287
- apiUtil$2.checkApiCallValid(tokenData, req).then(companyInfo => {
1288
- params.companyId = params.companyId || companyInfo.companyId;
1289
- req.companyInfo = companyInfo;
1290
- next();
1291
- }, e => {
1292
- console.log('apiCallInvalid:', e);
1293
- let errResponse = spoApiFail(e, req);
1294
-
1295
- apiUtil$2._saveApiCallRec(req.apiCallRec, errResponse);
1296
-
1297
- apiUtil$2.sendOrRedirect(req, res, errResponse);
1298
- });
1299
- }
1300
- }
1301
- /**
1302
- * 检查机构令牌
1303
- */
1304
-
1305
-
1306
- static $checkApiKey(req, res, next) {
1307
- if (req.ignoreToken) {
1308
- next();
1309
- return;
1310
- }
1311
-
1312
- let accessToken = apiUtil$2.extractToken(req);
1313
-
1314
- if (!accessToken) {
1315
- res.send(apiUtil$2.spoApiFail(err$2.TOKEN_NEEDED, req)); // apiUtil.sendOrRedirect(req, res, spoApiFail(err.TOKEN_NEEDED, req));
1316
-
1317
- return;
1318
- }
1319
-
1320
- apiUtil$2.restoreTokenData(apiUtil$2.apiTokenStoreKey(accessToken)).then(p => {
1321
- // req.headers[hnApiKey] = p.apiKey || '';
1322
- // req.headers[hnApiSecret] = p.apiSecret || '';
1323
- if (req.isMobile) {
1324
- // GET 请求的应该都是H5页面,由手机前端发起,需要把headers中的请求方ip换成服务端的
1325
- // todo: 如何在API访问记录中记住请求者(手机端)的真实IP
1326
- req.headers['x-phone-ip'] = apiUtil$2.getClientIp(req);
1327
- req.headers['x-real-ip'] = p.clientIp;
1328
- } else {
1329
- p.clientIp = apiUtil$2.getClientIp(req);
1330
- }
1331
-
1332
- req.isApiCall = true;
1333
- req.tokenData = p;
1334
- return apiUtil$2.$checkApiKeyOld(req, res, next);
1335
- }).catch(e => {
1336
- apiUtil$2.sendOrRedirect(req, res, apiUtil$2.spoApiFail(err$2.TOKEN_INVALID, req));
1337
- });
1338
- }
1339
- /**
1340
- * 检查请求令牌,允许个人用户令牌和机构令牌,若不存在或不合法,则返回错误信息
1341
- * @param req
1342
- * @param res
1343
- * @param next
1344
- * @returns {Promise<void|boolean|boolean|undefined>}
1345
- */
1346
-
1347
-
1348
- static async $checkTokenOrApiKey(req, res, next) {
1349
- let accessToken = apiUtil$2.extractToken(req, res);
1350
-
1351
- if (accessToken) {
1352
- let tokenExist = await apiUtil$2.tokenDataExist(apiUtil$2.userTokenStoreKey(accessToken));
1353
-
1354
- if (tokenExist) {
1355
- return await apiUtil$2.$checkToken(req, res, next);
1356
- }
1357
-
1358
- tokenExist = await apiUtil$2.tokenDataExist(apiUtil$2.apiTokenStoreKey(accessToken));
1359
-
1360
- if (tokenExist) {
1361
- return apiUtil$2.$checkApiKey(req, res, next);
1362
- } // 如果给定的token不存在,看看是不是平台内部调用
1363
-
1364
-
1365
- return await apiUtil$2.checkInternalToken(req).catch(e => {
1366
- res.send(apiUtil$2.apiFail(e, req));
1367
- return false;
1368
- });
1369
- }
1370
- } //#endregion
1371
- //region ====一些通用服务功能
1372
-
1373
- /**
1374
- * 获取配置数据
1375
- * @param options
1376
- * @returns {Promise<boolean|*>}
1377
- */
1378
- // api/config/appSetting.ossEnabled
1379
-
1380
-
1381
- static async getConfigValue(options) {
1382
- configNeeded();
1383
- const params = options.replacements;
1384
- let item = params.item;
1385
- delete params.item;
1386
- if (await apiUtil$2.checkForwardsHost(options)) return true;
1387
- let c = apiUtil$2.parseCfgPath(item);
1388
- delete c.parent;
1389
- c.host = ppUtil$4.getMyIp();
1390
- return c;
1391
- }
1392
- /**
1393
- * 设置配置数据
1394
- * @param options
1395
- * @returns {Promise<*>}
1396
- */
1397
-
1398
-
1399
- static async setConfigValue(options) {
1400
- configNeeded();
1401
- const params = options.replacements;
1402
- let item = params.item;
1403
- delete params.item; // if (await checkForwardsHost(options))
1404
- // return true;
1405
-
1406
- await apiUtil$2.parametersOK(params, "value");
1407
- let c = apiUtil$2.parseCfgPath(item);
1408
-
1409
- if (typeof c.value === "object") {
1410
- throw [err$2.ACCESS_REFUSED, `暂不支持修改复杂配置`];
1411
- } // c.oldValue = c.value;
1412
-
1413
-
1414
- c.newValue = params.value;
1415
- c.parent[c.key] = c.newValue;
1416
- delete c.parent;
1417
- c.host = ppUtil$4.getMyIp();
1418
- return c; // 应用层自行处理多机同步
1419
- }
1420
- /**
1421
- * 修改重启标志文件
1422
- * 前提,PM2监视 restart,please.flag 文件,此文件若被修改则重启服务进程
1423
- * 此服务假设 restart,please.flag 文件最后一行是一个整数,此整数+1后保存
1424
- * @param options
1425
- * @returns {Promise<number>}
1426
- */
1427
-
1428
-
1429
- static async updateRestartFlag(options) {
1430
- const fileName = `restart,please.flag`;
1431
- let text = await fs$1.async_readFile(fileName, 'utf8');
1432
- const lines = text.split('\r\n');
1433
- const n = +lines[lines.length - 1] + 1;
1434
- lines[lines.length - 1] = ' '.repeat(50) + n.toString();
1435
- text = lines.join('\r\n');
1436
- await fs$1.async_writeFile(fileName, text);
1437
- return n;
1438
- } //#endregion
1439
-
1440
-
1441
- }
1442
-
1443
- function configNeeded() {
1444
- if (!conf$3 || !err$2) {
1445
- ppUtil$4.configNeeded();
1446
- conf$3 = ppUtil$4.appConfig;
1447
- appSetting$1 = conf$3.appSetting;
1448
- err$2 = ppUtil$4.appErrCfg;
1449
- }
1450
- }
1451
-
1452
- var ppUtilApi = apiUtil$2;
1453
-
1454
- const fs = require$$4;
1455
- const path = require$$1$1;
1456
- const ppUtil$3 = require$$1.ppUtil;
1457
- const ppRedis = require$$2.ppRedis;
1458
- let conf$2, err$1; // const dbCheck = require('../dbupdate/dd-version');
1459
-
1460
- /**
1461
- * API服务启动时前置条件检查
1462
- * ok = await preconditions
1463
- * .setAppName(conf.thisApp)
1464
- * .addCheckPromise(preconditions.checkExEnvSetting())
1465
- * .addCheckPromise(preconditions.checkRedis())
1466
- * .addCheckPromise(checkDatabase())
1467
- * .addChecker(myChecker, args)
1468
- * .checkAll()
1469
- */
1470
-
1471
- class preconditions$1 {
1472
- static appName;
1473
- static checkFunctions = [];
1474
- static checkPromises = [];
1475
-
1476
- static setAppName(aName) {
1477
- ppUtil$3.configNeeded();
1478
- conf$2 = ppUtil$3.appConfig;
1479
- err$1 = ppUtil$3.appErrCfg;
1480
- preconditions$1.appName = aName || conf$2.thisApp;
1481
- return preconditions$1;
1482
- }
1483
-
1484
- static addCheckPromise(p) {
1485
- preconditions$1.checkPromises.push(p);
1486
- return preconditions$1;
1487
- }
1488
-
1489
- static addChecker(checkFunc, args) {
1490
- preconditions$1.checkFunctions.push({
1491
- check: checkFunc,
1492
- args: args
1493
- });
1494
- return preconditions$1;
1495
- }
1496
-
1497
- static async checkAll() {
1498
- for (let i = 0; i < preconditions$1.checkFunctions.length; i++) {
1499
- const f = preconditions$1.checkFunctions[i];
1500
- preconditions$1.addCheckPromise(f.check(f.args));
1501
- }
1502
-
1503
- for (let i = 0; i < preconditions$1.checkPromises.length; i++) {
1504
- let msg = await preconditions$1.checkPromises[i];
1505
-
1506
- if (msg) {
1507
- if (!preconditions$1.messages) {
1508
- preconditions$1.messages = [];
1509
- }
1510
-
1511
- preconditions$1.messages.push(`${preconditions$1.messages.length + 1}. ${msg}`);
1512
- }
1513
- }
1514
-
1515
- let text = "";
1516
-
1517
- if (preconditions$1.messages) {
1518
- text = `*** 由于以下原因,${preconditions$1.appName} 尚不能正常提供服务:\n\n` + preconditions$1.messages.join("\n\n") + "\n\n*** 请检查以上各项条件,修复后重新启动服务";
1519
- console.log("\n" + text); // for (let i = 0; i < preconditions.messages.length; i++) {
1520
- // console.log(preconditions.messages[i]);
1521
- // }
1522
- }
1523
-
1524
- if (!(await fs.async_exists("../log"))) {
1525
- await fs.async_mkdir("../log");
1526
- }
1527
-
1528
- await fs.async_writeFile(`../log/preconditions`, text);
1529
- return !text;
1530
- } // 检查器
1531
-
1532
- /**
1533
- * 检查本机外部配置文件版本
1534
- */
1535
-
1536
-
1537
- static async checkExEnvSetting(loadjs) {
1538
- return await checkExEnvSetting(loadjs);
1539
- }
1540
- /**
1541
- * redis 可用性检查
1542
- */
1543
-
1544
-
1545
- static async checkRedis() {
1546
- return await checkRedis();
1547
- } // /**
1548
- // * 数据库可用性及业务数据版本版本检查
1549
- // */
1550
- // static async checkDatabase() {
1551
- // return await checkDatabase();
1552
- // }
1553
-
1554
-
1555
- }
1556
-
1557
- async function createEnvSettingFile(fileName, lastEnvSettingEx) {
1558
- try {
1559
- const cfgText = await ppUtil$3.doCreateEnvSettingFile(fileName, lastEnvSettingEx);
1560
-
1561
- if (cfgText.indexOf(`cfgVersion: "${conf$2._envSetting.cfgVersion}"`) > 0) {
1562
- console.log(`\n*** 已創建新的本機環境配置文件\n${fileName}\n请檢查各項內容是否正确(此项更新服务可正常运行)\n\n`);
1563
- return "";
1564
- }
1565
-
1566
- return `已創建新的本機環境配置文件\n${fileName}\n请檢查並正確設置各項內容,然後將配置版本號修改為${conf$2._envSettings.default.cfgVersion}`;
1567
- } catch (e) {
1568
- return `創建本機環境配置文件失敗\n《${fileName}》\n${err$1.ERROR(e).message}`;
1569
- }
1570
- }
1571
-
1572
- async function checkExEnvSetting(loadjs) {
1573
- if (!conf$2._envSetting.localSettingImported) {
1574
- let fileName = conf$2.getEnvSettingFileName();
1575
-
1576
- if (!fs.existsSync(fileName)) {
1577
- return await createEnvSettingFile(fileName);
1578
- }
1579
-
1580
- let envSettingEx = loadjs(fileName);
1581
-
1582
- if (envSettingEx.cfgVersion !== conf$2._envSettings.default.cfgVersion) {
1583
- if (envSettingEx.cfgVersion !== "0.0") {
1584
- let fp = path.parse(fileName);
1585
- let lastVerFileName = path.join(fp.dir, fp.name + envSettingEx.cfgVersion + fp.ext);
1586
-
1587
- if (!fs.existsSync(lastVerFileName)) {
1588
- fs.renameSync(fileName, lastVerFileName);
1589
- }
1590
- }
1591
-
1592
- let msg = await createEnvSettingFile(fileName, envSettingEx);
1593
-
1594
- if (!msg) {
1595
- delete require.cache[fileName];
1596
- ppUtil$3.importExEnvSetting(conf$2._envSetting, fileName, loadjs);
1597
- }
1598
-
1599
- return msg;
1600
- }
1601
- }
1602
- } // async function checkDatabase() {
1603
- // return dbCheck.check().then(info => info.updateMessage);
1604
- // }
1605
-
1606
-
1607
- async function checkRedis() {
1608
- if (!conf$2.redis.enabled) return "";
1609
- const redis = ppRedis.newClient("kv"); // console.log(`checkRedis begin:`);
1610
-
1611
- await ppUtil$3.wait(15 * 1000, param => {
1612
- if (redis.ready && redis.connected || redis.error) {
1613
- return true;
1614
- }
1615
- }).catch(e => {
1616
- redis.error = e;
1617
- });
1618
-
1619
- if (redis.error) {
1620
- console.log(`未能成功連接 redis: ${redis.error},請檢查相關設置`);
1621
- return `未能成功連接 redis: ${redis.error},請檢查相關設置`;
1622
- } // console.log(`checkRedis done:`);
1623
- // console.log(redis);
1624
-
1625
- }
1626
-
1627
- var ppPrecond = preconditions$1;
1628
-
1629
- // 若 redis 可用,则使用基于 redis 的消息订阅发布系统
1630
- // 若 redis 不可用,则将消息转发到其它主机
1631
-
1632
- const {
1633
- ppUtil: ppUtil$2
1634
- } = require$$1;
1635
- const RedisMessenger = require$$1$2;
1636
- const {
1637
- netUtil
1638
- } = require$$3;
1639
- let conf$1;
1640
-
1641
- class commonMessenger$2 {
1642
- static myMsgAddr = "";
1643
- static redisMessenger = null;
1644
- static onMessage = [];
1645
-
1646
- static initMessenger(appConfig, appErrCfg, msgChannel) {
1647
- conf$1 = appConfig;
1648
- this.redisMessenger = conf$1.redis.enabled ? new RedisMessenger(appConfig, appErrCfg, msgChannel).subscribe(msgChannel) : null;
1649
- this.myMsgAddr = commonMessenger$2.getMyMsgAddr();
1650
- }
1651
-
1652
- static getMyMsgAddr() {
1653
- if (!conf$1.myMsgAddr) {
1654
- conf$1.pm_id = process.env.pm_id || "0";
1655
- let myIp = ppUtil$2.getMyIp();
1656
-
1657
- if (this.redisMessenger) {
1658
- myIp = `${conf$1.pm_id}@${myIp}`;
1659
- }
1660
-
1661
- conf$1.myMsgAddr = myIp;
1662
- console.log(`myMsgAddr: `, conf$1.myMsgAddr);
1663
- }
1664
-
1665
- return conf$1.myMsgAddr;
1666
- }
1667
-
1668
- static setMessageHandle(messageHandle) {
1669
- if (this.redisMessenger) {
1670
- return this.redisMessenger.setMessageHandle(messageHandle, msgChannel);
1671
- }
1672
-
1673
- if (commonMessenger$2.onMessage.indexOf(messageHandle) < 0) {
1674
- commonMessenger$2.onMessage.push(messageHandle);
1675
- }
1676
-
1677
- return commonMessenger$2;
1678
- }
1679
-
1680
- static async publishMessage(message) {
1681
- if (this.redisMessenger) {
1682
- return this.redisMessenger.publish(message, msgChannel);
1683
- } else {
1684
- //没有消息处理器,则将消息转发到目标地址(不支持pm2多进程)
1685
- if (message.destAddr) {
1686
- await forwardMessage(message);
1687
- } else {
1688
- for (let i = 0; i < conf$1.appSetting.serverHosts.length; i++) {
1689
- message.destAddr = conf$1.appSetting.serverHosts[i];
1690
-
1691
- if (message.destAddr !== conf$1.myMsgAddr) {
1692
- await forwardMessage(message);
1693
- }
1694
- }
1695
- }
1696
- }
1697
- }
1698
-
1699
- static async handleInternalMessage(options) {
1700
- const message = options.replacements;
1701
-
1702
- for (let i = 0; i < commonMessenger$2.onMessage.length; i++) {
1703
- const appMsgHandle = commonMessenger$2.onMessage[i];
1704
-
1705
- try {
1706
- await appMsgHandle(message);
1707
- } catch (e) {
1708
- console.log(e);
1709
- }
1710
- }
1711
- }
1712
-
1713
- }
1714
-
1715
- async function forwardMessage(message) {
1716
- let reqOptions = {
1717
- url: `https://${message.destAddr}:${appSetting.listenPort}/api/message`,
1718
- method: "POST",
1719
- data: message
1720
- };
1721
- let res = await netUtil.apiRequest(reqOptions).catch(e => {
1722
- console.log(`转发消息失败, url: ${reqOptions.url}`, e);
1723
- throw e;
1724
- });
1725
- return res.data;
1726
- }
1727
-
1728
- var ppMessengerEx = commonMessenger$2;
1729
-
1730
- /**
1731
- * CSCA 自动任务
1732
- * ==============
1733
- */
1734
- const CRON = require$$0$1;
1735
- const {
1736
- ppUtil: ppUtil$1
1737
- } = require$$1;
1738
- const apiUtil$1 = ppUtilApi;
1739
- const commonMessenger$1 = ppMessengerEx;
1740
- const MT_SCHEDULE_STATE = 'schedule-state',
1741
- MT_SCHEDULE_STATE_REPLY = 'schedule-state-reply';
1742
- let conf, err;
1743
-
1744
- class schedule$1 {
1745
- static tasks = {};
1746
- /**
1747
- * 注册一个计划任务
1748
- * @param taskName
1749
- * @param taskCaption
1750
- * @param cronExp
1751
- * @param taskFunc
1752
- * @param autoStart
1753
- * @returns {Promise<{}|{}|{cron: *, _message_: string}|any>}
1754
- */
1755
-
1756
- static async registerTask(taskName, taskCaption, cronExp, taskFunc, autoStart) {
1757
- if (!conf) {
1758
- ppUtil$1.configNeeded();
1759
- conf = ppUtil$1.appConfig;
1760
- err = ppUtil$1.appErrCfg;
1761
- }
1762
-
1763
- if (!this.envAllowed()) return {};
1764
- let task = this.findTask(taskName);
1765
-
1766
- if (task) {
1767
- if (task.cronExp !== cronExp || task.taskFunc !== taskFunc) {
1768
- this.stop(taskName);
1769
- task = null;
1770
- }
1771
- }
1772
-
1773
- if (!task) {
1774
- task = {
1775
- name: taskName,
1776
- caption: taskCaption,
1777
- cronExp: cronExp,
1778
- taskFunc: taskFunc,
1779
- schedule: null,
1780
- doScheduleTask: async function () {
1781
- let self = this;
1782
- if (this._pp_task) self = this._pp_task;
1783
- console.log(new Date().format('[yyyy/MM/dd hh:mm:ss:S]'), `开始执行${self.caption}任务 ...`);
1784
- await self.taskFunc();
1785
- console.log(new Date().format('[yyyy/MM/dd hh:mm:ss:S]'), `${self.caption}任务执行结束`);
1786
- }
1787
- };
1788
- this.tasks[taskName] = task;
1789
-
1790
- if (autoStart) {
1791
- return await this.start(task);
1792
- }
1793
-
1794
- return this.getTaskInfo(task, "任务已注册。");
1795
- }
1796
-
1797
- return this.getTaskInfo(task, "任务已存在。");
1798
- }
1799
-
1800
- static getTaskInfo(task, msg) {
1801
- // return Object.assign({_message_: msg}, task);
1802
- return {
1803
- name: task.name,
1804
- caption: task.caption,
1805
- cronExp: task.cronExp,
1806
- state: task.schedule ? "started" : "stopped",
1807
- _message_: msg
1808
- };
1809
- }
1810
- /**
1811
- * 启动任务
1812
- * @param taskOrName
1813
- * @param cronExp
1814
- * @returns {Promise<{}|{cron: *, _message_: string}>}
1815
- */
1816
-
1817
-
1818
- static async start(taskOrName, cronExp) {
1819
- if (!this.envAllowed()) return {};
1820
- let task = taskOrName;
1821
-
1822
- if (typeof task === "string") {
1823
- task = this.findTask(taskOrName);
1824
- }
1825
-
1826
- if (!task) throw [err.INVALID_PARAM, `任务${taskOrName}不存在`];
1827
- cronExp = cronExp || task.cronExp;
1828
-
1829
- if (task.cronExp !== cronExp) {
1830
- this.stop(task);
1831
- task.cronExp = cronExp;
1832
- }
1833
-
1834
- if (task.schedule) {
1835
- return this.getTaskInfo(task, "任务已启动,无需重复启动。");
1836
- }
1837
-
1838
- task.schedule = CRON.schedule(cronExp, task.doScheduleTask);
1839
- task.schedule._task._pp_task = task;
1840
- return this.getTaskInfo(task, "任务已启动。");
1841
- }
1842
- /**
1843
- * 停止任务
1844
- * @param taskOrName
1845
- * @returns {Promise<{}|{cron: *, _message_: string}|{_message_: string}>}
1846
- */
1847
-
1848
-
1849
- static async stop(taskOrName) {
1850
- if (!this.envAllowed()) return {};
1851
- let task = taskOrName;
1852
-
1853
- if (typeof task === "string") {
1854
- task = this.findTask(taskOrName);
1855
- }
1856
-
1857
- if (!task) throw [err.INVALID_PARAM, `任务${taskOrName}不存在`];
1858
-
1859
- if (task.schedule) {
1860
- task.schedule.stop();
1861
- task.schedule = null;
1862
- return this.getTaskInfo(task, "任务已停止。");
1863
- }
1864
-
1865
- return this.getTaskInfo(task, "任务未启动,无需停止。");
1866
- }
1867
- /**
1868
- * 获取或设置当前状态(来自远程的API调用)
1869
- * @param options
1870
- * @returns {Promise<unknown>}
1871
- */
1872
-
1873
-
1874
- static async state(options) {
1875
- const params = options.replacements; // await commonMessenger.parametersOK(params, "taskName");
1876
-
1877
- const req = options._res.req;
1878
-
1879
- if (req) {
1880
- if (req.method === 'GET') {
1881
- delete params.state;
1882
- } else {
1883
- await apiUtil$1.parametersOK(params, 'taskName', "state");
1884
- }
1885
- }
1886
-
1887
- if (this.envAllowed()) {
1888
- return this._state(params);
1889
- } // 环境变量启用计划任务,但当前环境不能执行,
1890
- // 说明收到此API请求的进程不是第一个进程,或者此前服务器不是第一台服务器,
1891
- // 这种情况下,将此请求作为消息发布出去,
1892
- // 第一台服务器的第一个进程收到消息后进行处理,再将结果作为消息发布,
1893
- // 本进程收到处理结果后果作为响应发送回客户端
1894
-
1895
-
1896
- if (process.env.SCHEDULE_ENABLED) {
1897
- let replyId = ppUtil$1.newGuid();
1898
- return await new Promise((resolve, reject) => {
1899
- commonMessenger$1.setMessageHandle(message => {
1900
- if (message._type === MT_SCHEDULE_STATE_REPLY && message.replyId === replyId) {
1901
- replyId = '';
1902
- resolve(message.result);
1903
- return;
1904
- }
1905
-
1906
- if (message._type === MT_SCHEDULE_STATE) {
1907
- if (schedule$1.enabled()) {
1908
- message.result = schedule$1._state(message.params);
1909
- message._type = MT_SCHEDULE_STATE_REPLY;
1910
- message.destAddr = message.replyAddr;
1911
- commonMessenger$1.publishMessage(message);
1912
- }
1913
- }
1914
- });
1915
- commonMessenger$1.publishMessage({
1916
- _type: MT_SCHEDULE_STATE,
1917
- replyId,
1918
- replyAddr: conf.myMsgAddr,
1919
- params
1920
- }).catch(e => {
1921
- replyId = '';
1922
- reject(e);
1923
- });
1924
- setTimeout(() => {
1925
- if (replyId) {
1926
- reject("schedule.state: 等待处理结果超时");
1927
- }
1928
- }, 2 * 60 * 1000);
1929
- });
1930
- }
1931
-
1932
- return {
1933
- _message_: "此环境无自动任务"
1934
- };
1935
- }
1936
-
1937
- static async _state(params) {
1938
- let apiResult = {};
1939
- let task;
1940
-
1941
- if (params.taskName) {
1942
- task = this.findTask(params.taskName);
1943
- if (!task) throw [err.INVALID_PARAM, `任务${params.taskName}不存在`];
1944
- }
1945
-
1946
- if (params.state) {
1947
- if ("started" === params.state) {
1948
- apiResult = await this.start(task, params.cron);
1949
- } else if ("stopped" === params.state) {
1950
- apiResult = await this.stop(task);
1951
- } else {
1952
- throw [err.INVALID_PARAM, `(${params.state}), 只能是 started/stopped`];
1953
- }
1954
-
1955
- if (params.runOnce) {
1956
- task = this.findTask(params.taskName);
1957
- apiResult._message_ += "\n正在手动执行任务"; // await
1958
-
1959
- task.doScheduleTask();
1960
- }
1961
-
1962
- return this.getTaskInfo(task, apiResult._message_);
1963
- }
1964
-
1965
- if (task) {
1966
- return this.getTaskInfo(task);
1967
- }
1968
-
1969
- const result = [];
1970
-
1971
- for (let k in this.tasks) {
1972
- result.push(this.getTaskInfo(this.tasks[k]));
1973
- }
1974
-
1975
- return result;
1976
- }
1977
-
1978
- static deleteTask(taskName) {
1979
- delete this.tasks[taskName];
1980
- }
1981
-
1982
- static findTask(taskName) {
1983
- return this.tasks[taskName];
1984
- }
1985
- /**
1986
- * 确定当前进程环境是否启用自动任务
1987
- */
1988
-
1989
-
1990
- static envAllowed() {
1991
- let enabled = // PM2配置中指定了环境变量 SCHEDULE_ENABLED
1992
- !!process.env.SCHEDULE_ENABLED // 如果PM2启动了多个进程,则只在第一个进程执行
1993
- && process.env.NODE_APP_INSTANCE === '0'; // 如果有多台服务器,则只在第一台执行
1994
-
1995
- if (enabled && conf.appSetting.serverHosts && conf.appSetting.serverHosts.length > 1) {
1996
- enabled = conf.myMsgAddr.indexOf(conf.appSetting.serverHosts[0]) === 0;
1997
- }
1998
-
1999
- return enabled;
2000
- }
2001
-
2002
- }
2003
-
2004
- var ppSchedule = schedule$1;
2005
-
2006
- const apiUtil = ppUtilApi;
2007
- const preconditions = ppPrecond;
2008
- const commonMessenger = ppMessengerEx;
2009
- const schedule = ppSchedule;
2010
- var ppUtil = {
2011
- apiUtil,
2012
- preconditions,
2013
- commonMessenger,
2014
- schedule
2015
- };
2016
-
2017
- export { ppUtil as default };