@be-link/shield-for-tcb-node-sdk 1.0.19 → 1.0.21

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.
@@ -6,6 +6,7 @@ import BaseService from '../BaseService';
6
6
  */
7
7
  export declare class ConfigServiceV2 extends BaseService implements ServiceV2.ConfigController {
8
8
  protected prefixUrl: string;
9
+ private callEncryptedConfig;
9
10
  /**
10
11
  * 获取单个配置
11
12
  * @param req 请求参数
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.backendConfigServiceV2 = exports.ConfigServiceV2 = void 0;
7
7
  const http_1 = require("../../utils/http");
8
8
  const cloudFunctionHttp_1 = require("../../utils/cloudFunctionHttp");
9
+ const encryption_1 = require("../../utils/encryption");
9
10
  const BaseService_1 = __importDefault(require("../BaseService"));
10
11
  /**
11
12
  * V2 配置服务
@@ -16,15 +17,32 @@ class ConfigServiceV2 extends BaseService_1.default {
16
17
  super(...arguments);
17
18
  this.prefixUrl = '/shield/v2/config';
18
19
  }
20
+ async callEncryptedConfig(path, req, fallbackData) {
21
+ const keyId = (0, encryption_1.getKeyId)();
22
+ const enhancedReq = keyId ? { ...req, encryption: { version: 1, keyId } } : req;
23
+ const response = await (0, http_1.callApiFull)(this.getApiUrlV2(path), enhancedReq).catch(() => ({
24
+ success: false,
25
+ data: fallbackData,
26
+ requestId: '',
27
+ message: '',
28
+ errorType: '',
29
+ encryption: undefined,
30
+ }));
31
+ if (response.encryption?.version && typeof response.data === 'string') {
32
+ return (0, encryption_1.decryptPayload)(response.data);
33
+ }
34
+ if ((0, encryption_1.isEncryptionAvailable)()) {
35
+ throw new Error('Encrypted response required but server returned plaintext');
36
+ }
37
+ return response.data;
38
+ }
19
39
  /**
20
40
  * 获取单个配置
21
41
  * @param req 请求参数
22
42
  * @returns 配置数据,如果配置不存在返回 null
23
43
  */
24
44
  fetchConfig(req) {
25
- return (0, http_1.callApi)(this.getApiUrlV2('/fetch'), req, {
26
- skipErrorHandling: true,
27
- }).catch(() => null);
45
+ return this.callEncryptedConfig('/fetch', req, null);
28
46
  }
29
47
  /**
30
48
  * 批量获取配置
@@ -32,7 +50,7 @@ class ConfigServiceV2 extends BaseService_1.default {
32
50
  * @returns 配置数据映射
33
51
  */
34
52
  fetchConfigs(req) {
35
- return (0, http_1.callApi)(this.getApiUrlV2('/fetch-batch'), req).catch(() => ({}));
53
+ return this.callEncryptedConfig('/fetch-batch', req, {});
36
54
  }
37
55
  /**
38
56
  * 云函数单 Key 配置读取
@@ -18,6 +18,10 @@ export declare namespace ServiceV2 {
18
18
  }
19
19
  }
20
20
  namespace Request {
21
+ interface EncryptionRequest {
22
+ version: number;
23
+ keyId: string;
24
+ }
21
25
  /**
22
26
  * 获取单个配置请求参数
23
27
  */
@@ -25,6 +29,7 @@ export declare namespace ServiceV2 {
25
29
  service: string;
26
30
  key: string;
27
31
  env?: string;
32
+ encryption?: EncryptionRequest;
28
33
  }
29
34
  /**
30
35
  * 批量获取配置请求参数
@@ -33,6 +38,7 @@ export declare namespace ServiceV2 {
33
38
  service: string;
34
39
  keys?: string[];
35
40
  env?: string;
41
+ encryption?: EncryptionRequest;
36
42
  }
37
43
  /**
38
44
  * 获取服务列表请求参数
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@be-link/shield-for-tcb-node-sdk",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "ShieldForTCB Node.js SDK",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -0,0 +1,3 @@
1
+ export declare function isEncryptionAvailable(): boolean;
2
+ export declare function getKeyId(): string | null;
3
+ export declare function decryptPayload(encryptedBase64: string): any;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isEncryptionAvailable = isEncryptionAvailable;
7
+ exports.getKeyId = getKeyId;
8
+ exports.decryptPayload = decryptPayload;
9
+ const crypto_1 = __importDefault(require("crypto"));
10
+ const ALGORITHM = 'aes-256-gcm';
11
+ const IV_LENGTH = 12;
12
+ const AUTH_TAG_LENGTH = 16;
13
+ function getDecryptionKey() {
14
+ const keyB64 = process.env.SHIELD_ENCRYPTION_KEY;
15
+ if (!keyB64)
16
+ return null;
17
+ return Buffer.from(keyB64, 'base64');
18
+ }
19
+ function isEncryptionAvailable() {
20
+ return !!process.env.SHIELD_ENCRYPTION_KEY;
21
+ }
22
+ function getKeyId() {
23
+ const key = getDecryptionKey();
24
+ if (!key)
25
+ return null;
26
+ return crypto_1.default.createHash('sha256').update(key).digest('hex').slice(0, 8);
27
+ }
28
+ function decryptPayload(encryptedBase64) {
29
+ const key = getDecryptionKey();
30
+ if (!key)
31
+ throw new Error('SHIELD_ENCRYPTION_KEY not configured');
32
+ const data = Buffer.from(encryptedBase64, 'base64');
33
+ const iv = data.subarray(0, IV_LENGTH);
34
+ const authTag = data.subarray(data.length - AUTH_TAG_LENGTH);
35
+ const ciphertext = data.subarray(IV_LENGTH, data.length - AUTH_TAG_LENGTH);
36
+ const decipher = crypto_1.default.createDecipheriv(ALGORITHM, key, iv);
37
+ decipher.setAuthTag(authTag);
38
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
39
+ return JSON.parse(decrypted.toString('utf8'));
40
+ }
package/utils/http.d.ts CHANGED
@@ -1,10 +1,22 @@
1
- type CallApiOptions = {
1
+ export type ResponseData = {
2
+ data: any;
3
+ success: boolean;
4
+ requestId: string;
5
+ message: string;
6
+ errorType: string;
7
+ encryption?: {
8
+ version: number;
9
+ keyId: string;
10
+ };
11
+ };
12
+ export type CallApiOptions = {
2
13
  skipErrorHandling?: boolean;
3
14
  };
4
15
  type GetApiOptions = {
5
16
  skipErrorHandling?: boolean;
6
17
  };
7
18
  export declare function callApi<T extends (args: any) => Promise<any>>(url: string, request?: Parameters<T>[0], options?: CallApiOptions): Promise<Awaited<ReturnType<T>>>;
19
+ export declare function callApiFull<TRequest = any>(url: string, request?: TRequest, options?: CallApiOptions): Promise<ResponseData>;
8
20
  /**
9
21
  * GET 请求 API
10
22
  */
package/utils/http.js CHANGED
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.callApi = callApi;
40
+ exports.callApiFull = callApiFull;
40
41
  exports.getApi = getApi;
41
42
  const axios_1 = __importDefault(require("axios"));
42
43
  const uuid_1 = require("uuid");
@@ -100,6 +101,32 @@ async function callApi(url, request, options) {
100
101
  throw error;
101
102
  }
102
103
  }
104
+ async function callApiFull(url, request, options) {
105
+ const requestId = (0, uuid_1.v4)();
106
+ try {
107
+ console.info(`准备发起shield-for-tcb请求[${requestId}]: ${url}, 参数: ${JSON.stringify(request)}`);
108
+ const response = await axios_1.default.post(url, request || {}, {
109
+ headers: { 'x-request-id': requestId, 'content-type': 'application/json' },
110
+ });
111
+ return response.data;
112
+ }
113
+ catch (error) {
114
+ if (options && options.skipErrorHandling) {
115
+ throw error;
116
+ }
117
+ const axiosError = error;
118
+ if (axiosError.response) {
119
+ const response = axiosError.response;
120
+ const data = response.data;
121
+ console.error(`shield-for-tcb 异常: ${axiosError.message}, requestId: ${requestId}`);
122
+ console.info('响应信息', data.message);
123
+ console.error('异常堆栈', JSON.stringify(error.stack));
124
+ throw axiosError;
125
+ }
126
+ console.error(`shield-for-tcb 未知异常: ${axiosError.message}`, error.stack);
127
+ throw error;
128
+ }
129
+ }
103
130
  /**
104
131
  * GET 请求 API
105
132
  */