@nest-omni/core 4.1.3-20 → 4.1.3-23
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/audit/audit.module.d.ts +1 -0
- package/audit/audit.module.js +5 -3
- package/audit/controllers/audit.controller.d.ts +3 -11
- package/audit/controllers/audit.controller.js +12 -19
- package/audit/decorators/audit-operation.decorator.d.ts +0 -7
- package/audit/decorators/audit-operation.decorator.js +0 -7
- package/audit/dto/audit-action-query.dto.d.ts +13 -0
- package/audit/dto/audit-action-query.dto.js +77 -0
- package/audit/dto/index.d.ts +1 -0
- package/audit/dto/index.js +1 -0
- package/audit/entities/entity-audit-log.entity.d.ts +1 -4
- package/audit/entities/entity-audit-log.entity.js +1 -17
- package/audit/entities/manual-operation-log.entity.d.ts +0 -2
- package/audit/entities/manual-operation-log.entity.js +0 -8
- package/audit/enums/audit.enums.d.ts +0 -8
- package/audit/enums/audit.enums.js +1 -10
- package/audit/examples/decorator-value-mapping.example.d.ts +70 -0
- package/audit/examples/decorator-value-mapping.example.js +414 -0
- package/audit/index.d.ts +1 -0
- package/audit/index.js +5 -1
- package/audit/interceptors/audit.interceptor.d.ts +1 -0
- package/audit/interceptors/audit.interceptor.js +19 -11
- package/audit/interfaces/audit.interfaces.d.ts +2 -17
- package/audit/services/audit-context.service.d.ts +9 -0
- package/audit/services/entity-audit.service.d.ts +65 -24
- package/audit/services/entity-audit.service.js +280 -93
- package/audit/services/manual-audit-log.service.d.ts +0 -1
- package/audit/services/manual-audit-log.service.js +1 -3
- package/audit/subscribers/entity-audit.subscriber.d.ts +1 -0
- package/audit/subscribers/entity-audit.subscriber.js +22 -5
- package/cache/cache.module.d.ts +7 -2
- package/cache/cache.module.js +9 -7
- package/cache/cache.service.d.ts +4 -4
- package/cache/cache.service.js +5 -5
- package/cache/entities/index.d.ts +1 -0
- package/cache/entities/index.js +17 -0
- package/cache/entities/typeorm-cache.entity.d.ts +71 -0
- package/cache/entities/typeorm-cache.entity.js +110 -0
- package/cache/index.d.ts +2 -1
- package/cache/index.js +19 -2
- package/cache/providers/index.d.ts +2 -1
- package/cache/providers/index.js +2 -1
- package/cache/providers/lrucache.provider.d.ts +76 -0
- package/cache/providers/lrucache.provider.js +226 -0
- package/cache/providers/typeorm-cache.provider.d.ts +211 -0
- package/cache/providers/typeorm-cache.provider.js +483 -0
- package/common/boilerplate.polyfill.d.ts +1 -0
- package/common/boilerplate.polyfill.js +17 -0
- package/common/helpers/validation-metadata-helper.d.ts +55 -0
- package/common/helpers/validation-metadata-helper.js +60 -0
- package/common/index.d.ts +1 -0
- package/common/index.js +4 -0
- package/decorators/field.decorators.d.ts +71 -2
- package/decorators/field.decorators.js +147 -18
- package/decorators/transform.decorators.d.ts +0 -2
- package/decorators/transform.decorators.js +0 -23
- package/filters/bad-request.filter.js +19 -4
- package/http-client/examples/axios-config-extended.example.d.ts +17 -0
- package/http-client/examples/axios-config-extended.example.js +313 -0
- package/http-client/examples/index.d.ts +2 -0
- package/http-client/examples/index.js +2 -0
- package/http-client/examples/ssl-certificate.example.d.ts +47 -0
- package/http-client/examples/ssl-certificate.example.js +431 -0
- package/http-client/index.d.ts +1 -1
- package/http-client/interfaces/http-client-config.interface.d.ts +73 -0
- package/http-client/services/http-client.service.js +46 -5
- package/http-client/utils/context-extractor.util.js +2 -0
- package/ip-filter/constants.d.ts +21 -0
- package/ip-filter/constants.js +24 -0
- package/ip-filter/decorators/index.d.ts +1 -0
- package/ip-filter/decorators/index.js +17 -0
- package/ip-filter/decorators/ip-filter.decorator.d.ts +58 -0
- package/ip-filter/decorators/ip-filter.decorator.js +79 -0
- package/ip-filter/guards/index.d.ts +1 -0
- package/ip-filter/guards/index.js +17 -0
- package/ip-filter/guards/ip-filter.guard.d.ts +62 -0
- package/ip-filter/guards/ip-filter.guard.js +174 -0
- package/ip-filter/index.d.ts +7 -0
- package/ip-filter/index.js +23 -0
- package/ip-filter/interfaces/index.d.ts +4 -0
- package/ip-filter/interfaces/index.js +20 -0
- package/ip-filter/interfaces/ip-filter-async-options.interface.d.ts +15 -0
- package/ip-filter/interfaces/ip-filter-async-options.interface.js +2 -0
- package/ip-filter/interfaces/ip-filter-metadata.interface.d.ts +26 -0
- package/ip-filter/interfaces/ip-filter-metadata.interface.js +2 -0
- package/ip-filter/interfaces/ip-filter-options.interface.d.ts +34 -0
- package/ip-filter/interfaces/ip-filter-options.interface.js +2 -0
- package/ip-filter/interfaces/ip-rule.interface.d.ts +36 -0
- package/ip-filter/interfaces/ip-rule.interface.js +2 -0
- package/ip-filter/ip-filter.module.d.ts +55 -0
- package/ip-filter/ip-filter.module.js +105 -0
- package/ip-filter/services/index.d.ts +1 -0
- package/ip-filter/services/index.js +17 -0
- package/ip-filter/services/ip-filter.service.d.ts +92 -0
- package/ip-filter/services/ip-filter.service.js +238 -0
- package/ip-filter/utils/index.d.ts +1 -0
- package/ip-filter/utils/index.js +17 -0
- package/ip-filter/utils/ip-utils.d.ts +61 -0
- package/ip-filter/utils/ip-utils.js +162 -0
- package/package.json +23 -24
- package/providers/context.provider.d.ts +9 -0
- package/providers/context.provider.js +13 -0
- package/setup/bootstrap.setup.d.ts +1 -1
- package/setup/bootstrap.setup.js +1 -1
- package/shared/service-registry.module.js +0 -1
- package/cache/providers/memory-cache.provider.d.ts +0 -69
- package/cache/providers/memory-cache.provider.js +0 -237
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.sslEnvVars = exports.DynamicCertificateManager = void 0;
|
|
13
|
+
exports.disableSslVerificationExample = disableSslVerificationExample;
|
|
14
|
+
exports.selfSignedCaExample = selfSignedCaExample;
|
|
15
|
+
exports.multipleCaCertsExample = multipleCaCertsExample;
|
|
16
|
+
exports.clientCertificateExample = clientCertificateExample;
|
|
17
|
+
exports.tlsVersionAndCiphersExample = tlsVersionAndCiphersExample;
|
|
18
|
+
exports.certFromEnvExample = certFromEnvExample;
|
|
19
|
+
exports.productionSslConfigExample = productionSslConfigExample;
|
|
20
|
+
exports.environmentBasedSslConfig = environmentBasedSslConfig;
|
|
21
|
+
exports.handleCertExpirationExample = handleCertExpirationExample;
|
|
22
|
+
exports.directHttpsAgentExample = directHttpsAgentExample;
|
|
23
|
+
exports.sslErrorHandlingExample = sslErrorHandlingExample;
|
|
24
|
+
exports.diagnoseSslConfig = diagnoseSslConfig;
|
|
25
|
+
const index_1 = require("../index");
|
|
26
|
+
const fs_1 = require("fs");
|
|
27
|
+
const path_1 = require("path");
|
|
28
|
+
/**
|
|
29
|
+
* SSL/TLS 证书配置示例
|
|
30
|
+
* 解决自签名证书、客户端证书认证等问题
|
|
31
|
+
*/
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// 示例 1: 禁用证书验证(仅用于开发环境)
|
|
34
|
+
// ============================================================================
|
|
35
|
+
function disableSslVerificationExample() {
|
|
36
|
+
const config = {
|
|
37
|
+
baseURL: 'https://self-signed.example.com',
|
|
38
|
+
timeout: 30000,
|
|
39
|
+
axiosConfig: {
|
|
40
|
+
ssl: {
|
|
41
|
+
// ⚠️ 危险:禁用证书验证,仅用于开发/测试环境
|
|
42
|
+
// 生产环境严禁使用!
|
|
43
|
+
rejectUnauthorized: false,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
// 使用示例
|
|
48
|
+
// const httpClient = new HttpClientService(circuitBreakerService, loggingService, null, config);
|
|
49
|
+
// return httpClient.get('/api/data');
|
|
50
|
+
}
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// 示例 2: 使用自签名 CA 证书(推荐)
|
|
53
|
+
// ============================================================================
|
|
54
|
+
function selfSignedCaExample() {
|
|
55
|
+
// 方式1: 从文件读取 CA 证书
|
|
56
|
+
const caCert = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'certs/ca.crt'));
|
|
57
|
+
const config = {
|
|
58
|
+
baseURL: 'https://self-signed.example.com',
|
|
59
|
+
timeout: 30000,
|
|
60
|
+
axiosConfig: {
|
|
61
|
+
ssl: {
|
|
62
|
+
// 指定信任的 CA 证书
|
|
63
|
+
ca: caCert,
|
|
64
|
+
// 仍然验证证书,但信任我们的自签名 CA
|
|
65
|
+
rejectUnauthorized: true,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
return config;
|
|
70
|
+
}
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// 示例 3: 使用多个 CA 证书
|
|
73
|
+
// ============================================================================
|
|
74
|
+
function multipleCaCertsExample() {
|
|
75
|
+
const ca1 = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'certs/ca1.crt'), 'utf8');
|
|
76
|
+
const ca2 = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'certs/ca2.crt'), 'utf8');
|
|
77
|
+
const ca3 = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'certs/ca3.crt'), 'utf8');
|
|
78
|
+
const config = {
|
|
79
|
+
baseURL: 'https://multi-ca.example.com',
|
|
80
|
+
timeout: 30000,
|
|
81
|
+
axiosConfig: {
|
|
82
|
+
ssl: {
|
|
83
|
+
// 支持多个 CA 证书
|
|
84
|
+
ca: [ca1, ca2, ca3],
|
|
85
|
+
rejectUnauthorized: true,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
return config;
|
|
90
|
+
}
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// 示例 4: 使用客户端证书(双向 TLS 认证)
|
|
93
|
+
// ============================================================================
|
|
94
|
+
function clientCertificateExample() {
|
|
95
|
+
const caCert = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'certs/ca.crt'));
|
|
96
|
+
const clientCert = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'certs/client.crt'));
|
|
97
|
+
const clientKey = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'certs/client.key'));
|
|
98
|
+
const config = {
|
|
99
|
+
baseURL: 'https://mutual-tls.example.com',
|
|
100
|
+
timeout: 30000,
|
|
101
|
+
axiosConfig: {
|
|
102
|
+
ssl: {
|
|
103
|
+
// CA 证书(验证服务器)
|
|
104
|
+
ca: caCert,
|
|
105
|
+
// 客户端证书(服务器验证客户端)
|
|
106
|
+
cert: clientCert,
|
|
107
|
+
// 客户端私钥
|
|
108
|
+
key: clientKey,
|
|
109
|
+
// 如果私钥有密码
|
|
110
|
+
passphrase: 'your-private-key-password',
|
|
111
|
+
rejectUnauthorized: true,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
return config;
|
|
116
|
+
}
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// 示例 5: 配置 TLS 版本和加密套件
|
|
119
|
+
// ============================================================================
|
|
120
|
+
function tlsVersionAndCiphersExample() {
|
|
121
|
+
const config = {
|
|
122
|
+
baseURL: 'https://secure-api.example.com',
|
|
123
|
+
timeout: 30000,
|
|
124
|
+
axiosConfig: {
|
|
125
|
+
ssl: {
|
|
126
|
+
// 限制 TLS 版本
|
|
127
|
+
minVersion: 'TLSv1.2',
|
|
128
|
+
maxVersion: 'TLSv1.3',
|
|
129
|
+
// 指定加密套件
|
|
130
|
+
ciphers: 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:' +
|
|
131
|
+
'ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384',
|
|
132
|
+
rejectUnauthorized: true,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
return config;
|
|
137
|
+
}
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// 示例 6: 从环境变量加载证书
|
|
140
|
+
// ============================================================================
|
|
141
|
+
function certFromEnvExample() {
|
|
142
|
+
const config = {
|
|
143
|
+
baseURL: process.env.API_BASE_URL,
|
|
144
|
+
timeout: 30000,
|
|
145
|
+
axiosConfig: {
|
|
146
|
+
ssl: {
|
|
147
|
+
// CA 证书路径或内容
|
|
148
|
+
ca: process.env.SSL_CA_CERT,
|
|
149
|
+
// 客户端证书
|
|
150
|
+
cert: process.env.SSL_CLIENT_CERT,
|
|
151
|
+
// 客户端私钥
|
|
152
|
+
key: process.env.SSL_CLIENT_KEY,
|
|
153
|
+
// 私钥密码
|
|
154
|
+
passphrase: process.env.SSL_PASSPHRASE,
|
|
155
|
+
rejectUnauthorized: true,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
return config;
|
|
160
|
+
}
|
|
161
|
+
// ============================================================================
|
|
162
|
+
// 示例 7: 完整的 SSL 配置(生产环境)
|
|
163
|
+
// ============================================================================
|
|
164
|
+
function productionSslConfigExample() {
|
|
165
|
+
// 在实际应用中,证书应该从安全的配置中心加载
|
|
166
|
+
const caCert = (0, fs_1.readFileSync)(process.env.SSL_CA_PATH || '/etc/ssl/certs/ca.pem');
|
|
167
|
+
const clientCert = (0, fs_1.readFileSync)(process.env.SSL_CLIENT_CERT_PATH || '/etc/ssl/certs/client.pem');
|
|
168
|
+
const clientKey = (0, fs_1.readFileSync)(process.env.SSL_CLIENT_KEY_PATH || '/etc/ssl/private/client.key');
|
|
169
|
+
return {
|
|
170
|
+
baseURL: process.env.API_BASE_URL,
|
|
171
|
+
timeout: 30000,
|
|
172
|
+
// 重试配置
|
|
173
|
+
retry: {
|
|
174
|
+
enabled: true,
|
|
175
|
+
retries: 3,
|
|
176
|
+
},
|
|
177
|
+
// 熔断器配置
|
|
178
|
+
circuitBreaker: {
|
|
179
|
+
enabled: true,
|
|
180
|
+
failureThreshold: 5,
|
|
181
|
+
},
|
|
182
|
+
// 日志配置
|
|
183
|
+
logging: {
|
|
184
|
+
enabled: true,
|
|
185
|
+
logLevel: 'info',
|
|
186
|
+
},
|
|
187
|
+
// SSL 配置
|
|
188
|
+
axiosConfig: {
|
|
189
|
+
ssl: {
|
|
190
|
+
ca: caCert,
|
|
191
|
+
cert: clientCert,
|
|
192
|
+
key: clientKey,
|
|
193
|
+
passphrase: process.env.SSL_PASSPHRASE,
|
|
194
|
+
rejectUnauthorized: true, // 生产环境必须验证证书
|
|
195
|
+
minVersion: 'TLSv1.2', // 最低 TLS 1.2
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// 示例 8: 不同环境使用不同 SSL 配置
|
|
202
|
+
// ============================================================================
|
|
203
|
+
function environmentBasedSslConfig() {
|
|
204
|
+
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
205
|
+
const isTest = process.env.NODE_ENV === 'test';
|
|
206
|
+
const baseConfig = {
|
|
207
|
+
baseURL: process.env.API_BASE_URL,
|
|
208
|
+
timeout: 30000,
|
|
209
|
+
};
|
|
210
|
+
// 开发/测试环境:禁用证书验证
|
|
211
|
+
if (isDevelopment || isTest) {
|
|
212
|
+
baseConfig.axiosConfig = {
|
|
213
|
+
ssl: {
|
|
214
|
+
rejectUnauthorized: false, // 仅用于开发/测试
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
// 生产环境:使用证书验证
|
|
220
|
+
baseConfig.axiosConfig = {
|
|
221
|
+
ssl: {
|
|
222
|
+
ca: (0, fs_1.readFileSync)(process.env.SSL_CA_PATH),
|
|
223
|
+
rejectUnauthorized: true,
|
|
224
|
+
minVersion: 'TLSv1.2',
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return baseConfig;
|
|
229
|
+
}
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// 示例 9: 处理证书过期问题
|
|
232
|
+
// ============================================================================
|
|
233
|
+
function handleCertExpirationExample() {
|
|
234
|
+
const config = {
|
|
235
|
+
baseURL: 'https://api.example.com',
|
|
236
|
+
timeout: 30000,
|
|
237
|
+
axiosConfig: {
|
|
238
|
+
ssl: {
|
|
239
|
+
// 设置服务器名称(SNI)
|
|
240
|
+
servername: 'api.example.com',
|
|
241
|
+
// 如果服务器证书过期但仍需要连接(不推荐)
|
|
242
|
+
rejectUnauthorized: process.env.ALLOW_EXPIRED_CERT === 'true',
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
// 重试配置:证书问题通常不需要重试
|
|
246
|
+
retry: {
|
|
247
|
+
enabled: true,
|
|
248
|
+
retries: 1,
|
|
249
|
+
retryCondition: (error) => {
|
|
250
|
+
// 不重试证书错误
|
|
251
|
+
if (error.code === 'CERT_HAS_EXPIRED' ||
|
|
252
|
+
error.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
return !error.response || error.response.status >= 500;
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
return config;
|
|
260
|
+
}
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// 示例 10: 使用 https.Agent 直接配置(兼容旧方式)
|
|
263
|
+
// ============================================================================
|
|
264
|
+
function directHttpsAgentExample() {
|
|
265
|
+
const https = require('https');
|
|
266
|
+
const fs = require('fs');
|
|
267
|
+
// 直接创建 https.Agent
|
|
268
|
+
const httpsAgent = new https.Agent({
|
|
269
|
+
ca: fs.readFileSync('/path/to/ca.crt'),
|
|
270
|
+
cert: fs.readFileSync('/path/to/client.crt'),
|
|
271
|
+
key: fs.readFileSync('/path/to/client.key'),
|
|
272
|
+
rejectUnauthorized: true,
|
|
273
|
+
keepAlive: true,
|
|
274
|
+
});
|
|
275
|
+
const config = {
|
|
276
|
+
baseURL: 'https://api.example.com',
|
|
277
|
+
timeout: 30000,
|
|
278
|
+
axiosConfig: {
|
|
279
|
+
// 直接使用 httpsAgent
|
|
280
|
+
httpsAgent: httpsAgent,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
return config;
|
|
284
|
+
}
|
|
285
|
+
// ============================================================================
|
|
286
|
+
// 示例 11: 错误处理和调试
|
|
287
|
+
// ============================================================================
|
|
288
|
+
function sslErrorHandlingExample() {
|
|
289
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
290
|
+
const config = {
|
|
291
|
+
baseURL: 'https://self-signed.example.com',
|
|
292
|
+
timeout: 30000,
|
|
293
|
+
axiosConfig: {
|
|
294
|
+
ssl: {
|
|
295
|
+
rejectUnauthorized: true, // 先启用验证
|
|
296
|
+
ca: (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'certs/ca.crt')),
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
// const httpClient = new HttpClientService(circuitBreakerService, loggingService, null, config);
|
|
301
|
+
try {
|
|
302
|
+
// return await httpClient.get('/api/data');
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
// 常见的 SSL 错误码
|
|
306
|
+
switch (error.code) {
|
|
307
|
+
case 'CERT_HAS_EXPIRED':
|
|
308
|
+
console.error('证书已过期');
|
|
309
|
+
break;
|
|
310
|
+
case 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY':
|
|
311
|
+
console.error('无法获取本地颁发者证书,请检查 CA 配置');
|
|
312
|
+
break;
|
|
313
|
+
case 'SELF_SIGNED_CERT_IN_CHAIN':
|
|
314
|
+
console.error('证书链中有自签名证书,请添加 CA 证书');
|
|
315
|
+
break;
|
|
316
|
+
case 'DEPTH_ZERO_SELF_SIGNED_CERT':
|
|
317
|
+
console.error('自签名证书,需要设置 rejectUnauthorized: false 或添加 CA');
|
|
318
|
+
break;
|
|
319
|
+
case 'CERT_REVOKED':
|
|
320
|
+
console.error('证书已被吊销');
|
|
321
|
+
break;
|
|
322
|
+
default:
|
|
323
|
+
console.error('SSL 错误:', error.message);
|
|
324
|
+
}
|
|
325
|
+
throw error;
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
// ============================================================================
|
|
330
|
+
// 示例 12: 动态加载证书(支持证书更新)
|
|
331
|
+
// ============================================================================
|
|
332
|
+
class DynamicCertificateManager {
|
|
333
|
+
constructor(circuitBreakerService, loggingService, certPaths) {
|
|
334
|
+
this.circuitBreakerService = circuitBreakerService;
|
|
335
|
+
this.loggingService = loggingService;
|
|
336
|
+
this.certPaths = certPaths;
|
|
337
|
+
this.certCache = {};
|
|
338
|
+
this.lastUpdate = 0;
|
|
339
|
+
this.cacheDuration = 5 * 60 * 1000; // 5分钟
|
|
340
|
+
this.httpClient = new index_1.HttpClientService(this.circuitBreakerService, this.loggingService, null, this.buildConfig());
|
|
341
|
+
}
|
|
342
|
+
loadCerts() {
|
|
343
|
+
const now = Date.now();
|
|
344
|
+
if (now - this.lastUpdate < this.cacheDuration && Object.keys(this.certCache).length > 0) {
|
|
345
|
+
return this.certCache;
|
|
346
|
+
}
|
|
347
|
+
// 重新加载证书
|
|
348
|
+
if (this.certPaths.ca) {
|
|
349
|
+
this.certCache.ca = (0, fs_1.readFileSync)(this.certPaths.ca);
|
|
350
|
+
}
|
|
351
|
+
if (this.certPaths.cert) {
|
|
352
|
+
this.certCache.cert = (0, fs_1.readFileSync)(this.certPaths.cert);
|
|
353
|
+
}
|
|
354
|
+
if (this.certPaths.key) {
|
|
355
|
+
this.certCache.key = (0, fs_1.readFileSync)(this.certPaths.key);
|
|
356
|
+
}
|
|
357
|
+
this.lastUpdate = now;
|
|
358
|
+
return this.certCache;
|
|
359
|
+
}
|
|
360
|
+
buildConfig() {
|
|
361
|
+
const certs = this.loadCerts();
|
|
362
|
+
return {
|
|
363
|
+
baseURL: process.env.API_BASE_URL,
|
|
364
|
+
timeout: 30000,
|
|
365
|
+
axiosConfig: {
|
|
366
|
+
ssl: Object.assign(Object.assign({}, certs), { rejectUnauthorized: true }),
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
// 重新创建客户端以使用新证书
|
|
371
|
+
refreshCerts() {
|
|
372
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
373
|
+
this.lastUpdate = 0;
|
|
374
|
+
const config = this.buildConfig();
|
|
375
|
+
// 在实际应用中,可能需要重新创建 HttpClientService 实例
|
|
376
|
+
// 或者提供更新配置的方法
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
get(url) {
|
|
380
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
381
|
+
// 每次请求前检查是否需要更新证书
|
|
382
|
+
this.loadCerts();
|
|
383
|
+
// return this.httpClient.get(url);
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
exports.DynamicCertificateManager = DynamicCertificateManager;
|
|
388
|
+
// ============================================================================
|
|
389
|
+
// 环境变量配置示例
|
|
390
|
+
// ============================================================================
|
|
391
|
+
exports.sslEnvVars = {
|
|
392
|
+
// SSL 配置
|
|
393
|
+
SSL_CA_PATH: '/path/to/ca.crt', // CA 证书文件路径
|
|
394
|
+
SSL_CLIENT_CERT_PATH: '/path/to/client.crt', // 客户端证书文件路径
|
|
395
|
+
SSL_CLIENT_KEY_PATH: '/path/to/client.key', // 客户端私钥文件路径
|
|
396
|
+
SSL_PASSPHRASE: 'your-passphrase', // 私钥密码
|
|
397
|
+
SSL_REJECT_UNAUTHORIZED: 'true', // 是否验证证书(true/false)
|
|
398
|
+
SSL_MIN_VERSION: 'TLSv1.2', // 最低 TLS 版本
|
|
399
|
+
SSL_CIPHERS: 'ECDHE-RSA-AES128-GCM-SHA256:...', // 加密套件
|
|
400
|
+
// 或者直接提供证书内容
|
|
401
|
+
SSL_CA_CERT: '-----BEGIN CERTIFICATE-----\n...', // CA 证书内容
|
|
402
|
+
SSL_CLIENT_CERT: '-----BEGIN CERTIFICATE-----\n...', // 客户端证书内容
|
|
403
|
+
SSL_CLIENT_KEY: '-----BEGIN PRIVATE KEY-----\n...', // 客户端私钥内容
|
|
404
|
+
};
|
|
405
|
+
// ============================================================================
|
|
406
|
+
// 快速诊断脚本
|
|
407
|
+
// ============================================================================
|
|
408
|
+
function diagnoseSslConfig(apiUrl) {
|
|
409
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
410
|
+
const https = require('https');
|
|
411
|
+
return new Promise((resolve, reject) => {
|
|
412
|
+
const req = https.get(apiUrl, (res) => {
|
|
413
|
+
const cert = res.socket.getPeerCertificate();
|
|
414
|
+
console.log('服务器证书信息:');
|
|
415
|
+
console.log(' 主题:', cert.subject);
|
|
416
|
+
console.log(' 颁发者:', cert.issuer);
|
|
417
|
+
console.log(' 有效期:', new Date(cert.valid_from), '至', new Date(cert.valid_to));
|
|
418
|
+
console.log(' 序列号:', cert.serialNumber);
|
|
419
|
+
resolve(cert);
|
|
420
|
+
});
|
|
421
|
+
req.on('error', (error) => {
|
|
422
|
+
console.error('SSL 连接失败:', error.code, error.message);
|
|
423
|
+
reject(error);
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
// 使用示例:
|
|
429
|
+
// diagnoseSslConfig('https://self-signed.example.com')
|
|
430
|
+
// .then(cert => console.log('证书 OK'))
|
|
431
|
+
// .catch(err => console.log('证书错误'));
|
package/http-client/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { HttpClientConfig, RetryConfig, CircuitBreakerConfig, ProxyConfig, LoggingConfig, InterceptorConfig, ConnectionPoolConfig, HttpContext, HttpInterceptor, HttpMethod, HttpStats, } from './interfaces/http-client-config.interface';
|
|
1
|
+
export { HttpClientConfig, AxiosNativeConfig, SslCertificateConfig, RetryConfig, CircuitBreakerConfig, ProxyConfig, LoggingConfig, InterceptorConfig, ConnectionPoolConfig, HttpContext, HttpInterceptor, HttpMethod, HttpStats, } from './interfaces/http-client-config.interface';
|
|
2
2
|
export * from './interfaces/api-client-config.interface';
|
|
3
3
|
export { HttpLogEntity, RetryRecord } from './entities';
|
|
4
4
|
export * from './decorators';
|
|
@@ -1,6 +1,73 @@
|
|
|
1
1
|
import { AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
2
2
|
import { LogCleanupConfig } from '../services/log-cleanup.service';
|
|
3
3
|
import { SSRFProtectionConfig, URLValidationConfig } from '../utils/security-validator.util';
|
|
4
|
+
import type { AgentOptions } from 'https';
|
|
5
|
+
/**
|
|
6
|
+
* SSL/TLS 证书配置
|
|
7
|
+
* 兼容 Node.js https.Agent 的选项
|
|
8
|
+
* 参考: https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options
|
|
9
|
+
*/
|
|
10
|
+
export interface SslCertificateConfig extends Partial<Omit<AgentOptions, 'host' | 'port' | 'path'>> {
|
|
11
|
+
/**
|
|
12
|
+
* 是否禁用证书验证(仅用于开发/测试环境)
|
|
13
|
+
* 警告:设置为 true 会使连接不安全,生产环境严禁使用
|
|
14
|
+
*/
|
|
15
|
+
rejectUnauthorized?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* CA 证书内容(PEM 格式)
|
|
18
|
+
* 可以是单个证书或多个证书的字符串
|
|
19
|
+
*/
|
|
20
|
+
ca?: string | string[] | Buffer;
|
|
21
|
+
/**
|
|
22
|
+
* 客户端证书内容(PEM 格式)
|
|
23
|
+
*/
|
|
24
|
+
cert?: string | string[] | Buffer;
|
|
25
|
+
/**
|
|
26
|
+
* 客户端私钥内容(PEM 格式)
|
|
27
|
+
*/
|
|
28
|
+
key?: string | string[] | Buffer;
|
|
29
|
+
/**
|
|
30
|
+
* 私钥密码
|
|
31
|
+
*/
|
|
32
|
+
passphrase?: string;
|
|
33
|
+
/**
|
|
34
|
+
* 服务器名称(用于 SNI)
|
|
35
|
+
*/
|
|
36
|
+
servername?: string;
|
|
37
|
+
/**
|
|
38
|
+
* 支持的 TLS 版本
|
|
39
|
+
*/
|
|
40
|
+
minVersion?: 'TLSv1' | 'TLSv1.1' | 'TLSv1.2' | 'TLSv1.3';
|
|
41
|
+
maxVersion?: 'TLSv1' | 'TLSv1.1' | 'TLSv1.2' | 'TLSv1.3';
|
|
42
|
+
/**
|
|
43
|
+
* 支持的加密套件
|
|
44
|
+
*/
|
|
45
|
+
ciphers?: string;
|
|
46
|
+
/**
|
|
47
|
+
* 是否支持 DH 参数
|
|
48
|
+
*/
|
|
49
|
+
dhparam?: string | Buffer;
|
|
50
|
+
/**
|
|
51
|
+
* 是否使用安全 renegotiation
|
|
52
|
+
*/
|
|
53
|
+
secureOptions?: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Axios 原生配置透传
|
|
57
|
+
* 基于 AxiosRequestConfig,排除已在 HttpClientConfig 中定义的字段
|
|
58
|
+
*
|
|
59
|
+
* 使用方式:
|
|
60
|
+
* - 直接传 Axios 原生配置对象
|
|
61
|
+
* - 类型安全,支持所有 Axios 配置项
|
|
62
|
+
* - ssl 配置会被自动转换为 httpsAgent
|
|
63
|
+
*/
|
|
64
|
+
export type AxiosNativeConfig = Omit<AxiosRequestConfig, 'baseURL' | 'timeout' | 'proxy' | 'url' | 'method' | 'data' | 'headers' | 'params'> & {
|
|
65
|
+
/**
|
|
66
|
+
* SSL/TLS 证书配置(简化版)
|
|
67
|
+
* 会自动转换为 httpsAgent,优先级高于直接指定的 httpsAgent
|
|
68
|
+
*/
|
|
69
|
+
ssl?: SslCertificateConfig;
|
|
70
|
+
};
|
|
4
71
|
/**
|
|
5
72
|
* HTTP客户端配置接口
|
|
6
73
|
* 基于Spring Boot的@ConfigurationProperties设计理念
|
|
@@ -33,6 +100,12 @@ export interface HttpClientConfig {
|
|
|
33
100
|
/** 是否启用安全验证 */
|
|
34
101
|
enabled?: boolean;
|
|
35
102
|
};
|
|
103
|
+
/**
|
|
104
|
+
* Axios 原生配置透传
|
|
105
|
+
* 用于直接传递 Axios 支持的任何配置项,提供最大灵活性
|
|
106
|
+
* 优先级低于上述显式配置,会进行深度合并
|
|
107
|
+
*/
|
|
108
|
+
axiosConfig?: AxiosNativeConfig;
|
|
36
109
|
}
|
|
37
110
|
/**
|
|
38
111
|
* 重试配置
|
|
@@ -17,6 +17,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
17
17
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18
18
|
});
|
|
19
19
|
};
|
|
20
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
21
|
+
var t = {};
|
|
22
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
23
|
+
t[p] = s[p];
|
|
24
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
25
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
26
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
27
|
+
t[p[i]] = s[p[i]];
|
|
28
|
+
}
|
|
29
|
+
return t;
|
|
30
|
+
};
|
|
20
31
|
var HttpClientService_1;
|
|
21
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
33
|
exports.HttpClientService = void 0;
|
|
@@ -436,14 +447,44 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
436
447
|
* 创建Axios实例并配置axios-retry
|
|
437
448
|
*/
|
|
438
449
|
createAxiosInstance() {
|
|
439
|
-
var _a;
|
|
440
|
-
|
|
450
|
+
var _a, _b, _c;
|
|
451
|
+
// 合并代理配置和 axiosConfig 透传配置
|
|
452
|
+
const resolvedProxy = this.resolveProxyConfig(this.defaultConfig.proxy);
|
|
453
|
+
// 处理 SSL 配置和 httpsAgent
|
|
454
|
+
let httpsAgent = (_a = this.defaultConfig.axiosConfig) === null || _a === void 0 ? void 0 : _a.httpsAgent;
|
|
455
|
+
if ((_b = this.defaultConfig.axiosConfig) === null || _b === void 0 ? void 0 : _b.ssl) {
|
|
456
|
+
const ssl = this.defaultConfig.axiosConfig.ssl;
|
|
457
|
+
const https = require('https');
|
|
458
|
+
if (!httpsAgent) {
|
|
459
|
+
// 如果没有预定义的 httpsAgent,使用 SSL 配置创建
|
|
460
|
+
httpsAgent = new https.Agent(Object.assign(Object.assign({}, ssl), { keepAlive: true }));
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
// 如果同时提供了 httpsAgent 和 ssl 配置
|
|
464
|
+
// ssl 配置会覆盖 httpsAgent 的相关选项
|
|
465
|
+
this.logger.warn('Both httpsAgent and ssl config provided. SSL config will take precedence for certificate options.');
|
|
466
|
+
// 创建新的 Agent,合并原有配置和 SSL 配置
|
|
467
|
+
httpsAgent = new https.Agent(Object.assign({
|
|
468
|
+
// 保留原 Agent 的选项(除了证书相关)
|
|
469
|
+
keepAlive: true }, ssl));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// 构建 axios 创建配置
|
|
473
|
+
const axiosCreateConfig = {
|
|
441
474
|
baseURL: this.defaultConfig.baseURL,
|
|
442
475
|
timeout: this.defaultConfig.timeout,
|
|
443
|
-
proxy:
|
|
444
|
-
|
|
476
|
+
proxy: resolvedProxy !== undefined ? resolvedProxy : undefined,
|
|
477
|
+
// 应用处理后的 httpsAgent
|
|
478
|
+
httpsAgent: httpsAgent,
|
|
479
|
+
};
|
|
480
|
+
// 透传所有其他 axiosConfig 配置(排除已特殊处理的字段)
|
|
481
|
+
if (this.defaultConfig.axiosConfig) {
|
|
482
|
+
const _d = this.defaultConfig.axiosConfig, { ssl, httpsAgent: _ } = _d, restAxiosConfig = __rest(_d, ["ssl", "httpsAgent"]);
|
|
483
|
+
Object.assign(axiosCreateConfig, restAxiosConfig);
|
|
484
|
+
}
|
|
485
|
+
const instance = require('axios').create(axiosCreateConfig);
|
|
445
486
|
// 配置axios-retry
|
|
446
|
-
if ((
|
|
487
|
+
if ((_c = this.defaultConfig.retry) === null || _c === void 0 ? void 0 : _c.enabled) {
|
|
447
488
|
(0, axios_retry_1.default)(instance, {
|
|
448
489
|
retries: this.defaultConfig.retry.retries || 3,
|
|
449
490
|
retryDelay: this.defaultConfig.retry.retryDelay ||
|
|
@@ -15,6 +15,7 @@ class ContextExtractor {
|
|
|
15
15
|
const requestId = providers_1.ContextProvider.getRequestId();
|
|
16
16
|
const authUser = providers_1.ContextProvider.getAuthUser();
|
|
17
17
|
const router = providers_1.ContextProvider.getRouter();
|
|
18
|
+
const source = providers_1.ContextProvider.getSource();
|
|
18
19
|
return {
|
|
19
20
|
requestId,
|
|
20
21
|
userId: authUser === null || authUser === void 0 ? void 0 : authUser.uid,
|
|
@@ -23,6 +24,7 @@ class ContextExtractor {
|
|
|
23
24
|
metadata: {
|
|
24
25
|
authUser,
|
|
25
26
|
router,
|
|
27
|
+
source, // 添加 source 字段
|
|
26
28
|
},
|
|
27
29
|
tags: [],
|
|
28
30
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IP过滤元数据键名
|
|
3
|
+
* 用于在路由元数据中存储IP过滤配置
|
|
4
|
+
*/
|
|
5
|
+
export declare const IP_FILTER_KEY = "ip_filter";
|
|
6
|
+
/**
|
|
7
|
+
* IP过滤选项依赖注入键名
|
|
8
|
+
*/
|
|
9
|
+
export declare const IP_FILTER_OPTIONS = "IP_FILTER_OPTIONS";
|
|
10
|
+
/**
|
|
11
|
+
* 默认错误消息
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_ERROR_MESSAGE = "Access denied: Your IP address is not authorized";
|
|
14
|
+
/**
|
|
15
|
+
* 默认HTTP状态码
|
|
16
|
+
*/
|
|
17
|
+
export declare const DEFAULT_ERROR_STATUS_CODE = 403;
|
|
18
|
+
/**
|
|
19
|
+
* 默认优先级
|
|
20
|
+
*/
|
|
21
|
+
export declare const DEFAULT_PRIORITY = 50;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_PRIORITY = exports.DEFAULT_ERROR_STATUS_CODE = exports.DEFAULT_ERROR_MESSAGE = exports.IP_FILTER_OPTIONS = exports.IP_FILTER_KEY = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* IP过滤元数据键名
|
|
6
|
+
* 用于在路由元数据中存储IP过滤配置
|
|
7
|
+
*/
|
|
8
|
+
exports.IP_FILTER_KEY = 'ip_filter';
|
|
9
|
+
/**
|
|
10
|
+
* IP过滤选项依赖注入键名
|
|
11
|
+
*/
|
|
12
|
+
exports.IP_FILTER_OPTIONS = 'IP_FILTER_OPTIONS';
|
|
13
|
+
/**
|
|
14
|
+
* 默认错误消息
|
|
15
|
+
*/
|
|
16
|
+
exports.DEFAULT_ERROR_MESSAGE = 'Access denied: Your IP address is not authorized';
|
|
17
|
+
/**
|
|
18
|
+
* 默认HTTP状态码
|
|
19
|
+
*/
|
|
20
|
+
exports.DEFAULT_ERROR_STATUS_CODE = 403;
|
|
21
|
+
/**
|
|
22
|
+
* 默认优先级
|
|
23
|
+
*/
|
|
24
|
+
exports.DEFAULT_PRIORITY = 50;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ip-filter.decorator';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./ip-filter.decorator"), exports);
|