@nest-omni/core 4.1.3-12 → 4.1.3-14

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 (72) hide show
  1. package/cache/dependencies/db.dependency.d.ts +55 -6
  2. package/cache/dependencies/db.dependency.js +64 -13
  3. package/common/boilerplate.polyfill.js +1 -1
  4. package/file-upload/decorators/column.decorator.d.ts +151 -0
  5. package/file-upload/decorators/column.decorator.js +273 -0
  6. package/file-upload/decorators/csv-data.decorator.d.ts +17 -31
  7. package/file-upload/decorators/csv-data.decorator.js +45 -91
  8. package/file-upload/decorators/csv-import.decorator.d.ts +34 -0
  9. package/file-upload/decorators/csv-import.decorator.js +24 -0
  10. package/file-upload/decorators/examples/column-mapping.example.d.ts +76 -0
  11. package/file-upload/decorators/examples/column-mapping.example.js +122 -0
  12. package/file-upload/decorators/excel-data.decorator.d.ts +15 -29
  13. package/file-upload/decorators/excel-data.decorator.js +42 -82
  14. package/file-upload/decorators/index.d.ts +3 -2
  15. package/file-upload/decorators/index.js +20 -2
  16. package/file-upload/decorators/validate-data.decorator.d.ts +91 -0
  17. package/file-upload/decorators/validate-data.decorator.js +39 -0
  18. package/file-upload/dto/update-file.dto.d.ts +0 -1
  19. package/file-upload/dto/update-file.dto.js +0 -4
  20. package/file-upload/entities/file-metadata.entity.d.ts +6 -3
  21. package/file-upload/entities/file-metadata.entity.js +2 -10
  22. package/file-upload/entities/file.entity.d.ts +3 -18
  23. package/file-upload/entities/file.entity.js +0 -34
  24. package/file-upload/file-upload.module.d.ts +1 -1
  25. package/file-upload/file-upload.module.js +44 -16
  26. package/file-upload/index.d.ts +13 -2
  27. package/file-upload/index.js +21 -3
  28. package/file-upload/interceptors/file-upload.interceptor.d.ts +61 -8
  29. package/file-upload/interceptors/file-upload.interceptor.js +417 -257
  30. package/file-upload/interfaces/file-processor.interface.d.ts +93 -0
  31. package/file-upload/interfaces/file-processor.interface.js +2 -0
  32. package/file-upload/interfaces/file-upload-options.interface.d.ts +3 -46
  33. package/file-upload/interfaces/file-upload-options.interface.js +3 -0
  34. package/file-upload/interfaces/processor-options.interface.d.ts +102 -0
  35. package/file-upload/interfaces/processor-options.interface.js +2 -0
  36. package/file-upload/processors/csv.processor.d.ts +98 -0
  37. package/file-upload/processors/csv.processor.js +391 -0
  38. package/file-upload/processors/excel.processor.d.ts +130 -0
  39. package/file-upload/processors/excel.processor.js +547 -0
  40. package/file-upload/processors/image.processor.d.ts +199 -0
  41. package/file-upload/processors/image.processor.js +377 -0
  42. package/file-upload/services/file.service.d.ts +3 -0
  43. package/file-upload/services/file.service.js +39 -10
  44. package/file-upload/services/malicious-file-detector.service.d.ts +29 -3
  45. package/file-upload/services/malicious-file-detector.service.js +256 -57
  46. package/file-upload/utils/dynamic-import.util.d.ts +6 -2
  47. package/file-upload/utils/dynamic-import.util.js +17 -5
  48. package/http-client/decorators/http-client.decorators.d.ts +4 -2
  49. package/http-client/decorators/http-client.decorators.js +2 -1
  50. package/http-client/entities/http-log.entity.js +1 -9
  51. package/http-client/examples/proxy-from-environment.example.d.ts +133 -0
  52. package/http-client/examples/proxy-from-environment.example.js +410 -0
  53. package/http-client/http-client.module.js +65 -6
  54. package/http-client/interfaces/http-client-config.interface.d.ts +6 -0
  55. package/http-client/services/http-client.service.d.ts +8 -0
  56. package/http-client/services/http-client.service.js +61 -17
  57. package/http-client/services/logging.service.d.ts +1 -1
  58. package/http-client/services/logging.service.js +74 -58
  59. package/http-client/utils/index.d.ts +1 -0
  60. package/http-client/utils/index.js +1 -0
  61. package/http-client/utils/proxy-environment.util.d.ts +42 -0
  62. package/http-client/utils/proxy-environment.util.js +148 -0
  63. package/package.json +9 -5
  64. package/shared/service-registry.module.js +18 -0
  65. package/transaction/data-source.util.d.ts +142 -0
  66. package/transaction/data-source.util.js +330 -0
  67. package/transaction/index.d.ts +1 -0
  68. package/transaction/index.js +12 -1
  69. package/validators/is-exists.validator.d.ts +19 -2
  70. package/validators/is-exists.validator.js +27 -2
  71. package/validators/is-unique.validator.d.ts +12 -1
  72. package/validators/is-unique.validator.js +26 -1
