@nest-omni/core 4.1.3-25 → 4.1.3-27

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 (48) hide show
  1. package/cache/cache.service.d.ts +3 -1
  2. package/cache/cache.service.js +8 -8
  3. package/cache/decorators/cache-put.decorator.js +5 -4
  4. package/cache/dependencies/callback.dependency.js +9 -0
  5. package/cache/dependencies/tag.dependency.d.ts +1 -9
  6. package/cache/dependencies/tag.dependency.js +5 -14
  7. package/cache/providers/lrucache.provider.d.ts +1 -0
  8. package/cache/providers/lrucache.provider.js +6 -4
  9. package/cache/providers/redis-cache.provider.d.ts +1 -0
  10. package/cache/providers/redis-cache.provider.js +8 -6
  11. package/http-client/config/http-client.config.js +4 -2
  12. package/http-client/decorators/http-client.decorators.d.ts +1 -1
  13. package/http-client/decorators/http-client.decorators.js +1 -1
  14. package/http-client/examples/advanced-usage.example.js +3 -3
  15. package/http-client/examples/axios-config-extended.example.js +1 -3
  16. package/http-client/examples/flexible-response-example.d.ts +28 -0
  17. package/http-client/examples/flexible-response-example.js +120 -0
  18. package/http-client/examples/ssl-certificate.example.d.ts +2 -2
  19. package/http-client/examples/ssl-certificate.example.js +18 -17
  20. package/http-client/http-client.module.js +2 -2
  21. package/http-client/interfaces/http-client-config.interface.d.ts +6 -1
  22. package/http-client/services/api-client-registry.service.d.ts +6 -6
  23. package/http-client/services/api-client-registry.service.js +9 -9
  24. package/http-client/services/circuit-breaker.service.d.ts +9 -9
  25. package/http-client/services/circuit-breaker.service.js +24 -24
  26. package/http-client/services/http-client.service.d.ts +30 -13
  27. package/http-client/services/http-client.service.js +76 -47
  28. package/http-client/services/logging.service.d.ts +17 -33
  29. package/http-client/services/logging.service.js +81 -169
  30. package/http-client/utils/curl-generator.util.js +2 -5
  31. package/http-client/utils/index.d.ts +1 -0
  32. package/http-client/utils/index.js +1 -0
  33. package/http-client/utils/proxy-environment.util.d.ts +12 -12
  34. package/http-client/utils/proxy-environment.util.js +25 -19
  35. package/http-client/utils/retry-recorder.util.d.ts +0 -4
  36. package/http-client/utils/retry-recorder.util.js +2 -27
  37. package/http-client/utils/sanitize.util.d.ts +58 -0
  38. package/http-client/utils/sanitize.util.js +188 -0
  39. package/http-client/utils/security-validator.util.d.ts +19 -19
  40. package/http-client/utils/security-validator.util.js +66 -64
  41. package/interceptors/http-logging-interceptor.service.d.ts +38 -0
  42. package/interceptors/http-logging-interceptor.service.js +167 -0
  43. package/interceptors/index.d.ts +1 -0
  44. package/interceptors/index.js +1 -0
  45. package/package.json +1 -1
  46. package/setup/bootstrap.setup.js +1 -1
  47. package/shared/services/api-config.service.js +3 -18
  48. package/vault/vault-config.service.js +1 -1
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SanitizeUtil = void 0;
4
+ /**
5
+ * HTTP 请求数据脱敏工具
6
+ * 统一处理 headers、body、query string 的敏感信息过滤
7
+ */
8
+ class SanitizeUtil {
9
+ /**
10
+ * 脱敏 headers
11
+ * @param headers - 原始 headers 对象
12
+ * @param sensitiveFields - 自定义敏感字段列表
13
+ * @returns 脱敏后的 headers
14
+ */
15
+ static sanitizeHeaders(headers, sensitiveFields = []) {
16
+ if (!headers)
17
+ return {};
18
+ const sanitized = {};
19
+ const fieldsToSanitize = [
20
+ ...this.DEFAULT_SENSITIVE_FIELDS,
21
+ ...sensitiveFields,
22
+ ];
23
+ Object.keys(headers).forEach((key) => {
24
+ if (this.isSensitiveField(key, fieldsToSanitize)) {
25
+ sanitized[key] = '[FILTERED]';
26
+ }
27
+ else {
28
+ sanitized[key] = String(headers[key]);
29
+ }
30
+ });
31
+ return sanitized;
32
+ }
33
+ /**
34
+ * 脱敏 body
35
+ * @param body - 原始 body 数据
36
+ * @param sensitiveFields - 自定义敏感字段列表
37
+ * @returns 脱敏后的 body
38
+ */
39
+ static sanitizeBody(body, sensitiveFields = []) {
40
+ if (!body)
41
+ return undefined;
42
+ if (typeof body === 'string') {
43
+ try {
44
+ body = JSON.parse(body);
45
+ }
46
+ catch (_a) {
47
+ // 非 JSON 字符串,不做处理
48
+ return body.length > 1000 ? body.substring(0, 1000) + '...' : body;
49
+ }
50
+ }
51
+ if (typeof body === 'object') {
52
+ const sanitized = Object.assign({}, body);
53
+ const fieldsToSanitize = [
54
+ ...this.DEFAULT_SENSITIVE_FIELDS,
55
+ ...sensitiveFields,
56
+ ];
57
+ this.sanitizeObject(sanitized, fieldsToSanitize);
58
+ return sanitized;
59
+ }
60
+ return body;
61
+ }
62
+ /**
63
+ * 脱敏 URL 中的 query string
64
+ * @param url - 原始 URL
65
+ * @param sensitiveFields - 自定义敏感字段列表
66
+ * @returns 脱敏后的 URL
67
+ */
68
+ static sanitizeQueryString(url, sensitiveFields = []) {
69
+ if (!url)
70
+ return url;
71
+ const queryIndex = url.indexOf('?');
72
+ if (queryIndex === -1) {
73
+ return url; // 没有 query string
74
+ }
75
+ const baseUrl = url.substring(0, queryIndex);
76
+ const queryString = url.substring(queryIndex + 1);
77
+ const params = new URLSearchParams(queryString);
78
+ const fieldsToSanitize = [
79
+ ...this.DEFAULT_SENSITIVE_FIELDS,
80
+ ...sensitiveFields,
81
+ ];
82
+ params.forEach((value, key) => {
83
+ if (this.isSensitiveField(key, fieldsToSanitize)) {
84
+ params.set(key, '[FILTERED]');
85
+ }
86
+ });
87
+ const sanitizedQuery = params.toString();
88
+ return sanitizedQuery ? `${baseUrl}?${sanitizedQuery}` : baseUrl;
89
+ }
90
+ /**
91
+ * 脱敏 params 对象
92
+ * @param params - 原始 params 对象
93
+ * @param sensitiveFields - 自定义敏感字段列表
94
+ * @returns 脱敏后的 params
95
+ */
96
+ static sanitizeParams(params, sensitiveFields = []) {
97
+ if (!params)
98
+ return {};
99
+ const sanitized = {};
100
+ const fieldsToSanitize = [
101
+ ...this.DEFAULT_SENSITIVE_FIELDS,
102
+ ...sensitiveFields,
103
+ ];
104
+ Object.keys(params).forEach((key) => {
105
+ if (this.isSensitiveField(key, fieldsToSanitize)) {
106
+ sanitized[key] = '[FILTERED]';
107
+ }
108
+ else {
109
+ sanitized[key] = params[key];
110
+ }
111
+ });
112
+ return sanitized;
113
+ }
114
+ /**
115
+ * 将 body 转换为字符串并脱敏
116
+ * @param data - 原始数据
117
+ * @param sensitiveFields - 自定义敏感字段列表
118
+ * @returns 脱敏后的字符串
119
+ */
120
+ static sanitizeBodyAsString(data, sensitiveFields = []) {
121
+ const sanitized = this.sanitizeBody(data, sensitiveFields);
122
+ if (!sanitized)
123
+ return undefined;
124
+ if (typeof sanitized === 'string') {
125
+ return sanitized.length > 5000
126
+ ? sanitized.substring(0, 5000) + '...'
127
+ : sanitized;
128
+ }
129
+ const jsonString = JSON.stringify(sanitized);
130
+ return jsonString.length > 5000
131
+ ? jsonString.substring(0, 5000) + '...'
132
+ : jsonString;
133
+ }
134
+ /**
135
+ * 判断字段是否为敏感字段
136
+ * @param key - 字段名
137
+ * @param sensitiveFields - 敏感字段列表
138
+ * @returns 是否为敏感字段
139
+ */
140
+ static isSensitiveField(key, sensitiveFields) {
141
+ const lowerKey = key.toLowerCase();
142
+ return sensitiveFields.some((field) => lowerKey.includes(field.toLowerCase()));
143
+ }
144
+ /**
145
+ * 递归脱敏对象中的敏感字段
146
+ * @param obj - 目标对象
147
+ * @param sensitiveFields - 敏感字段列表
148
+ */
149
+ static sanitizeObject(obj, sensitiveFields) {
150
+ if (typeof obj !== 'object' || obj === null)
151
+ return;
152
+ for (const key in obj) {
153
+ if (this.isSensitiveField(key, sensitiveFields)) {
154
+ obj[key] = '[FILTERED]';
155
+ }
156
+ else if (typeof obj[key] === 'object') {
157
+ this.sanitizeObject(obj[key], sensitiveFields);
158
+ }
159
+ }
160
+ }
161
+ }
162
+ exports.SanitizeUtil = SanitizeUtil;
163
+ /**
164
+ * 默认敏感字段列表
165
+ */
166
+ SanitizeUtil.DEFAULT_SENSITIVE_FIELDS = [
167
+ // Headers 相关
168
+ 'authorization',
169
+ 'apikey',
170
+ 'x-api-key',
171
+ 'x-auth-token',
172
+ 'cookie',
173
+ 'set-cookie',
174
+ // Body/Query 通用字段
175
+ 'password',
176
+ 'secret',
177
+ 'token',
178
+ 'key',
179
+ 'credential',
180
+ 'private',
181
+ 'confidential',
182
+ 'ssn',
183
+ 'creditCard',
184
+ 'accessToken',
185
+ 'refreshToken',
186
+ 'apiKey',
187
+ 'apiSecret',
188
+ ];
@@ -75,6 +75,25 @@ export declare class SecurityValidator {
75
75
  valid: boolean;
76
76
  error?: string;
77
77
  };
