@nest-omni/core 4.1.3-25 → 4.1.3-26
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/cache/cache.service.d.ts +3 -1
- package/cache/cache.service.js +8 -8
- package/cache/decorators/cache-put.decorator.js +5 -4
- package/cache/dependencies/callback.dependency.js +9 -0
- package/cache/dependencies/tag.dependency.d.ts +1 -9
- package/cache/dependencies/tag.dependency.js +5 -14
- package/cache/providers/lrucache.provider.d.ts +1 -0
- package/cache/providers/lrucache.provider.js +6 -4
- package/cache/providers/redis-cache.provider.d.ts +1 -0
- package/cache/providers/redis-cache.provider.js +8 -6
- package/http-client/config/http-client.config.js +3 -1
- package/http-client/examples/axios-config-extended.example.js +1 -3
- package/http-client/examples/flexible-response-example.d.ts +28 -0
- package/http-client/examples/flexible-response-example.js +120 -0
- package/http-client/examples/ssl-certificate.example.d.ts +2 -2
- package/http-client/examples/ssl-certificate.example.js +18 -17
- package/http-client/services/api-client-registry.service.d.ts +6 -6
- package/http-client/services/api-client-registry.service.js +8 -8
- package/http-client/services/circuit-breaker.service.d.ts +9 -9
- package/http-client/services/circuit-breaker.service.js +24 -24
- package/http-client/services/http-client.service.d.ts +30 -13
- package/http-client/services/http-client.service.js +75 -46
- package/http-client/services/logging.service.d.ts +16 -16
- package/http-client/services/logging.service.js +46 -45
- package/http-client/utils/proxy-environment.util.d.ts +12 -12
- package/http-client/utils/proxy-environment.util.js +25 -19
- package/http-client/utils/security-validator.util.d.ts +19 -19
- package/http-client/utils/security-validator.util.js +66 -64
- package/package.json +1 -1
- package/vault/vault-config.service.js +1 -1
|
@@ -55,15 +55,6 @@ export declare class HttpCircuitBreakerService {
|
|
|
55
55
|
* 清理超过最大空闲时间的Circuit Breaker
|
|
56
56
|
*/
|
|
57
57
|
cleanupIdle(): number;
|
|
58
|
-
/**
|
|
59
|
-
* 基于LRU策略清理Circuit Breaker
|
|
60
|
-
* 删除最久未访问的Circuit Breaker直到数量降到maxSize以下
|
|
61
|
-
*/
|
|
62
|
-
private cleanupLRU;
|
|
63
|
-
/**
|
|
64
|
-
* 启动定期清理任务
|
|
65
|
-
*/
|
|
66
|
-
private startCleanupTask;
|
|
67
58
|
/**
|
|
68
59
|
* 手动触发清理(用于测试)
|
|
69
60
|
*/
|
|
@@ -76,6 +67,15 @@ export declare class HttpCircuitBreakerService {
|
|
|
76
67
|
* 获取清理配置
|
|
77
68
|
*/
|
|
78
69
|
getCleanupConfig(): CircuitBreakerCleanupConfig;
|
|
70
|
+
/**
|
|
71
|
+
* 基于LRU策略清理Circuit Breaker
|
|
72
|
+
* 删除最久未访问的Circuit Breaker直到数量降到maxSize以下
|
|
73
|
+
*/
|
|
74
|
+
private cleanupLRU;
|
|
75
|
+
/**
|
|
76
|
+
* 启动定期清理任务
|
|
77
|
+
*/
|
|
78
|
+
private startCleanupTask;
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* 熔断器类
|
|
@@ -160,6 +160,30 @@ let HttpCircuitBreakerService = HttpCircuitBreakerService_1 = class HttpCircuitB
|
|
|
160
160
|
}
|
|
161
161
|
return keysToDelete.length;
|
|
162
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* 手动触发清理(用于测试)
|
|
165
|
+
*/
|
|
166
|
+
manualCleanup() {
|
|
167
|
+
return this.cleanupIdle();
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 设置清理配置
|
|
171
|
+
*/
|
|
172
|
+
setCleanupConfig(config) {
|
|
173
|
+
Object.assign(this.cleanupConfig, config);
|
|
174
|
+
// 重启清理任务
|
|
175
|
+
if (this.cleanupTimer) {
|
|
176
|
+
clearInterval(this.cleanupTimer);
|
|
177
|
+
}
|
|
178
|
+
this.startCleanupTask();
|
|
179
|
+
this.logger.log(`Circuit breaker cleanup config updated: ${JSON.stringify(this.cleanupConfig)}`);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 获取清理配置
|
|
183
|
+
*/
|
|
184
|
+
getCleanupConfig() {
|
|
185
|
+
return Object.assign({}, this.cleanupConfig);
|
|
186
|
+
}
|
|
163
187
|
/**
|
|
164
188
|
* 基于LRU策略清理Circuit Breaker
|
|
165
189
|
* 删除最久未访问的Circuit Breaker直到数量降到maxSize以下
|
|
@@ -194,30 +218,6 @@ let HttpCircuitBreakerService = HttpCircuitBreakerService_1 = class HttpCircuitB
|
|
|
194
218
|
}, this.cleanupConfig.cleanupIntervalMs);
|
|
195
219
|
this.logger.log(`Circuit breaker cleanup task started (interval: ${this.cleanupConfig.cleanupIntervalMs}ms, maxIdleTime: ${this.cleanupConfig.maxIdleTimeMs}ms)`);
|
|
196
220
|
}
|
|
197
|
-
/**
|
|
198
|
-
* 手动触发清理(用于测试)
|
|
199
|
-
*/
|
|
200
|
-
manualCleanup() {
|
|
201
|
-
return this.cleanupIdle();
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* 设置清理配置
|
|
205
|
-
*/
|
|
206
|
-
setCleanupConfig(config) {
|
|
207
|
-
Object.assign(this.cleanupConfig, config);
|
|
208
|
-
// 重启清理任务
|
|
209
|
-
if (this.cleanupTimer) {
|
|
210
|
-
clearInterval(this.cleanupTimer);
|
|
211
|
-
}
|
|
212
|
-
this.startCleanupTask();
|
|
213
|
-
this.logger.log(`Circuit breaker cleanup config updated: ${JSON.stringify(this.cleanupConfig)}`);
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* 获取清理配置
|
|
217
|
-
*/
|
|
218
|
-
getCleanupConfig() {
|
|
219
|
-
return Object.assign({}, this.cleanupConfig);
|
|
220
|
-
}
|
|
221
221
|
};
|
|
222
222
|
exports.HttpCircuitBreakerService = HttpCircuitBreakerService;
|
|
223
223
|
exports.HttpCircuitBreakerService = HttpCircuitBreakerService = HttpCircuitBreakerService_1 = __decorate([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
2
2
|
import { HttpClientConfig } from '../interfaces/http-client-config.interface';
|
|
3
|
-
import {
|
|
3
|
+
import { ApiKeyConfig, AuthType, BasicAuthConfig, BearerTokenConfig, CustomAuthConfig, OAuth2Config } from '../interfaces/api-client-config.interface';
|
|
4
4
|
import { HttpCircuitBreakerService } from './circuit-breaker.service';
|
|
5
5
|
import { HttpLoggingService } from './logging.service';
|
|
6
6
|
import { RedisLockService } from '../../redis-lock/redis-lock.service';
|
|
@@ -11,6 +11,15 @@ interface ExtendedAxiosRequestConfig extends AxiosRequestConfig {
|
|
|
11
11
|
[key: string]: any;
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* 请求返回选项
|
|
16
|
+
*/
|
|
17
|
+
interface RequestReturnOptions {
|
|
18
|
+
/** 返回类型: 'data'(默认) | 'full' | 'custom' */
|
|
19
|
+
returnType?: 'data' | 'full' | 'custom';
|
|
20
|
+
/** 自定义返回转换函数 */
|
|
21
|
+
transform?: (response: AxiosResponse) => any;
|
|
22
|
+
}
|
|
14
23
|
/**
|
|
15
24
|
* 认证配置接口
|
|
16
25
|
*/
|
|
@@ -55,8 +64,16 @@ export declare class HttpClientService {
|
|
|
55
64
|
}): HttpClientService;
|
|
56
65
|
/**
|
|
57
66
|
* 执行HTTP请求
|
|
67
|
+
* @param config 请求配置
|
|
68
|
+
* @param decoratorContext 装饰器上下文
|
|
69
|
+
* @param clientName 客户端名称
|
|
70
|
+
* @param returnOptions 返回选项
|
|
71
|
+
* - returnType: 'data'(默认)只返回response.data
|
|
72
|
+
* - returnType: 'full'返回完整AxiosResponse对象
|
|
73
|
+
* - returnType: 'custom'使用自定义转换函数
|
|
74
|
+
* - transform: 自定义转换函数,接收完整response返回任意数据
|
|
58
75
|
*/
|
|
59
|
-
request<T = any>(config: ExtendedAxiosRequestConfig, decoratorContext?: any, clientName?: string): Promise<T>;
|
|
76
|
+
request<T = any>(config: ExtendedAxiosRequestConfig, decoratorContext?: any, clientName?: string, returnOptions?: RequestReturnOptions): Promise<T>;
|
|
60
77
|
/**
|
|
61
78
|
* 生成curl命令(动态生成,不存储)
|
|
62
79
|
*/
|
|
@@ -77,16 +94,11 @@ export declare class HttpClientService {
|
|
|
77
94
|
* 获取客户端名称
|
|
78
95
|
*/
|
|
79
96
|
getName(): string | undefined;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
get<T = any>(url: string, config?: AxiosRequestConfig, clientName?: string): Promise<T>;
|
|
86
|
-
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig, clientName?: string): Promise<T>;
|
|
87
|
-
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig, clientName?: string): Promise<T>;
|
|
88
|
-
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig, clientName?: string): Promise<T>;
|
|
89
|
-
delete<T = any>(url: string, config?: AxiosRequestConfig, clientName?: string): Promise<T>;
|
|
97
|
+
get<T = any>(url: string, config?: AxiosRequestConfig, clientName?: string, returnOptions?: RequestReturnOptions): Promise<T>;
|
|
98
|
+
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig, clientName?: string, returnOptions?: RequestReturnOptions): Promise<T>;
|
|
99
|
+
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig, clientName?: string, returnOptions?: RequestReturnOptions): Promise<T>;
|
|
100
|
+
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig, clientName?: string, returnOptions?: RequestReturnOptions): Promise<T>;
|
|
101
|
+
delete<T = any>(url: string, config?: AxiosRequestConfig, clientName?: string, returnOptions?: RequestReturnOptions): Promise<T>;
|
|
90
102
|
/**
|
|
91
103
|
* 带等待锁的认证请求方法
|
|
92
104
|
* 用于需要确保只有一个进程执行认证相关操作的场景
|
|
@@ -101,7 +113,7 @@ export declare class HttpClientService {
|
|
|
101
113
|
lockTimeout?: number;
|
|
102
114
|
waitTimeout?: number;
|
|
103
115
|
enableRetry?: boolean;
|
|
104
|
-
}, clientName?: string): Promise<T>;
|
|
116
|
+
}, clientName?: string, returnOptions?: RequestReturnOptions): Promise<T>;
|
|
105
117
|
/**
|
|
106
118
|
* 带等待锁的批量认证请求
|
|
107
119
|
* 用于需要批量执行认证相关操作但避免重复认证的场景
|
|
@@ -120,6 +132,11 @@ export declare class HttpClientService {
|
|
|
120
132
|
waitTimeout?: number;
|
|
121
133
|
maxConcurrency?: number;
|
|
122
134
|
}, clientName?: string): Promise<Array<T | null>>;
|
|
135
|
+
/**
|
|
136
|
+
* Capture calling context information
|
|
137
|
+
* @returns Calling context with service class and method name
|
|
138
|
+
*/
|
|
139
|
+
private captureCallingContext;
|
|
123
140
|
/**
|
|
124
141
|
* 创建Axios实例并配置axios-retry
|
|
125
142
|
*/
|
|
@@ -92,8 +92,16 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
94
|
* 执行HTTP请求
|
|
95
|
+
* @param config 请求配置
|
|
96
|
+
* @param decoratorContext 装饰器上下文
|
|
97
|
+
* @param clientName 客户端名称
|
|
98
|
+
* @param returnOptions 返回选项
|
|
99
|
+
* - returnType: 'data'(默认)只返回response.data
|
|
100
|
+
* - returnType: 'full'返回完整AxiosResponse对象
|
|
101
|
+
* - returnType: 'custom'使用自定义转换函数
|
|
102
|
+
* - transform: 自定义转换函数,接收完整response返回任意数据
|
|
95
103
|
*/
|
|
96
|
-
request(config, decoratorContext, clientName) {
|
|
104
|
+
request(config, decoratorContext, clientName, returnOptions) {
|
|
97
105
|
return __awaiter(this, void 0, void 0, function* () {
|
|
98
106
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
99
107
|
// Use the instance's clientName as fallback if not provided
|
|
@@ -107,7 +115,10 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
107
115
|
const baseURL = config.baseURL || this.defaultConfig.baseURL || '';
|
|
108
116
|
// 构建完整的URL用于验证
|
|
109
117
|
let fullURL = requestURL;
|
|
110
|
-
if (requestURL &&
|
|
118
|
+
if (requestURL &&
|
|
119
|
+
!requestURL.startsWith('http://') &&
|
|
120
|
+
!requestURL.startsWith('https://') &&
|
|
121
|
+
baseURL) {
|
|
111
122
|
// 处理相对URL
|
|
112
123
|
try {
|
|
113
124
|
fullURL = new URL(requestURL, baseURL).href;
|
|
@@ -118,7 +129,8 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
118
129
|
}
|
|
119
130
|
}
|
|
120
131
|
// 执行URL安全验证(只对完整URL进行验证)
|
|
121
|
-
if (fullURL &&
|
|
132
|
+
if (fullURL &&
|
|
133
|
+
(fullURL.startsWith('http://') || fullURL.startsWith('https://'))) {
|
|
122
134
|
const sanitizeResult = security_validator_util_1.SecurityValidator.sanitizeURL(fullURL, {
|
|
123
135
|
urlConfig: (_a = this.defaultConfig.security) === null || _a === void 0 ? void 0 : _a.urlValidation,
|
|
124
136
|
ssrfConfig: (_b = this.defaultConfig.security) === null || _b === void 0 ? void 0 : _b.ssrfProtection,
|
|
@@ -143,7 +155,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
143
155
|
let circuitBreakerState;
|
|
144
156
|
try {
|
|
145
157
|
// 应用认证配置
|
|
146
|
-
|
|
158
|
+
const authConfig = yield this.applyAuthToConfig(config);
|
|
147
159
|
// 应用装饰器配置
|
|
148
160
|
const enhancedConfig = this.applyDecoratorConfig(authConfig, decoratorContext);
|
|
149
161
|
// 将 retryRecorder 存储到 config metadata 中,供 onRetry 回调使用
|
|
@@ -191,7 +203,24 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
191
203
|
}
|
|
192
204
|
// 更新统计信息
|
|
193
205
|
this.updateRequestStats(true, Date.now() - startTime, ((_f = response.config) === null || _f === void 0 ? void 0 : _f.method) || 'GET', response.status);
|
|
194
|
-
|
|
206
|
+
// 根据返回选项处理响应
|
|
207
|
+
const options = returnOptions || { returnType: 'data' };
|
|
208
|
+
switch (options.returnType) {
|
|
209
|
+
case 'full':
|
|
210
|
+
// 返回完整响应对象
|
|
211
|
+
return response;
|
|
212
|
+
case 'custom':
|
|
213
|
+
// 使用自定义转换函数
|
|
214
|
+
if (options.transform) {
|
|
215
|
+
return options.transform(response);
|
|
216
|
+
}
|
|
217
|
+
// 如果没有提供转换函数,回退到返回data
|
|
218
|
+
return response.data;
|
|
219
|
+
case 'data':
|
|
220
|
+
default:
|
|
221
|
+
// 默认只返回data(保持向后兼容)
|
|
222
|
+
return response.data;
|
|
223
|
+
}
|
|
195
224
|
}
|
|
196
225
|
catch (error) {
|
|
197
226
|
// 错误日志
|
|
@@ -265,58 +294,30 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
265
294
|
getName() {
|
|
266
295
|
return this.clientName;
|
|
267
296
|
}
|
|
268
|
-
/**
|
|
269
|
-
* Capture calling context information
|
|
270
|
-
* @returns Calling context with service class and method name
|
|
271
|
-
*/
|
|
272
|
-
captureCallingContext() {
|
|
273
|
-
try {
|
|
274
|
-
// Use CallStackExtractor to get the calling context
|
|
275
|
-
const stackInfo = call_stack_extractor_util_1.CallStackExtractor.getCallInfo();
|
|
276
|
-
// Skip internal HTTP client calls
|
|
277
|
-
if (stackInfo.serviceClass && stackInfo.methodName) {
|
|
278
|
-
// Check if it's an internal call
|
|
279
|
-
const isInternal = call_stack_extractor_util_1.CallStackExtractor['isInternalCall'](stackInfo.serviceClass);
|
|
280
|
-
if (!isInternal) {
|
|
281
|
-
return {
|
|
282
|
-
serviceClass: stackInfo.serviceClass,
|
|
283
|
-
methodName: stackInfo.methodName,
|
|
284
|
-
operationName: stackInfo.operationName,
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
// If no useful context found, return empty
|
|
289
|
-
return {};
|
|
290
|
-
}
|
|
291
|
-
catch (error) {
|
|
292
|
-
this.logger.warn('Failed to capture calling context:', error.message);
|
|
293
|
-
return {};
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
297
|
// 便捷方法
|
|
297
|
-
get(url, config, clientName) {
|
|
298
|
+
get(url, config, clientName, returnOptions) {
|
|
298
299
|
return __awaiter(this, void 0, void 0, function* () {
|
|
299
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'GET', url }), undefined, clientName);
|
|
300
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'GET', url }), undefined, clientName, returnOptions);
|
|
300
301
|
});
|
|
301
302
|
}
|
|
302
|
-
post(url, data, config, clientName) {
|
|
303
|
+
post(url, data, config, clientName, returnOptions) {
|
|
303
304
|
return __awaiter(this, void 0, void 0, function* () {
|
|
304
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'POST', url, data }), undefined, clientName);
|
|
305
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'POST', url, data }), undefined, clientName, returnOptions);
|
|
305
306
|
});
|
|
306
307
|
}
|
|
307
|
-
put(url, data, config, clientName) {
|
|
308
|
+
put(url, data, config, clientName, returnOptions) {
|
|
308
309
|
return __awaiter(this, void 0, void 0, function* () {
|
|
309
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'PUT', url, data }), undefined, clientName);
|
|
310
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'PUT', url, data }), undefined, clientName, returnOptions);
|
|
310
311
|
});
|
|
311
312
|
}
|
|
312
|
-
patch(url, data, config, clientName) {
|
|
313
|
+
patch(url, data, config, clientName, returnOptions) {
|
|
313
314
|
return __awaiter(this, void 0, void 0, function* () {
|
|
314
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'PATCH', url, data }), undefined, clientName);
|
|
315
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'PATCH', url, data }), undefined, clientName, returnOptions);
|
|
315
316
|
});
|
|
316
317
|
}
|
|
317
|
-
delete(url, config, clientName) {
|
|
318
|
+
delete(url, config, clientName, returnOptions) {
|
|
318
319
|
return __awaiter(this, void 0, void 0, function* () {
|
|
319
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'DELETE', url }), undefined, clientName);
|
|
320
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'DELETE', url }), undefined, clientName, returnOptions);
|
|
320
321
|
});
|
|
321
322
|
}
|
|
322
323
|
/**
|
|
@@ -328,12 +329,12 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
328
329
|
* @param options 等待锁选项
|
|
329
330
|
* @returns 响应数据
|
|
330
331
|
*/
|
|
331
|
-
authRequest(config, tokenProvider, options, clientName) {
|
|
332
|
+
authRequest(config, tokenProvider, options, clientName, returnOptions) {
|
|
332
333
|
return __awaiter(this, void 0, void 0, function* () {
|
|
333
334
|
const effectiveClientName = clientName || this.clientName;
|
|
334
335
|
if (!this.redisLockService) {
|
|
335
336
|
this.logger.warn('RedisLockService not available, proceeding without lock');
|
|
336
|
-
return this.request(config, undefined, effectiveClientName);
|
|
337
|
+
return this.request(config, undefined, effectiveClientName, returnOptions);
|
|
337
338
|
}
|
|
338
339
|
const { lockKey = `auth-request:${(0, request_id_util_1.generateRequestId)()}`, lockTimeout = 30000, waitTimeout = 60000, enableRetry = true, } = options || {};
|
|
339
340
|
try {
|
|
@@ -344,7 +345,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
344
345
|
// 添加认证头
|
|
345
346
|
const authConfig = Object.assign(Object.assign({}, config), { headers: Object.assign(Object.assign({}, config.headers), { Authorization: `Bearer ${token}` }) });
|
|
346
347
|
// 执行请求
|
|
347
|
-
return this.request(authConfig, undefined, effectiveClientName);
|
|
348
|
+
return this.request(authConfig, undefined, effectiveClientName, returnOptions);
|
|
348
349
|
}), {
|
|
349
350
|
ttl: lockTimeout,
|
|
350
351
|
retryCount: enableRetry ? 3 : 0,
|
|
@@ -466,6 +467,34 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
466
467
|
}
|
|
467
468
|
});
|
|
468
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* Capture calling context information
|
|
472
|
+
* @returns Calling context with service class and method name
|
|
473
|
+
*/
|
|
474
|
+
captureCallingContext() {
|
|
475
|
+
try {
|
|
476
|
+
// Use CallStackExtractor to get the calling context
|
|
477
|
+
const stackInfo = call_stack_extractor_util_1.CallStackExtractor.getCallInfo();
|
|
478
|
+
// Skip internal HTTP client calls
|
|
479
|
+
if (stackInfo.serviceClass && stackInfo.methodName) {
|
|
480
|
+
// Check if it's an internal call
|
|
481
|
+
const isInternal = call_stack_extractor_util_1.CallStackExtractor['isInternalCall'](stackInfo.serviceClass);
|
|
482
|
+
if (!isInternal) {
|
|
483
|
+
return {
|
|
484
|
+
serviceClass: stackInfo.serviceClass,
|
|
485
|
+
methodName: stackInfo.methodName,
|
|
486
|
+
operationName: stackInfo.operationName,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// If no useful context found, return empty
|
|
491
|
+
return {};
|
|
492
|
+
}
|
|
493
|
+
catch (error) {
|
|
494
|
+
this.logger.warn('Failed to capture calling context:', error.message);
|
|
495
|
+
return {};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
469
498
|
/**
|
|
470
499
|
* 创建Axios实例并配置axios-retry
|
|
471
500
|
*/
|
|
@@ -77,6 +77,22 @@ export declare class HttpLoggingService implements OnModuleDestroy {
|
|
|
77
77
|
endDate?: Date;
|
|
78
78
|
userId?: string;
|
|
79
79
|
}): Promise<any>;
|
|
80
|
+
/**
|
|
81
|
+
* 获取日志队列统计信息
|
|
82
|
+
*/
|
|
83
|
+
getQueueStats(): {
|
|
84
|
+
queueSize: number;
|
|
85
|
+
isProcessing: boolean;
|
|
86
|
+
config: AsyncLogConfig;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* 手动刷新日志队列
|
|
90
|
+
*/
|
|
91
|
+
manualFlush(): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* 设置异步日志配置
|
|
94
|
+
*/
|
|
95
|
+
setAsyncConfig(config: Partial<AsyncLogConfig>): void;
|
|
80
96
|
/**
|
|
81
97
|
* 提取调用信息
|
|
82
98
|
*/
|
|
@@ -103,22 +119,6 @@ export declare class HttpLoggingService implements OnModuleDestroy {
|
|
|
103
119
|
* 同步保存日志到数据库(用于异步模式内部)
|
|
104
120
|
*/
|
|
105
121
|
private saveLogToDatabase;
|
|
106
|
-
/**
|
|
107
|
-
* 获取日志队列统计信息
|
|
108
|
-
*/
|
|
109
|
-
getQueueStats(): {
|
|
110
|
-
queueSize: number;
|
|
111
|
-
isProcessing: boolean;
|
|
112
|
-
config: AsyncLogConfig;
|
|
113
|
-
};
|
|
114
|
-
/**
|
|
115
|
-
* 手动刷新日志队列
|
|
116
|
-
*/
|
|
117
|
-
manualFlush(): Promise<void>;
|
|
118
|
-
/**
|
|
119
|
-
* 设置异步日志配置
|
|
120
|
-
*/
|
|
121
|
-
setAsyncConfig(config: Partial<AsyncLogConfig>): void;
|
|
122
122
|
/**
|
|
123
123
|
* 转换请求体为字符串
|
|
124
124
|
*/
|
|
@@ -61,7 +61,8 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
61
61
|
}
|
|
62
62
|
initRepository(dataSource, tableName) {
|
|
63
63
|
try {
|
|
64
|
-
this.logRepository =
|
|
64
|
+
this.logRepository =
|
|
65
|
+
(0, transaction_1.getDataSource)(dataSource).getRepository(http_log_entity_1.HttpLogEntity);
|
|
65
66
|
this.logger.log('Database logging initialized');
|
|
66
67
|
return this.logRepository;
|
|
67
68
|
}
|
|
@@ -374,6 +375,50 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
374
375
|
};
|
|
375
376
|
});
|
|
376
377
|
}
|
|
378
|
+
/**
|
|
379
|
+
* 获取日志队列统计信息
|
|
380
|
+
*/
|
|
381
|
+
getQueueStats() {
|
|
382
|
+
return {
|
|
383
|
+
queueSize: this.logQueue.length,
|
|
384
|
+
isProcessing: this.isProcessing,
|
|
385
|
+
config: Object.assign({}, this.asyncConfig),
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* 手动刷新日志队列
|
|
390
|
+
*/
|
|
391
|
+
manualFlush() {
|
|
392
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
393
|
+
return new Promise((resolve) => {
|
|
394
|
+
const checkProcessing = () => {
|
|
395
|
+
if (!this.isProcessing) {
|
|
396
|
+
this.flush();
|
|
397
|
+
// 等待刷新完成
|
|
398
|
+
setTimeout(() => resolve(), 100);
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
setTimeout(checkProcessing, 50);
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
checkProcessing();
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* 设置异步日志配置
|
|
410
|
+
*/
|
|
411
|
+
setAsyncConfig(config) {
|
|
412
|
+
Object.assign(this.asyncConfig, config);
|
|
413
|
+
// 重启刷新任务
|
|
414
|
+
if (this.flushTimer) {
|
|
415
|
+
clearInterval(this.flushTimer);
|
|
416
|
+
}
|
|
417
|
+
if (this.asyncConfig.enabled) {
|
|
418
|
+
this.startFlushTask();
|
|
419
|
+
}
|
|
420
|
+
this.logger.log(`Async log config updated: ${JSON.stringify(this.asyncConfig)}`);
|
|
421
|
+
}
|
|
377
422
|
/**
|
|
378
423
|
* 提取调用信息
|
|
379
424
|
*/
|
|
@@ -536,50 +581,6 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
536
581
|
}
|
|
537
582
|
});
|
|
538
583
|
}
|
|
539
|
-
/**
|
|
540
|
-
* 获取日志队列统计信息
|
|
541
|
-
*/
|
|
542
|
-
getQueueStats() {
|
|
543
|
-
return {
|
|
544
|
-
queueSize: this.logQueue.length,
|
|
545
|
-
isProcessing: this.isProcessing,
|
|
546
|
-
config: Object.assign({}, this.asyncConfig),
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* 手动刷新日志队列
|
|
551
|
-
*/
|
|
552
|
-
manualFlush() {
|
|
553
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
554
|
-
return new Promise((resolve) => {
|
|
555
|
-
const checkProcessing = () => {
|
|
556
|
-
if (!this.isProcessing) {
|
|
557
|
-
this.flush();
|
|
558
|
-
// 等待刷新完成
|
|
559
|
-
setTimeout(() => resolve(), 100);
|
|
560
|
-
}
|
|
561
|
-
else {
|
|
562
|
-
setTimeout(checkProcessing, 50);
|
|
563
|
-
}
|
|
564
|
-
};
|
|
565
|
-
checkProcessing();
|
|
566
|
-
});
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
/**
|
|
570
|
-
* 设置异步日志配置
|
|
571
|
-
*/
|
|
572
|
-
setAsyncConfig(config) {
|
|
573
|
-
Object.assign(this.asyncConfig, config);
|
|
574
|
-
// 重启刷新任务
|
|
575
|
-
if (this.flushTimer) {
|
|
576
|
-
clearInterval(this.flushTimer);
|
|
577
|
-
}
|
|
578
|
-
if (this.asyncConfig.enabled) {
|
|
579
|
-
this.startFlushTask();
|
|
580
|
-
}
|
|
581
|
-
this.logger.log(`Async log config updated: ${JSON.stringify(this.asyncConfig)}`);
|
|
582
|
-
}
|
|
583
584
|
/**
|
|
584
585
|
* 转换请求体为字符串
|
|
585
586
|
*/
|
|
@@ -12,6 +12,18 @@ export declare class ProxyEnvironmentParser {
|
|
|
12
12
|
* @returns 解析后的代理配置,如果不应使用代理则返回 false
|
|
13
13
|
*/
|
|
14
14
|
static parseFromEnvironment(protocol?: 'http' | 'https', targetUrl?: string): false | Required<Omit<ProxyConfig, 'enabled' | 'fromEnvironment'>>;
|
|
15
|
+
/**
|
|
16
|
+
* 获取所有代理相关的环境变量
|
|
17
|
+
*/
|
|
18
|
+
static getProxyEnvironmentVariables(): {
|
|
19
|
+
HTTP_PROXY?: string;
|
|
20
|
+
HTTPS_PROXY?: string;
|
|
21
|
+
NO_PROXY?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 检查是否设置了代理环境变量
|
|
25
|
+
*/
|
|
26
|
+
static hasProxyEnvironment(): boolean;
|
|
15
27
|
/**
|
|
16
28
|
* 解析代理 URL
|
|
17
29
|
* 支持格式: http://proxy:port, http://user:pass@proxy:port
|
|
@@ -27,16 +39,4 @@ export declare class ProxyEnvironmentParser {
|
|
|
27
39
|
* 支持精确匹配和 CIDR 表示法
|
|
28
40
|
*/
|
|
29
41
|
private static isIpMatch;
|
|
30
|
-
/**
|
|
31
|
-
* 获取所有代理相关的环境变量
|
|
32
|
-
*/
|
|
33
|
-
static getProxyEnvironmentVariables(): {
|
|
34
|
-
HTTP_PROXY?: string;
|
|
35
|
-
HTTPS_PROXY?: string;
|
|
36
|
-
NO_PROXY?: string;
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* 检查是否设置了代理环境变量
|
|
40
|
-
*/
|
|
41
|
-
static hasProxyEnvironment(): boolean;
|
|
42
42
|
}
|
|
@@ -40,6 +40,23 @@ class ProxyEnvironmentParser {
|
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* 获取所有代理相关的环境变量
|
|
45
|
+
*/
|
|
46
|
+
static getProxyEnvironmentVariables() {
|
|
47
|
+
return {
|
|
48
|
+
HTTP_PROXY: process.env.HTTP_PROXY || process.env.http_proxy,
|
|
49
|
+
HTTPS_PROXY: process.env.HTTPS_PROXY || process.env.https_proxy,
|
|
50
|
+
NO_PROXY: process.env.NO_PROXY || process.env.no_proxy,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 检查是否设置了代理环境变量
|
|
55
|
+
*/
|
|
56
|
+
static hasProxyEnvironment() {
|
|
57
|
+
const vars = this.getProxyEnvironmentVariables();
|
|
58
|
+
return !!(vars.HTTP_PROXY || vars.HTTPS_PROXY);
|
|
59
|
+
}
|
|
43
60
|
/**
|
|
44
61
|
* 解析代理 URL
|
|
45
62
|
* 支持格式: http://proxy:port, http://user:pass@proxy:port
|
|
@@ -50,7 +67,11 @@ class ProxyEnvironmentParser {
|
|
|
50
67
|
const config = {
|
|
51
68
|
protocol: url.protocol.replace(':', '') || 'http',
|
|
52
69
|
host: url.hostname,
|
|
53
|
-
port: url.port
|
|
70
|
+
port: url.port
|
|
71
|
+
? parseInt(url.port, 10)
|
|
72
|
+
: url.protocol === 'https:'
|
|
73
|
+
? 443
|
|
74
|
+
: 80,
|
|
54
75
|
auth: undefined,
|
|
55
76
|
};
|
|
56
77
|
// 解析认证信息
|
|
@@ -79,7 +100,9 @@ class ProxyEnvironmentParser {
|
|
|
79
100
|
const url = new URL(targetUrl);
|
|
80
101
|
const hostname = url.hostname;
|
|
81
102
|
// NO_PROXY 可以是逗号分隔的列表
|
|
82
|
-
const bypassList = noProxy
|
|
103
|
+
const bypassList = noProxy
|
|
104
|
+
.split(',')
|
|
105
|
+
.map((item) => item.trim().toLowerCase());
|
|
83
106
|
for (const bypass of bypassList) {
|
|
84
107
|
if (!bypass)
|
|
85
108
|
continue;
|
|
@@ -126,23 +149,6 @@ class ProxyEnvironmentParser {
|
|
|
126
149
|
}
|
|
127
150
|
return false;
|
|
128
151
|
}
|
|
129
|
-
/**
|
|
130
|
-
* 获取所有代理相关的环境变量
|
|
131
|
-
*/
|
|
132
|
-
static getProxyEnvironmentVariables() {
|
|
133
|
-
return {
|
|
134
|
-
HTTP_PROXY: process.env.HTTP_PROXY || process.env.http_proxy,
|
|
135
|
-
HTTPS_PROXY: process.env.HTTPS_PROXY || process.env.https_proxy,
|
|
136
|
-
NO_PROXY: process.env.NO_PROXY || process.env.no_proxy,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* 检查是否设置了代理环境变量
|
|
141
|
-
*/
|
|
142
|
-
static hasProxyEnvironment() {
|
|
143
|
-
const vars = this.getProxyEnvironmentVariables();
|
|
144
|
-
return !!(vars.HTTP_PROXY || vars.HTTPS_PROXY);
|
|
145
|
-
}
|
|
146
152
|
}
|
|
147
153
|
exports.ProxyEnvironmentParser = ProxyEnvironmentParser;
|
|
148
154
|
ProxyEnvironmentParser.logger = new common_1.Logger(ProxyEnvironmentParser.name);
|