@be-link/shield-for-tcb-node-sdk 1.0.20 → 1.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/modules/config/ShieldConfigManager.d.ts +5 -0
- package/modules/config/ShieldConfigManager.js +14 -10
- package/modules/config/serviceV2.d.ts +1 -0
- package/modules/config/serviceV2.js +22 -4
- package/modules/config/typesV2.d.ts +6 -0
- package/package.json +1 -1
- package/utils/encryption.d.ts +3 -0
- package/utils/encryption.js +40 -0
- package/utils/http.d.ts +13 -1
- package/utils/http.js +27 -0
|
@@ -39,6 +39,11 @@ export interface ShieldConfigManagerOptions {
|
|
|
39
39
|
enableInfoLog?: boolean;
|
|
40
40
|
/** 自定义日志函数,默认 console */
|
|
41
41
|
logger?: Logger;
|
|
42
|
+
/**
|
|
43
|
+
* 是否拉取 V1 全局动态配置(设置 process.env.authorizationTokenInside),默认 true(向后兼容 V1 SDK)。
|
|
44
|
+
* 纯 V2 接入、不需要 authorizationTokenInside 的场景可设为 false 跳过该逻辑。
|
|
45
|
+
*/
|
|
46
|
+
enableV1DynamicConfig?: boolean;
|
|
42
47
|
}
|
|
43
48
|
export interface Logger {
|
|
44
49
|
info: (message: string, ...args: any[]) => void;
|
|
@@ -71,7 +71,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
71
71
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
72
72
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
73
73
|
};
|
|
74
|
-
var _ShieldConfigManager_instances, _ShieldConfigManager_serviceName, _ShieldConfigManager_env, _ShieldConfigManager_refreshIntervalMs, _ShieldConfigManager_fetchTimeoutMs, _ShieldConfigManager_enableInfoLog, _ShieldConfigManager_logger, _ShieldConfigManager_config, _ShieldConfigManager_refreshTimer, _ShieldConfigManager_isSetup, _ShieldConfigManager_setupPromise, _ShieldConfigManager_doSetup, _ShieldConfigManager_startRefreshTimer;
|
|
74
|
+
var _ShieldConfigManager_instances, _ShieldConfigManager_serviceName, _ShieldConfigManager_env, _ShieldConfigManager_refreshIntervalMs, _ShieldConfigManager_fetchTimeoutMs, _ShieldConfigManager_enableInfoLog, _ShieldConfigManager_enableV1DynamicConfig, _ShieldConfigManager_logger, _ShieldConfigManager_config, _ShieldConfigManager_refreshTimer, _ShieldConfigManager_isSetup, _ShieldConfigManager_setupPromise, _ShieldConfigManager_doSetup, _ShieldConfigManager_startRefreshTimer;
|
|
75
75
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
76
76
|
exports.ShieldConfigManager = void 0;
|
|
77
77
|
const serviceV2_1 = require("./serviceV2");
|
|
@@ -103,6 +103,7 @@ class ShieldConfigManager {
|
|
|
103
103
|
_ShieldConfigManager_refreshIntervalMs.set(this, void 0);
|
|
104
104
|
_ShieldConfigManager_fetchTimeoutMs.set(this, void 0);
|
|
105
105
|
_ShieldConfigManager_enableInfoLog.set(this, void 0);
|
|
106
|
+
_ShieldConfigManager_enableV1DynamicConfig.set(this, void 0);
|
|
106
107
|
_ShieldConfigManager_logger.set(this, void 0);
|
|
107
108
|
_ShieldConfigManager_config.set(this, null);
|
|
108
109
|
_ShieldConfigManager_refreshTimer.set(this, null);
|
|
@@ -116,6 +117,7 @@ class ShieldConfigManager {
|
|
|
116
117
|
__classPrivateFieldSet(this, _ShieldConfigManager_refreshIntervalMs, options.refreshIntervalMs || DEFAULT_REFRESH_INTERVAL_MS, "f");
|
|
117
118
|
__classPrivateFieldSet(this, _ShieldConfigManager_fetchTimeoutMs, options.fetchTimeoutMs || DEFAULT_FETCH_TIMEOUT_MS, "f");
|
|
118
119
|
__classPrivateFieldSet(this, _ShieldConfigManager_enableInfoLog, options.enableInfoLog || false, "f");
|
|
120
|
+
__classPrivateFieldSet(this, _ShieldConfigManager_enableV1DynamicConfig, options.enableV1DynamicConfig !== false, "f");
|
|
119
121
|
const baseLogger = options.logger || defaultLogger;
|
|
120
122
|
__classPrivateFieldSet(this, _ShieldConfigManager_logger, __classPrivateFieldGet(this, _ShieldConfigManager_enableInfoLog, "f") ? baseLogger : { ...baseLogger, info: () => { } }, "f");
|
|
121
123
|
}
|
|
@@ -165,15 +167,17 @@ class ShieldConfigManager {
|
|
|
165
167
|
catch (error) {
|
|
166
168
|
__classPrivateFieldGet(this, _ShieldConfigManager_logger, "f").error(`ShieldConfigManager refresh error: ${error instanceof Error ? error.message : JSON.stringify(error)}`);
|
|
167
169
|
}
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
// 兼容旧的逻辑(V1 全局动态配置),可通过 enableV1DynamicConfig: false 跳过
|
|
171
|
+
if (__classPrivateFieldGet(this, _ShieldConfigManager_enableV1DynamicConfig, "f")) {
|
|
172
|
+
try {
|
|
173
|
+
const dynamicConfig = await withTimeout(shieldClient.backendConfigService.fetchGlobalDynamicConfig(), __classPrivateFieldGet(this, _ShieldConfigManager_fetchTimeoutMs, "f"));
|
|
174
|
+
if (dynamicConfig) {
|
|
175
|
+
process.env.authorizationTokenInside = dynamicConfig.tokenKey;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
__classPrivateFieldGet(this, _ShieldConfigManager_logger, "f").error(`fetch dynamic config error: ${error instanceof Error ? error.message : JSON.stringify(error)}`);
|
|
173
180
|
}
|
|
174
|
-
}
|
|
175
|
-
catch (error) {
|
|
176
|
-
__classPrivateFieldGet(this, _ShieldConfigManager_logger, "f").error(`fetch dynamic config error: ${error instanceof Error ? error.message : JSON.stringify(error)}`);
|
|
177
181
|
}
|
|
178
182
|
__classPrivateFieldGet(this, _ShieldConfigManager_logger, "f").info(`ShieldConfigManager config refreshed finally (config: ${JSON.stringify(__classPrivateFieldGet(this, _ShieldConfigManager_config, "f"))})`);
|
|
179
183
|
}
|
|
@@ -281,7 +285,7 @@ class ShieldConfigManager {
|
|
|
281
285
|
}
|
|
282
286
|
}
|
|
283
287
|
exports.ShieldConfigManager = ShieldConfigManager;
|
|
284
|
-
_ShieldConfigManager_serviceName = new WeakMap(), _ShieldConfigManager_env = new WeakMap(), _ShieldConfigManager_refreshIntervalMs = new WeakMap(), _ShieldConfigManager_fetchTimeoutMs = new WeakMap(), _ShieldConfigManager_enableInfoLog = new WeakMap(), _ShieldConfigManager_logger = new WeakMap(), _ShieldConfigManager_config = new WeakMap(), _ShieldConfigManager_refreshTimer = new WeakMap(), _ShieldConfigManager_isSetup = new WeakMap(), _ShieldConfigManager_setupPromise = new WeakMap(), _ShieldConfigManager_instances = new WeakSet(), _ShieldConfigManager_doSetup = async function _ShieldConfigManager_doSetup() {
|
|
288
|
+
_ShieldConfigManager_serviceName = new WeakMap(), _ShieldConfigManager_env = new WeakMap(), _ShieldConfigManager_refreshIntervalMs = new WeakMap(), _ShieldConfigManager_fetchTimeoutMs = new WeakMap(), _ShieldConfigManager_enableInfoLog = new WeakMap(), _ShieldConfigManager_enableV1DynamicConfig = new WeakMap(), _ShieldConfigManager_logger = new WeakMap(), _ShieldConfigManager_config = new WeakMap(), _ShieldConfigManager_refreshTimer = new WeakMap(), _ShieldConfigManager_isSetup = new WeakMap(), _ShieldConfigManager_setupPromise = new WeakMap(), _ShieldConfigManager_instances = new WeakSet(), _ShieldConfigManager_doSetup = async function _ShieldConfigManager_doSetup() {
|
|
285
289
|
try {
|
|
286
290
|
await this.refresh();
|
|
287
291
|
}
|
|
@@ -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
|
|
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
|
|
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
|
@@ -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
|
|
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
|
*/
|