78
+ /**
79
+ * 敏感数据检测
80
+ * 检查数据中是否包含敏感信息(如密码、token等)
81
+ * @param data 要检查的数据对象
82
+ * @param additionalPatterns 额外的敏感数据模式
83
+ * @returns 检测结果
84
+ */
85
+ static detectSensitiveData(data: any, additionalPatterns?: RegExp[]): {
86
+ hasSensitiveData: boolean;
87
+ fields: string[];
88
+ };
89
+ /**
90
+ * 获取默认SSRF防护配置
91
+ */
92
+ static getDefaultSSRFConfig(): SSRFProtectionConfig;
93
+ /**
94
+ * 获取默认URL验证配置
95
+ */
96
+ static getDefaultURLConfig(): URLValidationConfig;
78
97
  /**
79
98
  * 从主机名中提取IP地址
80
99
  */
@@ -96,23 +115,4 @@ export declare class SecurityValidator {
96
115
  * 验证主机名格式
97
116
  */
98
117
  private static isValidHostname;
99
- /**
100
- * 敏感数据检测
101
- * 检查数据中是否包含敏感信息(如密码、token等)
102
- * @param data 要检查的数据对象
103
- * @param additionalPatterns 额外的敏感数据模式
104
- * @returns 检测结果
105
- */
106
- static detectSensitiveData(data: any, additionalPatterns?: RegExp[]): {
107
- hasSensitiveData: boolean;
108
- fields: string[];
109
- };
110
- /**
111
- * 获取默认SSRF防护配置
112
- */
113
- static getDefaultSSRFConfig(): SSRFProtectionConfig;
114
- /**
115
- * 获取默认URL验证配置
116
- */
117
- static getDefaultURLConfig(): URLValidationConfig;
118
118
  }
