abler-api 0.1.81 → 0.2.3

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