abler-api 0.1.80 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cjs/pp-util.js +297 -49
  2. package/package.json +2 -2
@@ -37,7 +37,7 @@ const {
37
37
  kvStorage: kvStorage$1,
38
38
  redisSimulator
39
39
  } = require$$4__default["default"];
40
- let conf$3, err$2;
40
+ let conf$3, err$1;
41
41
  // const dbCheck = require('../dbupdate/dd-version');
42
42
 
43
43
  /**
@@ -57,7 +57,7 @@ class preconditions$2 {
57
57
  static setAppName(aName) {
58
58
  ppUtil$4.configNeeded();
59
59
  conf$3 = ppUtil$4.appConfig;
60
- err$2 = ppUtil$4.appErrCfg;
60
+ err$1 = ppUtil$4.appErrCfg;
61
61
  preconditions$2.appName = aName || conf$3.thisApp;
62
62
  return preconditions$2;
63
63
  }
@@ -167,7 +167,7 @@ async function createEnvSettingFile(fileName, lastEnvSettingEx) {
167
167
  }
168
168
  return t_f$3("已創建新的本機環境配置文件\n%s\n请檢查並正確設置各項內容,然後將配置版本號修改為%s", fileName, conf$3._envSettings.default.cfgVersion);
169
169
  } catch (e) {
170
- return t_f$3("創建本機環境配置文件失敗\n《%s》\n%s", fileName, err$2.ERROR(e).message);
170
+ return t_f$3("創建本機環境配置文件失敗\n《%s》\n%s", fileName, err$1.ERROR(e).message);
171
171
  }
172
172
  }
173
173
  async function checkExEnvSetting(loadjs) {
@@ -222,7 +222,7 @@ async function checkRedis() {
222
222
  }
223
223
  async function initDb(dbSql) {
224
224
  try {
225
- dbUtil$1.config(conf$3, err$2, dbSql);
225
+ dbUtil$1.config(conf$3, err$1, dbSql);
226
226
  } catch (e) {
227
227
  if (e.reason === 'bad decrypt') {
228
228
  e = new Error(t_f$3("解密数据库密码(%s)失败", conf$3.dbconn.dbpwd));
@@ -235,7 +235,7 @@ async function initDb(dbSql) {
235
235
  }
236
236
  async function initKvStorage(dbSql) {
237
237
  try {
238
- kvStorage$1.config(conf$3, err$2);
238
+ kvStorage$1.config(conf$3, err$1);
239
239
  if (conf$3.redis.enabled) {
240
240
  kvStorage$1.setRedis(ppRedis.newClient("kv"));
241
241
  } else {
@@ -258,7 +258,8 @@ const {
258
258
  t
259
259
  } = require$$2__default["default"];
260
260
  const {
261
- ppUtil: ppUtil$3
261
+ ppUtil: ppUtil$3,
262
+ ppCrypto
262
263
  } = require$$3__default["default"];
263
264
  const {
264
265
  dbUtil,
@@ -268,7 +269,7 @@ const {
268
269
  netUtil: netUtil$1
269
270
  } = require$$6__default["default"];
270
271
  const preconditions$1 = ppPrecond;
271
- let conf$2, appSetting, err$1, dbSql;
272
+ let conf$2, appSetting, errCfg, dbSql;
272
273
  const pnToken = "access_token",
273
274
  hnToken = pnToken,
274
275
  pnApiKey = "apiKey",
@@ -276,8 +277,6 @@ const pnToken = "access_token",
276
277
  // 我们接收到的 headers 中的字段名总是全小写的
277
278
  pnApiSecret = "apiSecret",
278
279
  hnApiSecret = pnApiSecret.toLowerCase();
279
- const MD5 = ppUtil$3.MD5,
280
- moveProperty = ppUtil$3.moveProperty;
281
280
  class apiUtil$2 {
282
281
  static debugFlag = ppUtil$3.newGuid(); //应用必须设置,否则谁也不知道是啥
283
282
  static testFlag = ppUtil$3.newGuid();
@@ -288,13 +287,14 @@ class apiUtil$2 {
288
287
  message: 'no basic auth validator'
289
288
  };
290
289
  };
290
+ static appAsPrefix = '?';
291
291
 
292
292
  // static apiCallRecSaver;
293
293
 
294
294
  static config(appConfig, appErrCfg, appDbSql) {
295
295
  conf$2 = appConfig;
296
296
  appSetting = conf$2?.appSetting;
297
- err$1 = appErrCfg;
297
+ errCfg = appErrCfg;
298
298
  dbSql = appDbSql;
299
299
  ppUtil$3.config(appConfig, appErrCfg);
300
300
  kvStorage.config(appConfig, appErrCfg);
@@ -302,6 +302,7 @@ class apiUtil$2 {
302
302
  apiUtil$2.testFlag = appSetting?.testFlag || apiUtil$2.testFlag;
303
303
  // apiUtil.apiCallRecSaver = apiCallRecSaver;
304
304
  preconditions$1.setAppName();
305
+ apiUtil$2.appAsPrefix = conf$2.thisApp.toLowerCase();
305
306
  }
306
307
 
307
308
  //#region ===== 需要应用系统重写的方法
@@ -406,7 +407,7 @@ class apiUtil$2 {
406
407
  if (this[name] === undefined) return defaultValue;
407
408
  let result = +this[name];
408
409
  if (typeof result !== "number" || result < minValue || result > maxValue) {
409
- throw [err$1.INVALID_PARAM, t_f$2("参数 %s 必须是 %s ~ %s 之间的数字", name, minValue, maxValue)];
410
+ throw [errCfg.INVALID_PARAM, t_f$2("参数 %s 必须是 %s ~ %s 之间的数字", name, minValue, maxValue)];
410
411
  }
411
412
  return result;
412
413
  });
@@ -471,7 +472,7 @@ class apiUtil$2 {
471
472
  */