@@ -143,6 +143,69 @@ class SecurityValidator {
143
143
  }
144
144
  return { url: sanitizedURL, valid: true };
145
145
  }
146
+ /**
147
+ * 敏感数据检测
148
+ * 检查数据中是否包含敏感信息(如密码、token等)
149
+ * @param data 要检查的数据对象
150
+ * @param additionalPatterns 额外的敏感数据模式
151
+ * @returns 检测结果
152
+ */
153
+ static detectSensitiveData(data, additionalPatterns = []) {
154
+ const sensitiveFields = [];
155
+ // 默认敏感字段名模式
156
+ const defaultPatterns = [
157
+ /password/i,
158
+ /secret/i,
159
+ /token/i,
160
+ /api[_-]?key/i,
161
+ /authorization/i,
162
+ /credential/i,
163
+ /private[_-]?key/i,
164
+ /access[_-]?token/i,
165
+ /refresh[_-]?token/i,
166
+ /session[_-]?id/i,
167
+ /csrf/i,
168
+ /ssn/i,
169
+ /credit[_-]?card/i,
170
+ ];
171
+ const allPatterns = [...defaultPatterns, ...additionalPatterns];
172
+ const checkObject = (obj, path = '') => {
173
+ if (!obj || typeof obj !== 'object') {
174
+ return;
175
+ }
176
+ for (const key in obj) {
177
+ if (!obj.hasOwnProperty(key)) {
178
+ continue;
179
+ }
180
+ const currentPath = path ? `${path}.${key}` : key;
181
+ // 检查键名是否匹配敏感模式
182
+ if (allPatterns.some((pattern) => pattern.test(key))) {
183
+ sensitiveFields.push(currentPath);
184
+ }
185
+ // 递归检查嵌套对象
186
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
187
+ checkObject(obj[key], currentPath);
188
+ }
189
+ }
190
+ };
191
+ checkObject(data);
192
+ return {
193
+ hasSensitiveData: sensitiveFields.length > 0,
194
+ fields: sensitiveFields,
195
+ };
196
+ }
197
+ /**
198
+ * 获取默认SSRF防护配置
199
+ */
200
+ static getDefaultSSRFConfig() {
201
+ return Object.assign({}, this.defaultSSRFConfig);
202
+ }
203
+ /**
204
+ * 获取默认URL验证配置
205
+ */
206
+ static getDefaultURLConfig() {
207
+ return Object.assign({}, this.defaultURLConfig);
208
+ }
146
209
  /**
147
210
  * 从主机名中提取IP地址
148
211
  */