@@ -31,6 +31,7 @@ const context_extractor_util_1 = require("../utils/context-extractor.util");
31
31
  const curl_generator_util_1 = require("../utils/curl-generator.util");
32
32
  const retry_recorder_util_1 = require("../utils/retry-recorder.util");
33
33
  const request_id_util_1 = require("../utils/request-id.util");
34
+ const proxy_environment_util_1 = require("../utils/proxy-environment.util");
34
35
  const redis_lock_service_1 = require("../../redis-lock/redis-lock.service");
35
36
  /**
36
37
  * HTTP客户端服务
@@ -105,12 +106,10 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
105
106
  // 应用代理配置
106
107
  const proxyConfig = decoratorConfigs.proxy || this.defaultConfig.proxy;
107
108
  if (proxyConfig === null || proxyConfig === void 0 ? void 0 : proxyConfig.enabled) {
108
- enhancedConfig.proxy = {
109
- host: proxyConfig.host,
110
- port: proxyConfig.port,
111
- protocol: proxyConfig.protocol,
112
- auth: proxyConfig.auth,
113
- };
109
+ const resolvedProxy = this.resolveProxyConfig(proxyConfig, enhancedConfig.url);
110
+ if (resolvedProxy !== false) {
111
+ enhancedConfig.proxy = resolvedProxy;
112
+ }
114
113
  }
115
114
  // 配置axios-retry的onRetry回调以记录重试
116
115
  if ((_c = this.defaultConfig.retry) === null || _c === void 0 ? void 0 : _c.enabled) {
@@ -136,7 +135,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
136
135
  }
137
136
  // 成功日志
138
137
  if (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.enabled) {
139
- const databaseLogging = (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.databaseLog) ||
138
+ const databaseLogging = (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.databaseLogging) ||
140
139
  ((_e = (_d = this.defaultConfig.logging) === null || _d === void 0 ? void 0 : _d.databaseLogging) === null || _e === void 0 ? void 0 : _e.enabled);
141
140
  this.loggingService.logRequestSuccess(response, startTime, requestId, loggingOptions, databaseLogging, retryRecorder.getRecords(), cacheHit, circuitBreakerState, decoratorContext);
142
141
  }
@@ -369,21 +368,14 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
369
368
  * 创建Axios实例并配置axios-retry
370
369
  */