472
473
  static apiFail(error, req, spOrderNum) {
473
474
  configNeeded();
474
- let response = err$1.ERROR(error, err$1.errorLangParamFlag + ppUtil$3.getMsgLang(req));
475
+ let response = errCfg.ERROR(error, errCfg.errorLangParamFlag + ppUtil$3.getMsgLang(req));
475
476
  response.datetime = new Date();
476
477
  if (spOrderNum) {
477
478
  response.spOrderNum = spOrderNum;
@@ -679,11 +680,11 @@ class apiUtil$2 {
679
680
  delete params.token;
680
681
  } else {
681
682
  // 如果没有token,则返回错误
682
- console.log(err$1.TOKEN_NEEDED);
683
+ console.log(errCfg.TOKEN_NEEDED);
683
684
  if (res) {
684
- res.send(err$1.ERROR(err$1.TOKEN_NEEDED));
685
+ res.send(errCfg.ERROR(errCfg.TOKEN_NEEDED));
685
686
  } else if (!noErr) {
686
- throw err$1.TOKEN_NEEDED;
687
+ throw errCfg.TOKEN_NEEDED;
687
688
  }
688
689
  }
689
690
  }
@@ -701,7 +702,7 @@ class apiUtil$2 {
701
702
  // return await checkInternalToken(req);
702
703
  const params = apiUtil$2.extractParams(req);
703
704
  req.accessToken = req.accessToken || apiUtil$2.extractToken(req);
704
- if (req.accessToken === MD5(ppUtil$3.commonHashDisturbing)) {
705
+ if (req.accessToken === ppCrypto.MD5(ppUtil$3.commonHashDisturbing)) {
705
706
  //todo: 检查IP
706
707
  params.__internal__ = true;
707
708
  req.ignoreToken = true;
@@ -722,7 +723,7 @@ class apiUtil$2 {
722
723
  return true;
723
724
  } else {
724
725
  // console.log('token error.', req.accessToken);
725
- throw err$1.TOKEN_INVALID;
726
+ throw errCfg.TOKEN_INVALID;
726
727
  }
727
728
  }
728
729
 
@@ -755,7 +756,7 @@ class apiUtil$2 {
755
756
  let dbgToken = req.headers['__debug__'] || req.headers[apiUtil$2.debugFlag];
756
757
  if (dbgToken) {
757
758
  let envId = process.env.ECS_DEPLOY_ID;
758
- req.__debug__ = dbgToken && dbgToken === MD5(req.headers.timestamp, ppUtil$3.commonHashDisturbing) && (envId === "florist_longdan" || envId === "shuzi");
759
+ req.__debug__ = dbgToken && dbgToken === ppCrypto.MD5(req.headers.timestamp, ppUtil$3.commonHashDisturbing) && (envId === "florist_longdan" || envId === "shuzi");
759
760
  } else {
760
761
  req.__debug__ = false;
761
762
  }
@@ -775,7 +776,7 @@ class apiUtil$2 {
775
776
  let dbgToken = req.headers["__postman__"] || req.headers[apiUtil$2.testFlag];
776
777
  if (dbgToken) {
777
778
  let envId = process.env.ECS_DEPLOY_ID;
778
- let testToken = MD5(req.headers.timestamp, apiUtil$2.extractToken(req, null, true) || req.headers.apikey);
779
+ let testToken = ppCrypto.MD5(req.headers.timestamp, apiUtil$2.extractToken(req, null, true) || req.headers.apikey);
779
780
  req.__postman__ = dbgToken === testToken && (envId.indexOf("myfacesign.com") < 0 || envId.indexOf(".dev.") > 0);
780
781
  } else {
781
782
  req.__postman__ = false;
@@ -995,7 +996,7 @@ class apiUtil$2 {
995
996
  return certInfo;
996
997
  }).catch(e => {
997
998
  console.log('获取企业证书失败:\n', e);
998
- throw [err$1.ACCESS_REFUSED, t_f$2("获取企业证书失败:%s", err$1.ERROR(e).message)];
999
+ throw [errCfg.ACCESS_REFUSED, t_f$2("获取企业证书失败:%s", errCfg.ERROR(e).message)];
999
1000
  });
1000
1001
  }
1001
1002
 
@@ -1005,20 +1006,20 @@ class apiUtil$2 {
1005
1006
  * @param req
1006
1007
  * @returns {Promise<*|boolean>}
1007
1008
  */
1008
- static async verifyApiSignature(tokenData, req) {
1009
+ static async verifyApiTokenSignature(tokenData, req) {
1009
1010
  try {
1010
1011
  if (!req.headers.timestamp) {
1011
- throw [err$1.ACCESS_REFUSED, t_f$2("必须在请求头中设置时戳")];
1012
+ throw [errCfg.ACCESS_REFUSED, t_f$2("必须在请求头中设置时戳")];
1012
1013
  }
1013
1014
  let timestamp = parseInt(req.headers.timestamp);
1014
1015
  let currentTime = new Date().valueOf();
1015
1016
  let maxTimeDiff = apiUtil$2.isDebugMode(req) ? 7 * 24 * 3600 : apiUtil$2.isTestMode(req) ? 4 * 3600 : 100; // 100
1016
1017
  if (timestamp.toString() == "NaN" || currentTime < timestamp - 5000 || currentTime - timestamp > maxTimeDiff * 1000) {
1017
- throw [err$1.ACCESS_REFUSED, t_f$2("时戳(%s)无效,当前时戳:%s,时差 %s秒", timestamp, currentTime, (currentTime - timestamp) / 1000)];
1018
+ throw [errCfg.ACCESS_REFUSED, t_f$2("时戳(%s)无效,当前时戳:%s,时差 %s秒", timestamp, currentTime, (currentTime - timestamp) / 1000)];
1018
1019
  }
1019
1020
  let signature = req.headers.signature;
1020
1021
  if (!signature) {
1021
- throw [err$1.ACCESS_REFUSED, t_f$2("必须在请求头中提供签名")];
1022
+ throw [errCfg.ACCESS_REFUSED, t_f$2("必须在请求头中提供签名")];
1022
1023
  }
1023
1024
  let signData = apiUtil$2.extractToken(req, null, true) || tokenData.apiKey;
1024
1025
  signData += timestamp;
@@ -1064,7 +1065,7 @@ class apiUtil$2 {
1064
1065
  verifyOK = false;
1065
1066
  if (!tokenData.certPublicKeySpare) {
1066
1067
  throw {
1067
- eo: err$1.ACCESS_REFUSED,
1068
+ eo: errCfg.ACCESS_REFUSED,
1068
1069
  msgArgv: t("签名验证异常"),
1069
1070
  data: e
1070
1071
  };
@@ -1109,7 +1110,7 @@ class apiUtil$2 {
1109
1110
  } catch (e) {
1110
1111
  console.log("SPO服务签名验证异常(备用证书), ", e);
1111
1112
  throw {
1112
- eo: err$1.ACCESS_REFUSED,
1113
+ eo: errCfg.ACCESS_REFUSED,
1113
1114
  msgArgv: t("签名验证异常(spare)"),
1114
1115
  data: e
1115
1116
  };
@@ -1117,7 +1118,7 @@ class apiUtil$2 {
1117
1118
  }
1118
1119
  if (!verifyOK) {
1119
1120
  console.log("SPO服务签名验证失败, tokenData: ", tokenData);
1120
- throw [err$1.ACCESS_REFUSED, t("签名验证未通过")];
1121
+ throw [errCfg.ACCESS_REFUSED, t("签名验证未通过")];
1121
1122
  }
1122
1123
  return verifyOK;
1123
1124
  }
@@ -1133,26 +1134,26 @@ class apiUtil$2 {
1133
1134
  if (!tokenData.companyInfo) {
1134
1135
  tokenData.companyInfo = await apiUtil$2.queryCompanyInfo(tokenData.apiKey, true).catch(e => {
1135
1136
  console.log("执行企业信息查询发生异常", e);
1136
- throw [err$1.API_KEY_INVALID, t("执行企业信息查询发生异常:") + e.message || ""];
1137
+ throw [errCfg.API_KEY_INVALID, t("执行企业信息查询发生异常:") + e.message || ""];
1137
1138
  });
1138
1139
  }
1139
1140
  const companyInfo = tokenData.companyInfo;
1140
1141
  if (companyInfo != null) {
1141
1142
  let secret = apiUtil$2._getApiSecret(tokenData.apiKey);
1142
- if (secret !== tokenData.apiSecret) throw err$1.API_SCREPT_INVALID;
1143
+ if (secret !== tokenData.apiSecret) throw errCfg.API_SCREPT_INVALID;
1143
1144
  // 不再检查 IP 白名单,改为验证证书签名
1144
1145
  // console.log("fromIp:", tokenData.clientIp, "ipWhiteList:", companyInfo.ipWhiteList);
1145
1146
  // let whiteList = companyInfo.ipWhiteList || "";
1146
1147
  // // 没有设置ip白名单的就不检查了
1147
1148
  // if (whiteList == "" || ipMatched(tokenData.clientIp, whiteList + ",127.0.0.1,1"))
1148
1149
  if (apiUtil$2.signatureVerified(req)) return companyInfo;
1149
- return apiUtil$2.verifyApiSignature(tokenData, req).then(x => {
1150
+ return apiUtil$2.verifyApiTokenSignature(tokenData, req).then(x => {
1150
1151
  return companyInfo;
1151
1152
  }, e => {
1152
1153
  throw e;
1153
1154
  });
1154
1155
  }
1155
- throw [err$1.API_KEY_INVALID, tokenData.apiKey];
1156
+ throw [errCfg.API_KEY_INVALID, tokenData.apiKey];
1156
1157
  }
1157
1158
 
1158
1159
  /**
@@ -1195,7 +1196,7 @@ class apiUtil$2 {
1195
1196
  } else {
1196
1197
  paramExists = typeof params[paramName] != 'undefined' || typeof params[paramName + paramSufix] != 'undefined';
1197
1198
  }
1198
- if (!paramExists) return ppUtil$3.errorPormise(err$1.ERROR(err$1.PARAMETER_NEEDED, paramName));
1199
+ if (!paramExists) return ppUtil$3.errorPormise(errCfg.ERROR(errCfg.PARAMETER_NEEDED, paramName));
1199
1200
  }
1200
1201
  return Promise.resolve(paramSufix);
1201
1202
  }
@@ -1261,7 +1262,7 @@ class apiUtil$2 {
1261
1262
  const myEnvHosts = apiUtil$2.getEnvHosts(params);
1262
1263
  const myIp = ppUtil$3.getMyIp();
1263
1264
  const host = params.host || myIp;
1264
- if (!myEnvHosts.hosts.contains(host)) throw [err$1.ACCESS_REFUSED, t_f$2("环境%s貌似无此主机:%s", conf$2.envId, params.host)];
1265
+ if (!myEnvHosts.hosts.contains(host)) throw [errCfg.ACCESS_REFUSED, t_f$2("环境%s貌似无此主机:%s", conf$2.envId, params.host)];
1265
1266
  if (!apiUtil$2.hostIsMySelf(host, options._res.req)) {
1266
1267
  let result = await apiUtil$2.forwardsTo(host, myEnvHosts.port, params, options._res.req, myIp);
1267
1268
  options._res.send(result);
@@ -1292,7 +1293,7 @@ class apiUtil$2 {
1292
1293
  let result = await netUtil$1.apiRequest(reqOptions);
1293
1294
  if (result.status !== 200) {
1294
1295
  console.log(t_f$2("访问 %s 出错", reqOptions.url), result.status, result.res.statusMessage);
1295
- throw [err$1.EXCEPTION, `[${result.status}] - ${result.res.statusMessage}`];
1296
+ throw [errCfg.EXCEPTION, `[${result.status}] - ${result.res.statusMessage}`];
1296
1297
  }
1297
1298
  return result.data;
1298
1299
  }
@@ -1314,7 +1315,7 @@ class apiUtil$2 {
1314
1315
  key = keys[i];
1315
1316
  value = parent[key];
1316
1317
  if (value === undefined) {
1317
- throw [err$1.INVALID_PARAM, t_f$2("无此配置项: %s", key)];
1318
+ throw [errCfg.INVALID_PARAM, t_f$2("无此配置项: %s", key)];
1318
1319
  }
1319
1320
  }
1320
1321
  return {
@@ -1350,6 +1351,246 @@ class apiUtil$2 {
1350
1351
  static corsCheckOrigin(origin, callback) {
1351
1352
  callback(null, apiUtil$2.isOriginAllowed(origin));
1352
1353
  }
1354
+ static validCek(cek, checkExpired = true) {
1355
+ if (!cek) {
1356
+ throw [errCfg.ACCESS_REFUSED, t("CEK(内容加密密钥)尚未申请或已过期")];
1357
+ }
1358
+ if (checkExpired && new Date().valueOf() >= cek.expireAt) {
1359
+ throw errCfg.CEK_EXPIRED;
1360
+ }
1361
+ }
1362
+ static encryptContent(content, cek) {
1363
+ return ppCrypto.encryptData(content, cek.key, 'base64');
1364
+ }
1365
+ static decryptContent(content, cek) {
1366
+ this.validCek(cek);
1367
+ try {
1368
+ const result = ppCrypto.decryptData(content, cek.key, 'utf8');
1369
+ if (result.startsWith('{') || result.startsWith('[')) {
1370
+ return JSON.parse(result);
1371
+ }
1372
+ return result;
1373
+ } catch (e) {
1374
+ throw [errCfg.DECRYPT_FAIL, e.message];
1375
+ }
1376
+ }
1377
+ static decryptRequestContent(req, res) {
1378
+ if (!appSetting.e2eEncryptionNeeded) return;
1379
+ let params = apiUtil$2.extractParams(req);
1380
+ if (params.encryptedContent) {
1381
+ if (['DELETE', 'GET'].contains(req.method)) {
1382
+ params.encryptedContent = decodeURIComponent(params.encryptedContent);
1383
+ }
1384
+ if (!req.appInfo) {
1385
+ throw t('应用系统信息未知');
1386
+ }
1387
+ const decryptedContent = apiUtil$2.decryptContent(params.encryptedContent, req.appInfo.cek);
1388
+ delete params.encryptedContent;
1389
+ Object.assign(req.params, decryptedContent);
1390
+ }
1391
+ if (res) {
1392
+ apiUtil$2.validCek(req.appInfo.cek);
1393
+ res.cek = req.appInfo.cek; // 设置用于对称加密的CEK
1394
+ }
1395
+ }
1396
+ static async restoreObj(key) {
1397
+ return kvStorage.restoreObj(key).catch(() => null);
1398
+ }
1399
+ static cekStoreKey(apiKey, req) {
1400
+ return `cek_${this.appAsPrefix}_${apiKey}_${apiUtil$2.getClientIp(req)}`;
1401
+ }
1402
+ static async generateCek(appInfo, req, key) {
1403
+ //todo: 检查既有 cek
1404
+ let t = new Date().valueOf();
1405
+ const cek = {
1406
+ key: key || ppUtil$3.newGuid() + ppUtil$3.newGuid(),
1407
+ //要求32字节
1408
+ issueAt: t,
1409
+ expireAt: t + appSetting.cekExpireTime * 1000
1410
+ };
1411
+ if (appInfo) {
1412
+ // appInfo.cek = cek;
1413
+ await kvStorage.storeObj(cekStoreKey(appInfo.apiKey, req), cek, appSetting.cekExpireTime);
1414
+ }
1415
+ return cek;
1416
+ }
1417
+ static async getContentEncKey(options) {
1418
+ const req = options._res.req;
1419
+ try {
1420
+ const appInfo = req.appInfo;
1421
+ //todo: 检查既有CEK,限制频繁申请
1422
+ const apiKey = appInfo.apiKey;
1423
+ let cek = null;
1424
+ if (appSetting.e2eEncryptionNeeded) {
1425
+ const cekKey = apiUtil$2.cekStoreKey(apiKey, req);
1426
+ // cek = await apiUtil.restoreObj(cekKey);
1427
+ cek = await apiUtil$2.restoreObj(cekKey);
1428
+ if (cek && new Date().valueOf() + 60 * 1000 >= cek.expireAt) cek = null;
1429
+ if (!cek) {
1430
+ cek = await apiUtil$2.generateCek(appInfo, req);
1431
+ }
1432
+ }
1433
+ let result = apiUtil$2.apiSuccess(cek, req);
1434
+ delete result.message; //减少数据尺寸,否则数据太长无法用公钥加密
1435
+ delete result._elapse;
1436
+ // options._res.publicKey = appInfo.publicKey; // 设置用于加密结果的公钥
1437
+ return result;
1438
+ } catch (e) {
1439
+ return apiUtil$2.apiFail(e, req);
1440
+ }
1441
+ }
1442
+ static async restoreAppInfo(apiKey, req) {
1443
+ let appInfo = await apiUtil$2.restoreObj(`${this.appAsPrefix}App_${apiKey}`, appSetting.tokenExpireTime);
1444
+ if (!appInfo) {
1445
+ appInfo = await dbUtil.dbQueryOneAndUnstringify(dbSql.APP_QUERY, {
1446
+ apiKey
1447
+ }, "exData");
1448
+ if (appInfo) {
1449
+ // await generateCek(appInfo);
1450
+ await kvStorage.storeObj(`cbpApp_${appInfo.apiKey}`, appInfo, appSetting.tokenExpireTime);
1451
+ }
1452
+ }
1453
+ // if (appInfo && !appInfo.cek && appSetting.e2eEncryptionNeeded) {
1454
+ if (appInfo && appSetting.e2eEncryptionNeeded) {
1455
+ appInfo.cek = await apiUtil$2.restoreObj(apiUtil$2.cekStoreKey(apiKey, req));
1456
+ }
1457
+ return appInfo;
1458
+ }
1459
+ static async generateApiSignature(options) {
1460
+ const params = options.replacements || options;
1461
+ await this.parametersOK(params, 'apiKey', 'privateKey');
1462
+ // console.log("generateApiSignature for apiKey", params.apiKey);
1463
+ // console.log("privateKey", params.privateKey);
1464
+ const result = {
1465
+ headers: {
1466
+ "api-key": params.apiKey
1467
+ },
1468
+ body: {
1469
+ encryptedContent: ''
1470
+ }
1471
+ };
1472
+ const appInfo = await this.restoreAppInfo(params.apiKey, options._res.req);
1473
+ if (!appInfo) {
1474
+ throw [errCfg.ACCESS_REFUSED, t_f$2("apiKey (%s) 无效", params.apiKey)];
1475
+ }
1476
+ let content = '';
1477
+ if (appSetting.e2eEncryptionNeeded) {
1478
+ if (params.decryptedContent) {
1479
+ // if (!appInfo.cek) {
1480
+ // await generateCek(appInfo);
1481
+ // }
1482
+ await this.parametersOK(params, 'cek');
1483
+ const cek = await this.generateCek(undefined, undefined, params.cek.key || params.cek); //避免过期
1484
+ // commonUtil.validCek(params.cek);
1485
+ content = this.encryptContent(params.decryptedContent, cek);
1486
+ result.body.encryptedContent = content;
1487
+ }
1488
+ } else {
1489
+ // result.body = null
1490
+ content = JSON.stringify(params.decryptedContent);
1491
+ }
1492
+ if (appSetting.reqSignatureNeeded) {
1493
+ result.headers.timestamp = new Date().valueOf();
1494
+ result.headers.signature = this.getRequestSignature(appInfo.apiKey, result.headers.timestamp, content, params.privateKey);
1495
+ }
1496
+ return result;
1497
+ }
1498
+ static getRequestSignature(apiKey, timestamp, content, privateKey) {
1499
+ let dataToSign = apiKey + timestamp + (content || '');
1500
+ const sign = crypto.createSign('RSA-SHA256');
1501
+ sign.update(dataToSign);
1502
+ return sign.sign(privateKey, 'base64');
1503
+ }
1504
+ static async decryptApiResponse(options) {
1505
+ const params = options.replacements || options;
1506
+ await this.parametersOK(params, 'encryptedContent', 'cek/privateKey');
1507
+ let result;
1508
+ if (params.cek) {
1509
+ const cek = await this.generateCek(undefined, undefined, params.cek.key || params.cek); //避免过期
1510
+ result = this.decryptContent(params.encryptedContent, cek);
1511
+ console.log('CEK 解密结果:', result);
1512
+ } else {
1513
+ let content = await ppCrypto.rsaDecrypt(params);
1514
+ result = JSON.parse(content.decryptedContent);
1515
+ // result = await commonUtil.rsaDecrypt(params);
1516
+ }
1517
+ return result;
1518
+ }
1519
+ static async reqAppInfoNeeded(req) {
1520
+ if (!req.appInfo) {
1521
+ let apiKey = req.headers["api-key"] || req.leInfo?.apiKey || req.userInfo?.apiKey;
1522
+ if (!apiKey) {
1523
+ throw [errCfg.ACCESS_REFUSED, t("必须在请求头中设置api-key")];
1524
+ }
1525
+ req.appInfo = await this.restoreAppInfo(apiKey, req);
1526
+ if (!req.appInfo) {
1527
+ throw [errCfg.ACCESS_REFUSED, t_f$2("api-key (%s) 无效", apiKey)];
1528
+ }
1529
+ }
1530
+ }
1531
+ static async verifyApiSignature(req) {
1532
+ if (req.appInfo) {
1533
+ return true;
1534
+ }
1535
+ await this.reqAppInfoNeeded(req);
1536
+ if (!appSetting.reqSignatureNeeded) return true;
1537
+ if (!req.headers.timestamp) {
1538
+ throw [errCfg.ACCESS_REFUSED, t_f$2("必须在请求头中设置时戳")];
1539
+ }
1540
+ let timestamp = parseInt(req.headers.timestamp);
1541
+ let currentTime = new Date().valueOf();
1542
+ let maxTimeDiff = apiUtil$2.isDebugMode(req) ? 7 * 24 * 3600 : apiUtil$2.isTestMode(req) ? 4 * 3600 : 300; // 100
1543
+ if (timestamp.toString() === "NaN" || currentTime < timestamp - 5000 || currentTime - timestamp > maxTimeDiff * 1000) {
1544
+ throw [errCfg.ACCESS_REFUSED, t_f$2("时戳(%s)无效,当前时戳:%s,时差 %s秒", timestamp, currentTime, (currentTime - timestamp) / 1000)];
1545
+ }
1546
+ let signature = req.headers.signature;
1547
+ if (!signature) {
1548
+ throw [errCfg.ACCESS_REFUSED, t_f$2("必须在请求头中提供签名")];
1549
+ }
1550
+ const params = apiUtil$2.extractParams(req);
1551
+ const content = appSetting.e2eEncryptionNeeded ? params?.encryptedContent || '' : JSON.stringify(params || null);
1552
+ const apiKey = req.appInfo.apiKey;
1553
+ let signData = apiKey + timestamp + content;
1554
+ let verify = crypto.createVerify('RSA-SHA256');
1555
+ verify.update(signData);
1556
+ let verifyOK = false;
1557
+ try {
1558
+ verifyOK = verify.verify(req.appInfo.publicKey, signature, 'base64');
1559
+ } catch (e) {
1560
+ console.log("服务签名验证异常, ", e);
1561
+ verifyOK = false;
1562
+ }
1563
+ if (!verifyOK) {
1564
+ // console.log("服务签名验证失败, tokenData: ", tokenData);
1565
+ throw [errCfg.ACCESS_REFUSED, t("签名验证未通过")];
1566
+ }
1567
+ return verifyOK;
1568
+ }
1569
+ static checkWhiteList(req) {
1570
+ const ipWhiteList = req.appInfo.exData?.ipWhiteList;
1571
+ if (!ipWhiteList || ipWhiteList.length === 0) {
1572
+ // 没有设置ip白名单的就不检查了
1573
+ return true;
1574
+ }
1575
+ let clientIp = apiUtil$2.getClientIp(req);
1576
+ console.log("fromIp:", clientIp, "ipWhiteList:", ipWhiteList);
1577
+ if (!clientIp) {
1578
+ throw errCfg.GET_CLIENTIP_FAIL;
1579
+ }
1580
+ return this.ipMatched(clientIp, ipWhiteList, true);
1581
+ }
1582
+ static ipMatched(fromIp, whiteList, localAllowed) {
1583
+ let ips = Array.isArray(whiteList) ? whiteList : (whiteList || '').split(',');
1584
+ for (let i = 0; i < ips.length; i++) {
1585
+ let ip = ips[i];
1586
+ if (ip === fromIp) return true;
1587
+ if (ip.indexOf("*") > 0) {
1588
+ let l = ip.indexOf("*");
1589
+ if (fromIp.substring(0, l) === ip.substring(0, l)) return true;
1590
+ }
1591
+ }
1592
+ return localAllowed && (fromIp === "127.0.0.1" || fromIp === "1");
1593
+ }
1353
1594
 
1354
1595
  //#endregion
1355
1596
 
@@ -1363,7 +1604,7 @@ class apiUtil$2 {
1363
1604
  await apiUtil$2.checkRequestToken(req);
1364
1605
  next();
1365
1606
  } catch (e) {
1366
- res.send(err$1.ERROR(e));
1607
+ res.send(errCfg.ERROR(e));
1367
1608
  }
1368
1609
  return false;
1369
1610
  }
@@ -1405,7 +1646,7 @@ class apiUtil$2 {
1405
1646
  }
1406
1647
  return true;
1407
1648
  } catch (e) {
1408
- res.send(err$1.ERROR(e));
1649
+ res.send(errCfg.ERROR(e));
1409
1650
  return false;
1410
1651
  }
1411
1652
  }
@@ -1426,9 +1667,9 @@ class apiUtil$2 {
1426
1667
  req.isMobile = true;
1427
1668
  if (!apiUtil$2.signatureVerified(req)) {
1428
1669
  let params = apiUtil$2.extractParams(req);
1429
- moveProperty(params, req.headers, "access_token");
1430
- moveProperty(params, req.headers, "timestamp");
1431
- if (moveProperty(params, req.headers, "signature")) {
1670
+ ppUtil$3.moveProperty(params, req.headers, "access_token");
1671
+ ppUtil$3.moveProperty(params, req.headers, "timestamp");
1672
+ if (ppUtil$3.moveProperty(params, req.headers, "signature")) {
1432
1673
  req.headers.signature = decodeURIComponent(req.headers.signature);
1433
1674
  }
1434
1675
  }
@@ -1448,11 +1689,11 @@ class apiUtil$2 {
1448
1689
  apiUtil$2.createApiCallRec(tokenData, req);
1449
1690
  let errResponse = null;
1450
1691
  if (!tokenData.clientIp) {
1451
- errResponse = apiUtil$2.spoApiFail(err$1.GET_CLIENTIP_FAIL, req);
1692
+ errResponse = apiUtil$2.spoApiFail(errCfg.GET_CLIENTIP_FAIL, req);
1452
1693
  } else if (!tokenData.apiKey) {
1453
- errResponse = apiUtil$2.spoApiFail([err$1.PARAMETER_NEEDED, "apiKey"], req);
1694
+ errResponse = apiUtil$2.spoApiFail([errCfg.PARAMETER_NEEDED, "apiKey"], req);
1454
1695
  } else if (!tokenData.apiSecret) {
1455
- errResponse = apiUtil$2.spoApiFail([err$1.PARAMETER_NEEDED, "apiSecret"], req);
1696
+ errResponse = apiUtil$2.spoApiFail([errCfg.PARAMETER_NEEDED, "apiSecret"], req);
1456
1697
  }
1457
1698
  if (errResponse != null) {
1458
1699
  apiUtil$2._saveApiCallRec(req.apiCallRec, errResponse);
@@ -1481,7 +1722,7 @@ class apiUtil$2 {
1481
1722
  }
1482
1723
  let accessToken = apiUtil$2.extractToken(req);
1483
1724
  if (!accessToken) {
1484
- res.send(apiUtil$2.spoApiFail(err$1.TOKEN_NEEDED, req));
1725
+ res.send(apiUtil$2.spoApiFail(errCfg.TOKEN_NEEDED, req));
1485
1726
  // apiUtil.sendOrRedirect(req, res, spoApiFail(err.TOKEN_NEEDED, req));
1486
1727
  return;
1487
1728
  }
@@ -1501,7 +1742,7 @@ class apiUtil$2 {
1501
1742
  return apiUtil$2.$checkApiKeyOld(req, res, next);
1502
1743
  }).catch(e => {
1503
1744
  const params = apiUtil$2.extractParams(req);
1504
- apiUtil$2.sendOrRedirect(res, params.redirectUrl || params.callbackUrl, apiUtil$2.spoApiFail(err$1.TOKEN_INVALID, req));
1745
+ apiUtil$2.sendOrRedirect(res, params.redirectUrl || params.callbackUrl, apiUtil$2.spoApiFail(errCfg.TOKEN_INVALID, req));
1505
1746
  });
1506
1747
  }
1507
1748
 
@@ -1611,7 +1852,7 @@ class apiUtil$2 {
1611
1852
  await apiUtil$2.parametersOK(params, "value");
1612
1853
  let c = apiUtil$2.parseCfgPath(cfgItem);
1613
1854
  if (typeof c.value === "object") {
1614
- throw [err$1.ACCESS_REFUSED, t_f$2("暂不支持修改复杂配置")];
1855
+ throw [errCfg.ACCESS_REFUSED, t_f$2("暂不支持修改复杂配置")];
1615
1856
  }
1616
1857
  // c.oldValue = c.value;
1617
1858
  c.newValue = params.value;
@@ -1664,11 +1905,11 @@ class apiUtil$2 {
1664
1905
  //#endregion
1665
1906
  }
1666
1907
  function configNeeded() {
1667
- if (!conf$2 || !err$1) {
1908
+ if (!conf$2 || !errCfg) {
1668
1909
  ppUtil$3.configNeeded();
1669
1910
  conf$2 = ppUtil$3.appConfig;
1670
1911
  appSetting = conf$2.appSetting;
1671
- err$1 = ppUtil$3.appErrCfg;
1912
+ errCfg = ppUtil$3.appErrCfg;
1672
1913
  }
1673
1914
  }
1674
1915
  var ppUtilApi = apiUtil$2;
@@ -1700,6 +1941,12 @@ class commonMessenger$2 {
1700
1941
  commonMessenger$2.redisMessenger = conf$1.redis.enabled ? new RedisMessenger(appConfig, appErrCfg, msgChannel).subscribe(msgChannel) : null;
1701
1942
  commonMessenger$2.myMsgAddr = commonMessenger$2.getMyMsgAddr();
1702
1943
  }
1944
+ static subscribe(channel) {
1945
+ if (commonMessenger$2.redisMessenger) {
1946
+ commonMessenger$2.redisMessenger.subscribe(channel);
1947
+ }
1948
+ return commonMessenger$2;
1949
+ }
1703
1950
  static getMyMsgAddr() {
1704
1951
  if (!conf$1.myMsgAddr) {
1705
1952
  conf$1.pm_id = process.env.pm_id || "0";
@@ -1723,7 +1970,7 @@ class commonMessenger$2 {
1723
1970
  }
1724
1971
  static async publishMessage(message, msgChannel) {
1725
1972
  if (commonMessenger$2.redisMessenger) {
1726
- return commonMessenger$2.redisMessenger.publish(message, msgChannel);
1973
+ commonMessenger$2.redisMessenger.publish(message, msgChannel);
1727
1974
  } else {
1728
1975
  //没有消息处理器,则将消息转发到目标地址(不支持pm2多进程)
1729
1976
  if (message.destAddr) {
@@ -1737,6 +1984,7 @@ class commonMessenger$2 {
1737
1984
  }
1738
1985
  }
1739
1986
  }
1987
+ return commonMessenger$2;
1740
1988
  }
1741
1989
  static async handleInternalMessage(options) {
1742
1990
  const message = options.replacements;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abler-api",
3
- "version": "0.1.80",
3
+ "version": "0.2.0",
4
4
  "description": "API服务相关工具",
5
5
  "main": "./dist/cjs/pp-util.js",
6
6
  "-module": "./dist/es/pp-util.js",
@@ -16,7 +16,7 @@
16
16
  "license": "ISC",
17
17
  "dependencies": {
18
18
  "q": "^1.5.1",
19
- "abler-util": ">=0.1.1",
19
+ "abler-util": ">=0.3.0",
20
20
  "abler-db": ">=0.1.1",
21
21
  "abler-i18n": "^0.1.20",
22
22
  "abler-messenger": ">=0.1.1",