@@ -165,7 +228,9 @@ class SecurityValidator {
165
228
  static validateIPAddress(ipAddress, config) {
166
229
  // 检查是否为本地回环地址
167
230
  if (!config.allowLoopback) {
168
- if (ipAddress === '127.0.0.1' || ipAddress === '::1' || ipAddress.startsWith('127.')) {
231
+ if (ipAddress === '127.0.0.1' ||
232
+ ipAddress === '::1' ||
233
+ ipAddress.startsWith('127.')) {
169
234
  return {
170
235
  valid: false,
171
236
  error: 'Loopback addresses are not allowed',
@@ -251,69 +316,6 @@ class SecurityValidator {
251
316
  const hostnameRegex = /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$/;
252
317
  return hostnameRegex.test(hostname);
253
318
  }
254
- /**
255
- * 敏感数据检测
256
- * 检查数据中是否包含敏感信息(如密码、token等)
257
- * @param data 要检查的数据对象
258
- * @param additionalPatterns 额外的敏感数据模式
259
- * @returns 检测结果
260
- */
261
- static detectSensitiveData(data, additionalPatterns = []) {
262
- const sensitiveFields = [];
263
- // 默认敏感字段名模式
264
- const defaultPatterns = [
265
- /password/i,
266
- /secret/i,
267
- /token/i,
268
- /api[_-]?key/i,
269
- /authorization/i,
270
- /credential/i,
271
- /private[_-]?key/i,
272
- /access[_-]?token/i,
273
- /refresh[_-]?token/i,
274
- /session[_-]?id/i,
275
- /csrf/i,
276
- /ssn/i,
277
- /credit[_-]?card/i,
278
- ];
279
- const allPatterns = [...defaultPatterns, ...additionalPatterns];
280
- const checkObject = (obj, path = '') => {
281
- if (!obj || typeof obj !== 'object') {
282
- return;
283
- }
284
- for (const key in obj) {
285
- if (!obj.hasOwnProperty(key)) {
286
- continue;
287
- }
288
- const currentPath = path ? `${path}.${key}` : key;
289
- // 检查键名是否匹配敏感模式
290
- if (allPatterns.some((pattern) => pattern.test(key))) {
291
- sensitiveFields.push(currentPath);
292
- }
293
- // 递归检查嵌套对象
294
- if (typeof obj[key] === 'object' && obj[key] !== null) {
295
- checkObject(obj[key], currentPath);
296
- }
297
- }
298
- };
299
- checkObject(data);
300
- return {
301
- hasSensitiveData: sensitiveFields.length > 0,
302
- fields: sensitiveFields,
303
- };
304
- }
305
- /**
306
- * 获取默认SSRF防护配置
307
- */
308
- static getDefaultSSRFConfig() {
309
- return Object.assign({}, this.defaultSSRFConfig);
310
- }
311
- /**
312
- * 获取默认URL验证配置
313
- */
314
- static getDefaultURLConfig() {
315
- return Object.assign({}, this.defaultURLConfig);
316
- }
317
319
  }
318
320
  exports.SecurityValidator = SecurityValidator;
319
321
  SecurityValidator.logger = new common_1.Logger(SecurityValidator.name);
@@ -0,0 +1,38 @@
1
+ import { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
2
+ import { Observable } from 'rxjs';
3
+ import { ApiConfigService } from '../shared/services/api-config.service';
4
+ /**
5
+ * HTTP 日志拦截器
6
+ * 参考 Tomcat AccessLog 的实现,每个请求只记录一条日志
7
+ * 在请求完成时同时记录请求和响应的完整信息
8
+ */
9
+ export declare class HttpLoggingInterceptor implements NestInterceptor {
10
+ private readonly configService;
11
+ private readonly logger;
12
+ constructor(configService: ApiConfigService);
13
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
14
+ /**
15
+ * 生成请求 ID
16
+ */
17
+ private generateRequestId;
18
+ /**
19
+ * 记录请求和响应(一条日志)
20
+ */
21
+ private logRequestResponse;
22
+ /**
23
+ * 记录请求和错误(一条日志)
24
+ */
25
+ private logRequestError;
26
+ /**
27
+ * 清理敏感的 header 信息
28
+ */
29
+ private sanitizeHeaders;
30
+ /**
31
+ * 清理敏感的 body 信息
32
+ */
33
+ private sanitizeBody;
34
+ /**
35
+ * 从 headers 提取用户信息
36
+ */
37
+ private extractUser;
38
+ }
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var HttpLoggingInterceptor_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.HttpLoggingInterceptor = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const operators_1 = require("rxjs/operators");
16
+ const api_config_service_1 = require("../shared/services/api-config.service");
17
+ /**
18
+ * HTTP 日志拦截器
19
+ * 参考 Tomcat AccessLog 的实现,每个请求只记录一条日志
20
+ * 在请求完成时同时记录请求和响应的完整信息
21
+ */
22
+ let HttpLoggingInterceptor = HttpLoggingInterceptor_1 = class HttpLoggingInterceptor {
23
+ constructor(configService) {
24
+ this.configService = configService;
25
+ this.logger = new common_1.Logger(HttpLoggingInterceptor_1.name);
26
+ }
27
+ intercept(context, next) {
28
+ const ctx = context.switchToHttp();
29
+ const request = ctx.getRequest();
30
+ const response = ctx.getResponse();
31
+ const startTime = Date.now();
32
+ const { method, url, headers, body } = request;
33
+ const requestId = String(request.id || headers['x-request-id'] || this.generateRequestId());
34
+ // 监听响应完成事件,一条日志同时记录请求和响应
35
+ response.on('finish', () => {
36
+ const duration = Date.now() - startTime;
37
+ this.logRequestResponse(requestId, request, response, duration);
38
+ });
39
+ return next.handle().pipe((0, operators_1.catchError)((error) => {
40
+ const duration = Date.now() - startTime;
41
+ this.logRequestError(requestId, request, error, duration);
42
+ throw error;
43
+ }));
44
+ }
45
+ /**
46
+ * 生成请求 ID
47
+ */
48
+ generateRequestId() {
49
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
50
+ }
51
+ /**
52
+ * 记录请求和响应(一条日志)
53
+ */
54
+ logRequestResponse(requestId, request, response, duration) {
55
+ const statusCode = response.statusCode;
56
+ const logData = {
57
+ requestId,
58
+ timestamp: new Date().toISOString(),
59
+ env: this.configService.nodeEnv,
60
+ appName: this.configService.getString('NAME'),
61
+ http: {
62
+ method: request.method,
63
+ url: request.url,
64
+ query: request.query,
65
+ statusCode,
66
+ duration: `${duration}ms`,
67
+ req: {
68
+ headers: this.sanitizeHeaders(request.headers),
69
+ body: this.sanitizeBody(request.body),
70
+ },
71
+ res: {
72
+ headers: this.sanitizeHeaders(response.getHeaders()),
73
+ },
74
+ },
75
+ user: this.extractUser(request.headers),
76
+ };
77
+ const message = `HTTP ${request.method} ${request.url} ${statusCode} (${duration}ms)`;
78
+ if (statusCode >= 500) {
79
+ this.logger.error(message, logData);
80
+ }
81
+ else if (statusCode >= 400) {
82
+ this.logger.warn(message, logData);
83
+ }
84
+ else {
85
+ this.logger.log(message, logData);
86
+ }
87
+ }
88
+ /**
89
+ * 记录请求和错误(一条日志)
90
+ */
91
+ logRequestError(requestId, request, error, duration) {
92
+ const logData = {
93
+ requestId,
94
+ timestamp: new Date().toISOString(),
95
+ env: this.configService.nodeEnv,
96
+ appName: this.configService.getString('NAME'),
97
+ http: {
98
+ method: request.method,
99
+ url: request.url,
100
+ query: request.query,
101
+ statusCode: (error === null || error === void 0 ? void 0 : error.status) || 500,
102
+ duration: `${duration}ms`,
103
+ req: {
104
+ headers: this.sanitizeHeaders(request.headers),
105
+ body: this.sanitizeBody(request.body),
106
+ },
107
+ },
108
+ error: {
109
+ message: error.message,
110
+ stack: error.stack,
111
+ code: error.code,
112
+ },
113
+ user: this.extractUser(request.headers),
114
+ };
115
+ this.logger.error(`HTTP ${request.method} ${request.url} ${(error === null || error === void 0 ? void 0 : error.status) || 500} (${duration}ms) - ${error.message}`, logData);
116
+ }
117
+ /**
118
+ * 清理敏感的 header 信息
119
+ */
120
+ sanitizeHeaders(headers) {
121
+ if (!headers)
122
+ return {};
123
+ const sanitized = Object.assign({}, headers);
124
+ const sensitiveKeys = ['authorization', 'apikey', 'x-api-key', 'token', 'cookie'];
125
+ for (const key of Object.keys(sanitized)) {
126
+ if (sensitiveKeys.includes(key.toLowerCase())) {
127
+ sanitized[key] = '[REDACTED]';
128
+ }
129
+ }
130
+ return sanitized;
131
+ }
132
+ /**
133
+ * 清理敏感的 body 信息
134
+ */
135
+ sanitizeBody(body) {
136
+ if (!body)
137
+ return body;
138
+ // 如果 body 太大,只记录部分信息
139
+ const bodyStr = JSON.stringify(body);
140
+ if (bodyStr.length > 1000) {
141
+ return { _large: `${bodyStr.length} bytes` };
142
+ }
143
+ return body;
144
+ }
145
+ /**
146
+ * 从 headers 提取用户信息
147
+ */
148
+ extractUser(headers) {
149
+ const userHeader = headers['user'] || headers['x-user'];
150
+ if (userHeader) {
151
+ try {
152
+ return typeof userHeader === 'string'
153
+ ? JSON.parse(userHeader)
154
+ : userHeader;
155
+ }
156
+ catch (_a) {
157
+ return userHeader;
158
+ }
159
+ }
160
+ return null;
161
+ }
162
+ };
163
+ exports.HttpLoggingInterceptor = HttpLoggingInterceptor;
164
+ exports.HttpLoggingInterceptor = HttpLoggingInterceptor = HttpLoggingInterceptor_1 = __decorate([
165
+ (0, common_1.Injectable)(),
166
+ __metadata("design:paramtypes", [api_config_service_1.ApiConfigService])
167
+ ], HttpLoggingInterceptor);
@@ -1,2 +1,3 @@
1
1
  export * from './language-interceptor.service';
2
2
  export * from './translation-interceptor.service';
3
+ export * from './http-logging-interceptor.service';
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./language-interceptor.service"), exports);
18
18
  __exportStar(require("./translation-interceptor.service"), exports);
19
+ __exportStar(require("./http-logging-interceptor.service"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nest-omni/core",
3
- "version": "4.1.3-25",
3
+ "version": "4.1.3-27",
4
4
  "description": "A comprehensive NestJS framework for building enterprise-grade applications with best practices",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -166,7 +166,7 @@ function bootstrapSetup(AppModule, SetupSwagger) {
166
166
  const reflector = app.get(core_1.Reflector);
167
167
  app.useGlobalFilters(new setup_1.SentryGlobalFilter(), new __1.HttpExceptionFilter(), new __1.QueryFailedFilter(reflector));
168
168
  // 全局拦截器
169
- app.useGlobalInterceptors(new __1.LanguageInterceptor(), new __1.TranslationInterceptor(), new nestjs_pino_1.LoggerErrorInterceptor());
169
+ app.useGlobalInterceptors(new __1.HttpLoggingInterceptor(configService), new __1.LanguageInterceptor(), new __1.TranslationInterceptor());
170
170
  // 全局管道
171
171
  app.useGlobalPipes(new nestjs_i18n_1.I18nValidationPipe({
172
172
  whitelist: true,
@@ -214,25 +214,10 @@ let ApiConfigService = ApiConfigService_1 = class ApiConfigService {
214
214
  genReqId: (req) => req.id,
215
215
  transport,
216
216
  level: this.isDev ? 'debug' : 'info',
217
- customLogLevel: function (res) {
218
- if (res.statusCode >= 500)
219
- return 'error';
220
- if (res.statusCode >= 400)
221
- return 'warn';
222
- return 'info';
223
- },
224
- wrapSerializers: true,
225
- customProps: (req, res) => {
226
- return {
227
- env: this.nodeEnv,
228
- appName: this.getString('NAME'),
229
- user: req === null || req === void 0 ? void 0 : req.user,
230
- 'req.body': req === null || req === void 0 ? void 0 : req.body,
231
- 'res.body': res === null || res === void 0 ? void 0 : res.body,
232
- };
233
- },
217
+ // 禁用 pinoHttp 的自动日志,由 HttpLoggingInterceptor 处理
218
+ autoLogging: false,
234
219
  redact: {
235
- paths: ['req.headers.authorization', 'req.headers.apikey'],
220
+ paths: ['req.headers.authorization', 'req.headers.apikey', 'req.headers.cookie'],
236
221
  remove: true,
237
222
  },
238
223
  },
@@ -172,7 +172,7 @@ let VaultConfigService = VaultConfigService_1 = class VaultConfigService {
172
172
  }
173
173
  try {
174
174
  const health = yield this.vaultClient.health();
175
- return health && !health.sealed;
175
+ return !!(health && !health.sealed);
176
176
  }
177
177
  catch (error) {
178
178
  this.logger.error('Vault health check failed', error.message);