371
370
  createAxiosInstance() {
372
- var _a, _b;
371
+ var _a;
373
372
  const instance = require('axios').create({
374
373
  baseURL: this.defaultConfig.baseURL,
375
374
  timeout: this.defaultConfig.timeout,
376
- proxy: ((_a = this.defaultConfig.proxy) === null || _a === void 0 ? void 0 : _a.enabled)
377
- ? {
378
- host: this.defaultConfig.proxy.host,
379
- port: this.defaultConfig.proxy.port,
380
- protocol: this.defaultConfig.proxy.protocol,
381
- auth: this.defaultConfig.proxy.auth,
382
- }
383
- : undefined,
375
+ proxy: this.resolveProxyConfig(this.defaultConfig.proxy),
384
376
  });
385
377
  // 配置axios-retry
386
- if ((_b = this.defaultConfig.retry) === null || _b === void 0 ? void 0 : _b.enabled) {
378
+ if ((_a = this.defaultConfig.retry) === null || _a === void 0 ? void 0 : _a.enabled) {
387
379
  (0, axios_retry_1.default)(instance, {
388
380
  retries: this.defaultConfig.retry.retries || 3,
389
381
  retryDelay: this.defaultConfig.retry.retryDelay ||
@@ -594,6 +586,58 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
594
586
  }
595
587
  return result;
596
588
  }
589
+ /**
590
+ * 解析代理配置
591
+ * 支持从环境变量或手动配置中读取
592
+ * @param proxyConfig 代理配置
593
+ * @param targetUrl 目标 URL(用于 NO_PROXY 检查)
594
+ * @returns 解析后的代理配置,如果不应使用代理则返回 false 或 undefined
595
+ */
596
+ resolveProxyConfig(proxyConfig, targetUrl) {
597
+ if (!(proxyConfig === null || proxyConfig === void 0 ? void 0 : proxyConfig.enabled)) {
598
+ return undefined;
599
+ }
600
+ // 如果启用从环境变量读取
601
+ if (proxyConfig.fromEnvironment) {
602
+ // 确定目标协议
603
+ let protocol = 'http';
604
+ if (targetUrl) {
605
+ try {
606
+ const url = new URL(targetUrl, this.defaultConfig.baseURL);
607
+ protocol = url.protocol === 'https:' ? 'https' : 'http';
608
+ }
609
+ catch (error) {
610
+ this.logger.warn(`Failed to parse target URL: ${error.message}`);
611
+ }
612
+ }
613
+ // 从环境变量解析
614
+ const fullUrl = targetUrl
615
+ ? new URL(targetUrl, this.defaultConfig.baseURL).href
616
+ : undefined;
617
+ const envProxy = proxy_environment_util_1.ProxyEnvironmentParser.parseFromEnvironment(protocol, fullUrl);
618
+ if (envProxy === false) {
619
+ this.logger.debug('Proxy bypassed based on environment configuration');
620
+ return false;
621
+ }
622
+ return {
623
+ host: envProxy.host,
624
+ port: envProxy.port,
625
+ protocol: envProxy.protocol,
626
+ auth: envProxy.auth,
627
+ };
628
+ }
629
+ // 使用手动配置
630
+ if (proxyConfig.host && proxyConfig.port) {
631
+ return {
632
+ host: proxyConfig.host,
633
+ port: proxyConfig.port,
634
+ protocol: proxyConfig.protocol,
635
+ auth: proxyConfig.auth,
636
+ };
637
+ }
638
+ this.logger.warn('Proxy is enabled but neither fromEnvironment nor manual configuration is provided');
639
+ return undefined;
640
+ }
597
641
  };
598
642
  exports.HttpClientService = HttpClientService;
599
643
  exports.HttpClientService = HttpClientService = HttpClientService_1 = __decorate([
@@ -17,7 +17,7 @@ export declare class HttpLoggingService {
17
17
  /**
18
18
  * 记录请求成功响应
19
19
  */
20
- logRequestSuccess(response: AxiosResponse, startTime: number, requestId: string, loggingOptions: any, databaseLogging?: boolean, retryRecords?: any[], cacheHit?: boolean, circuitBreakerState?: string, decoratorContext?: any): void;
20
+ logRequestSuccess(response: AxiosResponse, startTime: number, requestId: string, loggingOptions: any, databaseLogging?: boolean, retryRecords?: any[], cacheHit?: boolean, circuitBreakerState?: string, decoratorContext?: any): Promise<void>;
21
21
  /**
22
22
  * 记录请求错误
23
23
  */
@@ -26,6 +26,7 @@ const http_log_entity_1 = require("../entities/http-log.entity");
26
26
  const request_id_util_1 = require("../utils/request-id.util");
27
27
  const context_extractor_util_1 = require("../utils/context-extractor.util");
28
28
  const call_stack_extractor_util_1 = require("../utils/call-stack-extractor.util");
29
+ const transaction_1 = require("../../transaction");
29
30
  /**
30
31
  * HTTP日志服务
31
32
  * 基于Spring Boot的请求日志记录机制,集成现有的ContextProvider
@@ -74,71 +75,77 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
74
75
  /**
75
76
  * 记录请求成功响应
76
77
  */
77
- logRequestSuccess(response, startTime, requestId, loggingOptions, databaseLogging = false, retryRecords, cacheHit, circuitBreakerState, decoratorContext) {
78
- var _a, _b;
79
- const { logHeaders = true, logBody = true, sanitizeHeaders = [], } = loggingOptions || {};
80
- const responseTime = Date.now() - startTime;
81
- // 获取当前上下文信息
82
- const context = context_extractor_util_1.ContextExtractor.getHttpContext();
83
- const logData = {
84
- requestId,
85
- userId: context.userId,
86
- statusCode: response.status,
87
- responseTime,
88
- headers: logHeaders
89
- ? this.sanitizeHeaders(response.headers, sanitizeHeaders)
90
- : undefined,
91
- body: logBody ? this.sanitizeBody(response.data) : undefined,
92
- responseSize: JSON.stringify(response.data).length,
93
- cacheHit,
94
- circuitBreakerState,
95
- };
96
- const logMessage = `HTTP Response [${requestId}]: ${response.status} (${responseTime}ms)${cacheHit ? ' [CACHE HIT]' : ''}`;
97
- switch ((loggingOptions || {}).logLevel) {
98
- case 'debug':
99
- this.logger.debug(logMessage, logData);
100
- break;
101
- case 'info':
102
- default:
103
- this.logger.log(logMessage, logData);
104
- break;
105
- }
106
- // 数据库日志记录
107
- if (databaseLogging && this.logRepository) {
108
- // 提取调用信息
109
- const callInfo = this.extractCallInfo(decoratorContext, response.config);
110
- this.saveToDatabase({
111
- id: requestId,
78
+ logRequestSuccess(response_1, startTime_1, requestId_1, loggingOptions_1) {
79
+ return __awaiter(this, arguments, void 0, function* (response, startTime, requestId, loggingOptions, databaseLogging = false, retryRecords, cacheHit, circuitBreakerState, decoratorContext) {
80
+ var _a, _b;
81
+ const { logHeaders = true, logBody = true, sanitizeHeaders = [], } = loggingOptions || {};
82
+ const responseTime = Date.now() - startTime;
83
+ // 获取当前上下文信息
84
+ const context = context_extractor_util_1.ContextExtractor.getHttpContext();
85
+ const logData = {
112
86
  requestId,
113
87
  userId: context.userId,
114
- method: ((_a = response.config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN',
115
- url: response.config.url || '',
116
- headers: this.sanitizeHeaders(response.config.headers, sanitizeHeaders),
117
- body: this.sanitizeBodyAsString(response.config.data),
118
- params: response.config.params,
119
88
  statusCode: response.status,
120
89
  responseTime,
121
- attemptCount: (retryRecords === null || retryRecords === void 0 ? void 0 : retryRecords.length)
122
- ? Math.max(...retryRecords.map((r) => r.attempt))
123
- : 1,
124
- success: true,
125
- responseHeaders: this.sanitizeHeaders(response.headers, sanitizeHeaders),
126
- responseBody: logBody
127
- ? this.sanitizeBodyAsString(response.data)
90
+ headers: logHeaders
91
+ ? this.sanitizeHeaders(response.headers, sanitizeHeaders)
128
92
  : undefined,
93
+ body: logBody ? this.sanitizeBody(response.data) : undefined,
129
94
  responseSize: JSON.stringify(response.data).length,
130
- requestSize: JSON.stringify(response.config.data).length,
131
- serviceName: context.appId,
132
- operationName: callInfo.operationName,
133
- clientIp: context.clientIp,
134
- source: (_b = context.metadata) === null || _b === void 0 ? void 0 : _b.source,
135
- tags: context.tags,
136
- metadata: logData,
137
- retryRecords,
138
95
  cacheHit,
139
96
  circuitBreakerState,
140
- });
141
- }
97
+ };
98
+ const logMessage = `HTTP Response [${requestId}]: ${response.status} (${responseTime}ms)${cacheHit ? ' [CACHE HIT]' : ''}`;
99
+ switch ((loggingOptions || {}).logLevel) {
100
+ case 'debug':
101
+ this.logger.debug(logMessage, logData);
102
+ break;
103
+ ``;
104
+ case 'info':
105
+ default:
106
+ this.logger.log(logMessage, logData);
107
+ break;
108
+ }
109
+ if (!this.logRepository) {
110
+ this.logRepository = transaction_1.TransactionContextService.getDataSource().getRepository(loggingOptions.databaseLogging.tableName);
111
+ }
112
+ // 数据库日志记录
113
+ if (databaseLogging && this.logRepository) {
114
+ // 提取调用信息
115
+ const callInfo = this.extractCallInfo(decoratorContext, response.config);
116
+ this.saveToDatabase({
117
+ id: requestId,
118
+ requestId,
119
+ userId: context.userId,
120
+ method: ((_a = response.config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN',
121
+ url: response.config.url || '',
122
+ headers: this.sanitizeHeaders(response.config.headers, sanitizeHeaders),
123
+ body: this.sanitizeBodyAsString(response.config.data),
124
+ params: response.config.params,
125
+ statusCode: response.status,
126
+ responseTime,
127
+ attemptCount: (retryRecords === null || retryRecords === void 0 ? void 0 : retryRecords.length)
128
+ ? Math.max(...retryRecords.map((r) => r.attempt))
129
+ : 1,
130
+ success: true,
131
+ responseHeaders: this.sanitizeHeaders(response.headers, sanitizeHeaders),
132
+ responseBody: logBody
133
+ ? this.sanitizeBodyAsString(response.data)
134
+ : undefined,
135
+ responseSize: JSON.stringify(response.data).length,
136
+ requestSize: JSON.stringify(response.config.data).length,
137
+ serviceName: context.appId,
138
+ operationName: callInfo.operationName,
139
+ clientIp: context.clientIp,
140
+ source: (_b = context.metadata) === null || _b === void 0 ? void 0 : _b.source,
141
+ tags: context.tags,
142
+ metadata: logData,
143
+ retryRecords,
144
+ cacheHit,
145
+ circuitBreakerState,
146
+ });
147
+ }
148
+ });
142
149
  }
143
150
  /**
144
151
  * 记录请求错误
@@ -389,13 +396,22 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
389
396
  */
390
397
  saveToDatabase(logEntity) {
391
398
  return __awaiter(this, void 0, void 0, function* () {
392
- if (!this.logRepository)
399
+ if (!this.logRepository) {
400
+ this.logger.warn('Log repository not initialized, skipping database logging');
393
401
  return;
402
+ }
394
403
  try {
404
+ this.logger.debug('Saving HTTP log to database', {
405
+ requestId: logEntity.requestId,
406
+ });
395
407
  yield this.logRepository.save(logEntity);
408
+ this.logger.debug('Successfully saved HTTP log to database', {
409
+ requestId: logEntity.requestId,
410
+ });
396
411
  }
397
412
  catch (error) {
398
413
  this.logger.error('Failed to save HTTP log to database', error);
414
+ this.logger.error('Log entity details', logEntity);
399
415
  }
400
416
  });
401
417
  }
@@ -2,3 +2,4 @@ export * from './curl-generator.util';
2
2
  export * from './retry-recorder.util';
3
3
  export * from './context-extractor.util';
4
4
  export * from './call-stack-extractor.util';
5
+ export * from './proxy-environment.util';
@@ -18,3 +18,4 @@ __exportStar(require("./curl-generator.util"), exports);
18
18
  __exportStar(require("./retry-recorder.util"), exports);
19
19
  __exportStar(require("./context-extractor.util"), exports);
20
20
  __exportStar(require("./call-stack-extractor.util"), exports);
21
+ __exportStar(require("./proxy-environment.util"), exports);
@@ -0,0 +1,42 @@
1
+ import { ProxyConfig } from '../interfaces/http-client-config.interface';
2
+ /**
3
+ * 代理环境变量解析工具
4
+ * 支持标准的代理环境变量:HTTP_PROXY, HTTPS_PROXY, NO_PROXY
5
+ */
6
+ export declare class ProxyEnvironmentParser {
7
+ private static readonly logger;
8
+ /**
9
+ * 从环境变量解析代理配置
10
+ * @param protocol 请求协议 (http 或 https)
11
+ * @param targetUrl 目标 URL(用于检查 NO_PROXY)
12
+ * @returns 解析后的代理配置,如果不应使用代理则返回 false
13
+ */
14
+ static parseFromEnvironment(protocol?: 'http' | 'https', targetUrl?: string): false | Required<Omit<ProxyConfig, 'enabled' | 'fromEnvironment'>>;
15
+ /**
16
+ * 解析代理 URL
17
+ * 支持格式: http://proxy:port, http://user:pass@proxy:port
18
+ */
19
+ private static parseProxyUrl;
20
+ /**
21
+ * 检查是否应该绕过代理
22
+ * 基于 NO_PROXY 环境变量
23
+ */
24
+ private static shouldBypassProxy;
25
+ /**
26
+ * 简单的 IP 地址匹配
27
+ * 支持精确匹配和 CIDR 表示法
28
+ */
29
+ 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
+ }
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProxyEnvironmentParser = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ /**
6
+ * 代理环境变量解析工具
7
+ * 支持标准的代理环境变量:HTTP_PROXY, HTTPS_PROXY, NO_PROXY
8
+ */
9
+ class ProxyEnvironmentParser {
10
+ /**
11
+ * 从环境变量解析代理配置
12
+ * @param protocol 请求协议 (http 或 https)
13
+ * @param targetUrl 目标 URL(用于检查 NO_PROXY)
14
+ * @returns 解析后的代理配置,如果不应使用代理则返回 false
15
+ */
16
+ static parseFromEnvironment(protocol = 'http', targetUrl) {
17
+ // 获取代理环境变量 (大写和小写都支持)
18
+ const proxyEnv = protocol === 'https'
19
+ ? process.env.HTTPS_PROXY ||
20
+ process.env.https_proxy ||
21
+ process.env.HTTP_PROXY ||
22
+ process.env.http_proxy
23
+ : process.env.HTTP_PROXY || process.env.http_proxy;
24
+ // 检查是否应该跳过代理
25
+ if (targetUrl && this.shouldBypassProxy(targetUrl)) {
26
+ this.logger.debug(`Bypassing proxy for ${targetUrl} due to NO_PROXY`);
27
+ return false;
28
+ }
29
+ if (!proxyEnv) {
30
+ this.logger.debug('No proxy environment variable found');
31
+ return false;
32
+ }
33
+ try {
34
+ const proxyConfig = this.parseProxyUrl(proxyEnv);
35
+ this.logger.debug(`Parsed proxy configuration from environment: ${JSON.stringify(proxyConfig)}`);
36
+ return proxyConfig;
37
+ }
38
+ catch (error) {
39
+ this.logger.warn(`Failed to parse proxy URL from environment: ${error.message}`);
40
+ return false;
41
+ }
42
+ }
43
+ /**
44
+ * 解析代理 URL
45
+ * 支持格式: http://proxy:port, http://user:pass@proxy:port
46
+ */
47
+ static parseProxyUrl(proxyUrl) {
48
+ try {
49
+ const url = new URL(proxyUrl);
50
+ const config = {
51
+ protocol: url.protocol.replace(':', '') || 'http',
52
+ host: url.hostname,
53
+ port: url.port ? parseInt(url.port, 10) : url.protocol === 'https:' ? 443 : 80,
54
+ auth: undefined,
55
+ };
56
+ // 解析认证信息
57
+ if (url.username || url.password) {
58
+ config.auth = {
59
+ username: decodeURIComponent(url.username),
60
+ password: decodeURIComponent(url.password),
61
+ };
62
+ }
63
+ return config;
64
+ }
65
+ catch (error) {
66
+ throw new Error(`Invalid proxy URL format: ${proxyUrl}`);
67
+ }
68
+ }
69
+ /**
70
+ * 检查是否应该绕过代理
71
+ * 基于 NO_PROXY 环境变量
72
+ */
73
+ static shouldBypassProxy(targetUrl) {
74
+ const noProxy = process.env.NO_PROXY || process.env.no_proxy;
75
+ if (!noProxy) {
76
+ return false;
77
+ }
78
+ try {
79
+ const url = new URL(targetUrl);
80
+ const hostname = url.hostname;
81
+ // NO_PROXY 可以是逗号分隔的列表
82
+ const bypassList = noProxy.split(',').map((item) => item.trim().toLowerCase());
83
+ for (const bypass of bypassList) {
84
+ if (!bypass)
85
+ continue;
86
+ // 精确匹配
87
+ if (hostname.toLowerCase() === bypass) {
88
+ return true;
89
+ }
90
+ // 通配符匹配 (例如 .example.com)
91
+ if (bypass.startsWith('.') && hostname.toLowerCase().endsWith(bypass)) {
92
+ return true;
93
+ }
94
+ // 后缀匹配 (例如 example.com 匹配 api.example.com)
95
+ if (hostname.toLowerCase().endsWith('.' + bypass)) {
96
+ return true;
97
+ }
98
+ // 特殊值 * 表示所有域名都绕过
99
+ if (bypass === '*') {
100
+ return true;
101
+ }
102
+ // IP 地址范围匹配 (简单实现)
103
+ if (this.isIpMatch(hostname, bypass)) {
104
+ return true;
105
+ }
106
+ }
107
+ }
108
+ catch (error) {
109
+ this.logger.warn(`Failed to parse target URL for NO_PROXY check: ${error.message}`);
110
+ }
111
+ return false;
112
+ }
113
+ /**
114
+ * 简单的 IP 地址匹配
115
+ * 支持精确匹配和 CIDR 表示法
116
+ */
117
+ static isIpMatch(hostname, pattern) {
118
+ // 精确 IP 匹配
119
+ if (hostname === pattern) {
120
+ return true;
121
+ }
122
+ // CIDR 匹配 (简化版本,不做完整的 CIDR 计算)
123
+ if (pattern.includes('/')) {
124
+ const [baseIp] = pattern.split('/');
125
+ return hostname.startsWith(baseIp.substring(0, baseIp.lastIndexOf('.')));
126
+ }
127
+ return false;
128
+ }
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
+ }
147
+ exports.ProxyEnvironmentParser = ProxyEnvironmentParser;
148
+ ProxyEnvironmentParser.logger = new common_1.Logger(ProxyEnvironmentParser.name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nest-omni/core",
3
- "version": "4.1.3-12",
3
+ "version": "4.1.3-14",
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",
@@ -36,7 +36,7 @@
36
36
  "author": "Jinpy",
37
37
  "license": "Apache-2.0",
38
38
  "engines": {
39
- "node": ">=18.0.0",
39
+ "node": ">=22.0.0",
40
40
  "npm": ">=9.0.0"
41
41
  },
42
42
  "devDependencies": {
@@ -45,6 +45,7 @@
45
45
  "@types/express-session": "^1.18.2",
46
46
  "@types/lodash": "^4.17.20",
47
47
  "@types/node": "^24.9.1",
48
+ "@types/sharp": "^0.31.1",
48
49
  "@types/sprintf-js": "^1.1.4",
49
50
  "@types/uuid": "^11.0.0",
50
51
  "sqlite3": "^5.1.7",
@@ -66,7 +67,7 @@
66
67
  "@sentry/profiling-node": "^10.22.0",
67
68
  "@songkeys/nestjs-redis": "^11.0.0",
68
69
  "@songkeys/nestjs-redis-health": "^11.0.0",
69
- "axios": "^1.12.2",
70
+ "axios": "^1.13.2",
70
71
  "axios-retry": "^4.5.0",
71
72
  "bcrypt": "^6.0.0",
72
73
  "body-parser": "^2.2.0",
@@ -96,6 +97,9 @@
96
97
  "source-map-support": "^0.5.21",
97
98
  "sprintf-js": "^1.1.3",
98
99
  "typeorm": "^0.3.27",
99
- "uuid": "^13.0.0"
100
- }
100
+ "uuid": "^13.0.0",
101
+ "fast-csv": "^5.0.5",
102
+ "xlsx": "git+https://git.sheetjs.com/sheetjs/sheetjs.git#v0.20.3"
103
+ },
104
+ "peerDependencies": {}
101
105
  }
@@ -36,6 +36,7 @@ const redis_lock_1 = require("../redis-lock");
36
36
  const typeorm_2 = require("typeorm");
37
37
  const vault_1 = require("../vault");
38
38
  const validators_1 = require("../validators");
39
+ const transaction_1 = require("../transaction");
39
40
  const providers = [
40
41
  services_1.ApiConfigService,
41
42
  services_1.GeneratorService,
@@ -85,10 +86,27 @@ const modules = [
85
86
  }),
86
87
  ];
87
88
  if (services_1.ApiConfigService.toBoolean(process.env.DB_ENABLED, true)) {
89
+ // 1. 首先导入 TransactionModule
90
+ modules.push(transaction_1.TransactionModule.forRoot({
91
+ defaultDataSource: 'default',
92
+ enableDynamicRegistration: true,
93
+ }));
94
+ // 2. 导入 TypeORM 模块,并在初始化后注册数据源
88
95
  modules.push(typeorm_1.TypeOrmModule.forRootAsync({
89
96
  inject: [services_1.ApiConfigService],
90
97
  useFactory: (config) => config.typeormConfig,
91
98
  }));
99
+ // 3. 添加数据源注册服务,在应用启动时注册 DataSource
100
+ providers.push({
101
+ provide: 'DATASOURCE_REGISTRAR',
102
+ useFactory: (dataSource) => __awaiter(void 0, void 0, void 0, function* () {
103
+ // 手动注册 default 数据源到 DataSourceUtil
104
+ (0, transaction_1.addDataSource)('default', dataSource, true);
105
+ return dataSource;
106
+ }),
107
+ inject: [typeorm_2.DataSource],
108
+ });
109
+ // 4. 配置 CLS 模块
92
110
  modules.push(nestjs_cls_1.ClsModule.forRootAsync({
93
111
  imports: [typeorm_1.TypeOrmModule],
94
112
  useFactory(args) {