@nest-omni/core 4.1.3-20 → 4.1.3-22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/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,92 @@
|
|
|
1
|
+
import { IpFilterModuleOptions, IpRule } from '../interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* IP检查结果
|
|
4
|
+
*/
|
|
5
|
+
export interface IpCheckResult {
|
|
6
|
+
allowed: boolean;
|
|
7
|
+
matchedRule?: string;
|
|
8
|
+
ruleType?: 'whitelist' | 'blacklist';
|
|
9
|
+
reason?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* IP过滤服务(优化版)
|
|
13
|
+
*
|
|
14
|
+
* 功能:
|
|
15
|
+
* - IP 规则匹配
|
|
16
|
+
* - 优先级处理
|
|
17
|
+
* - 黑名单优先检查
|
|
18
|
+
* - 白名单验证
|
|
19
|
+
* - 默认策略应用
|
|
20
|
+
* - 规则热更新
|
|
21
|
+
*/
|
|
22
|
+
export declare class IpFilterService {
|
|
23
|
+
private readonly options?;
|
|
24
|
+
private readonly logger;
|
|
25
|
+
private rules;
|
|
26
|
+
private defaultPolicy;
|
|
27
|
+
constructor(options?: IpFilterModuleOptions);
|
|
28
|
+
/**
|
|
29
|
+
* 检查IP是否被允许访问
|
|
30
|
+
*/
|
|
31
|
+
checkIp(ip: string, routeMetadata?: any): IpCheckResult;
|
|
32
|
+
/**
|
|
33
|
+
* 检查路由级别规则
|
|
34
|
+
*/
|
|
35
|
+
private checkRouteRules;
|
|
36
|
+
/**
|
|
37
|
+
* 检查全局规则
|
|
38
|
+
*/
|
|
39
|
+
private checkGlobalRules;
|
|
40
|
+
/**
|
|
41
|
+
* 更新规则(热更新)
|
|
42
|
+
*
|
|
43
|
+
* @param rules - 新的规则列表
|
|
44
|
+
* @param clearCache - 是否清除缓存(默认: true)
|
|
45
|
+
*/
|
|
46
|
+
updateRules(rules: IpRule[], clearCache?: boolean): void;
|
|
47
|
+
/**
|
|
48
|
+
* 重新加载规则
|
|
49
|
+
*
|
|
50
|
+
* @param newOptions - 新的模块选项
|
|
51
|
+
*/
|
|
52
|
+
reloadRules(newOptions: IpFilterModuleOptions): void;
|
|
53
|
+
/**
|
|
54
|
+
* 添加单个规则
|
|
55
|
+
*
|
|
56
|
+
* @param rule - 要添加的规则
|
|
57
|
+
*/
|
|
58
|
+
addRule(rule: IpRule): void;
|
|
59
|
+
/**
|
|
60
|
+
* 删除规则
|
|
61
|
+
*
|
|
62
|
+
* @param ruleId - 要删除的规则ID
|
|
63
|
+
*/
|
|
64
|
+
removeRule(ruleId: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* 启用/禁用规则
|
|
67
|
+
*
|
|
68
|
+
* @param ruleId - 规则ID
|
|
69
|
+
* @param enabled - 是否启用
|
|
70
|
+
*/
|
|
71
|
+
toggleRule(ruleId: string, enabled: boolean): void;
|
|
72
|
+
/**
|
|
73
|
+
* 获取所有规则
|
|
74
|
+
*/
|
|
75
|
+
getRules(): IpRule[];
|
|
76
|
+
/**
|
|
77
|
+
* 获取规则统计
|
|
78
|
+
*/
|
|
79
|
+
getRulesStats(): {
|
|
80
|
+
total: number;
|
|
81
|
+
enabled: number;
|
|
82
|
+
disabled: number;
|
|
83
|
+
whitelist: number;
|
|
84
|
+
blacklist: number;
|
|
85
|
+
};
|
|
86
|
+
private logBlocked;
|
|
87
|
+
private logAllowed;
|
|
88
|
+
getErrorResponse(): {
|
|
89
|
+
message: string;
|
|
90
|
+
statusCode: number;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
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 IpFilterService_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.IpFilterService = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const utils_1 = require("../utils");
|
|
16
|
+
/**
|
|
17
|
+
* 默认常量
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_CONSTANTS = {
|
|
20
|
+
DEFAULT_PRIORITY: 50,
|
|
21
|
+
DEFAULT_ERROR_MESSAGE: 'Access denied',
|
|
22
|
+
DEFAULT_ERROR_STATUS_CODE: 403,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* IP过滤服务(优化版)
|
|
26
|
+
*
|
|
27
|
+
* 功能:
|
|
28
|
+
* - IP 规则匹配
|
|
29
|
+
* - 优先级处理
|
|
30
|
+
* - 黑名单优先检查
|
|
31
|
+
* - 白名单验证
|
|
32
|
+
* - 默认策略应用
|
|
33
|
+
* - 规则热更新
|
|
34
|
+
*/
|
|
35
|
+
let IpFilterService = IpFilterService_1 = class IpFilterService {
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this.options = options;
|
|
38
|
+
this.logger = new common_1.Logger(IpFilterService_1.name);
|
|
39
|
+
this.rules = [];
|
|
40
|
+
this.defaultPolicy = 'allow';
|
|
41
|
+
if (options === null || options === void 0 ? void 0 : options.rules) {
|
|
42
|
+
this.rules = [...options.rules];
|
|
43
|
+
}
|
|
44
|
+
if (options === null || options === void 0 ? void 0 : options.defaultPolicy) {
|
|
45
|
+
this.defaultPolicy = options.defaultPolicy;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 检查IP是否被允许访问
|
|
50
|
+
*/
|
|
51
|
+
checkIp(ip, routeMetadata) {
|
|
52
|
+
var _a;
|
|
53
|
+
const normalizedIp = utils_1.IpUtils.normalizeIp(ip);
|
|
54
|
+
if (!utils_1.IpUtils.isValidIp(normalizedIp)) {
|
|
55
|
+
return { allowed: false, reason: 'Invalid IP address format' };
|
|
56
|
+
}
|
|
57
|
+
// 路由级别规则优先
|
|
58
|
+
if ((routeMetadata === null || routeMetadata === void 0 ? void 0 : routeMetadata.enabled) !== false && ((_a = routeMetadata === null || routeMetadata === void 0 ? void 0 : routeMetadata.ipRanges) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
59
|
+
return this.checkRouteRules(normalizedIp, routeMetadata);
|
|
60
|
+
}
|
|
61
|
+
// 全局规则
|
|
62
|
+
return this.checkGlobalRules(normalizedIp);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 检查路由级别规则
|
|
66
|
+
*/
|
|
67
|
+
checkRouteRules(ip, metadata) {
|
|
68
|
+
const { mode, ipRanges } = metadata;
|
|
69
|
+
const matched = utils_1.IpUtils.matchAnyCidr(ip, ipRanges);
|
|
70
|
+
if (mode === 'whitelist') {
|
|
71
|
+
return {
|
|
72
|
+
allowed: matched,
|
|
73
|
+
ruleType: 'whitelist',
|
|
74
|
+
reason: matched ? 'IP in route whitelist' : 'IP not in route whitelist',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// blacklist
|
|
78
|
+
return {
|
|
79
|
+
allowed: !matched,
|
|
80
|
+
ruleType: matched ? 'blacklist' : undefined,
|
|
81
|
+
reason: matched ? 'IP in route blacklist' : 'IP not in route blacklist',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 检查全局规则
|
|
86
|
+
*/
|
|
87
|
+
checkGlobalRules(ip) {
|
|
88
|
+
var _a;
|
|
89
|
+
if (!((_a = this.rules) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
90
|
+
return {
|
|
91
|
+
allowed: this.defaultPolicy === 'allow',
|
|
92
|
+
reason: 'No rules configured',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// 按优先级排序:黑名单优先
|
|
96
|
+
const sortedRules = [...this.rules]
|
|
97
|
+
.filter(r => r.enabled !== false)
|
|
98
|
+
.sort((a, b) => (b.priority || DEFAULT_CONSTANTS.DEFAULT_PRIORITY) - (a.priority || DEFAULT_CONSTANTS.DEFAULT_PRIORITY));
|
|
99
|
+
// 黑名单优先检查
|
|
100
|
+
for (const rule of sortedRules.filter(r => r.type === 'blacklist')) {
|
|
101
|
+
if (utils_1.IpUtils.matchAnyCidr(ip, rule.ipRanges)) {
|
|
102
|
+
this.logBlocked(ip, rule);
|
|
103
|
+
return { allowed: false, matchedRule: rule.id, ruleType: 'blacklist' };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// 白名单
|
|
107
|
+
const whitelistRules = sortedRules.filter(r => r.type === 'whitelist');
|
|
108
|
+
if (whitelistRules.length) {
|
|
109
|
+
for (const rule of whitelistRules) {
|
|
110
|
+
if (utils_1.IpUtils.matchAnyCidr(ip, rule.ipRanges)) {
|
|
111
|
+
this.logAllowed(ip, rule);
|
|
112
|
+
return { allowed: true, matchedRule: rule.id, ruleType: 'whitelist' };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// 有白名单但未匹配
|
|
116
|
+
return { allowed: false, reason: 'IP not in any whitelist' };
|
|
117
|
+
}
|
|
118
|
+
// 默认策略
|
|
119
|
+
return {
|
|
120
|
+
allowed: this.defaultPolicy === 'allow',
|
|
121
|
+
reason: `Using default policy: ${this.defaultPolicy}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 更新规则(热更新)
|
|
126
|
+
*
|
|
127
|
+
* @param rules - 新的规则列表
|
|
128
|
+
* @param clearCache - 是否清除缓存(默认: true)
|
|
129
|
+
*/
|
|
130
|
+
updateRules(rules, clearCache = true) {
|
|
131
|
+
this.rules = [...rules];
|
|
132
|
+
if (clearCache) {
|
|
133
|
+
utils_1.IpUtils.clearCache();
|
|
134
|
+
this.logger.log(`Rules updated and cache cleared. Total rules: ${rules.length}`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this.logger.log(`Rules updated. Total rules: ${rules.length}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 重新加载规则
|
|
142
|
+
*
|
|
143
|
+
* @param newOptions - 新的模块选项
|
|
144
|
+
*/
|
|
145
|
+
reloadRules(newOptions) {
|
|
146
|
+
if (newOptions.rules) {
|
|
147
|
+
this.updateRules(newOptions.rules);
|
|
148
|
+
}
|
|
149
|
+
if (newOptions.defaultPolicy) {
|
|
150
|
+
this.defaultPolicy = newOptions.defaultPolicy;
|
|
151
|
+
}
|
|
152
|
+
this.logger.log('Rules reloaded successfully');
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 添加单个规则
|
|
156
|
+
*
|
|
157
|
+
* @param rule - 要添加的规则
|
|
158
|
+
*/
|
|
159
|
+
addRule(rule) {
|
|
160
|
+
this.rules.push(rule);
|
|
161
|
+
utils_1.IpUtils.clearCache();
|
|
162
|
+
this.logger.log(`Rule added: ${rule.id}`);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 删除规则
|
|
166
|
+
*
|
|
167
|
+
* @param ruleId - 要删除的规则ID
|
|
168
|
+
*/
|
|
169
|
+
removeRule(ruleId) {
|
|
170
|
+
const index = this.rules.findIndex(r => r.id === ruleId);
|
|
171
|
+
if (index !== -1) {
|
|
172
|
+
this.rules.splice(index, 1);
|
|
173
|
+
utils_1.IpUtils.clearCache();
|
|
174
|
+
this.logger.log(`Rule removed: ${ruleId}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 启用/禁用规则
|
|
179
|
+
*
|
|
180
|
+
* @param ruleId - 规则ID
|
|
181
|
+
* @param enabled - 是否启用
|
|
182
|
+
*/
|
|
183
|
+
toggleRule(ruleId, enabled) {
|
|
184
|
+
const rule = this.rules.find(r => r.id === ruleId);
|
|
185
|
+
if (rule) {
|
|
186
|
+
rule.enabled = enabled;
|
|
187
|
+
utils_1.IpUtils.clearCache();
|
|
188
|
+
this.logger.log(`Rule ${ruleId} ${enabled ? 'enabled' : 'disabled'}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 获取所有规则
|
|
193
|
+
*/
|
|
194
|
+
getRules() {
|
|
195
|
+
return [...this.rules];
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 获取规则统计
|
|
199
|
+
*/
|
|
200
|
+
getRulesStats() {
|
|
201
|
+
const total = this.rules.length;
|
|
202
|
+
const enabled = this.rules.filter(r => r.enabled !== false).length;
|
|
203
|
+
const disabled = total - enabled;
|
|
204
|
+
const whitelist = this.rules.filter(r => r.type === 'whitelist').length;
|
|
205
|
+
const blacklist = this.rules.filter(r => r.type === 'blacklist').length;
|
|
206
|
+
return {
|
|
207
|
+
total,
|
|
208
|
+
enabled,
|
|
209
|
+
disabled,
|
|
210
|
+
whitelist,
|
|
211
|
+
blacklist,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
logBlocked(ip, rule) {
|
|
215
|
+
var _a;
|
|
216
|
+
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.enableLogging) {
|
|
217
|
+
this.logger.warn(`IP blocked: ${ip} - Rule: ${rule.id}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
logAllowed(ip, rule) {
|
|
221
|
+
var _a;
|
|
222
|
+
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.enableLogging) {
|
|
223
|
+
this.logger.log(`IP allowed: ${ip} - Rule: ${rule.id}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
getErrorResponse() {
|
|
227
|
+
var _a, _b, _c, _d;
|
|
228
|
+
return {
|
|
229
|
+
message: ((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.errorResponse) === null || _b === void 0 ? void 0 : _b.message) || DEFAULT_CONSTANTS.DEFAULT_ERROR_MESSAGE,
|
|
230
|
+
statusCode: ((_d = (_c = this.options) === null || _c === void 0 ? void 0 : _c.errorResponse) === null || _d === void 0 ? void 0 : _d.statusCode) || DEFAULT_CONSTANTS.DEFAULT_ERROR_STATUS_CODE,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
exports.IpFilterService = IpFilterService;
|
|
235
|
+
exports.IpFilterService = IpFilterService = IpFilterService_1 = __decorate([
|
|
236
|
+
(0, common_1.Injectable)(),
|
|
237
|
+
__metadata("design:paramtypes", [Object])
|
|
238
|
+
], IpFilterService);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ip-utils';
|
|
@@ -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-utils"), exports);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IP匹配结果
|
|
3
|
+
*/
|
|
4
|
+
export interface IpMatchResult {
|
|
5
|
+
matched: boolean;
|
|
6
|
+
ruleId?: string;
|
|
7
|
+
ruleType?: 'whitelist' | 'blacklist';
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* IP工具类(优化版)
|
|
11
|
+
* 提供IP地址匹配和验证功能,带LRU缓存优化
|
|
12
|
+
*/
|
|
13
|
+
export declare class IpUtils {
|
|
14
|
+
private static cidrCache;
|
|
15
|
+
private static validationCache;
|
|
16
|
+
/**
|
|
17
|
+
* 检查IP地址是否匹配CIDR范围(带缓存)
|
|
18
|
+
*/
|
|
19
|
+
static matchCidr(ip: string, cidr: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* 实际执行CIDR匹配
|
|
22
|
+
*/
|
|
23
|
+
private static doMatchCidr;
|
|
24
|
+
/**
|
|
25
|
+
* 检查IP地址是否在任意一个CIDR范围内(短路优化)
|
|
26
|
+
*/
|
|
27
|
+
static matchAnyCidr(ip: string, cidrList: string[]): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* 验证IP地址格式是否有效(带缓存)
|
|
30
|
+
*/
|
|
31
|
+
static isValidIp(ip: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* 验证IP地址格式是否有效(同步版本)
|
|
34
|
+
*/
|
|
35
|
+
static isValidIpSync(ip: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* 标准化IP地址(优化版)
|
|
38
|
+
*/
|
|
39
|
+
static normalizeIp(ip: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* 获取IP版本
|
|
42
|
+
*/
|
|
43
|
+
static getIpVersion(ip: string): 'IPv4' | 'IPv6' | 'unknown';
|
|
44
|
+
/**
|
|
45
|
+
* 清空缓存(用于测试或规则变更)
|
|
46
|
+
*/
|
|
47
|
+
static clearCache(): void;
|
|
48
|
+
/**
|
|
49
|
+
* 获取缓存统计
|
|
50
|
+
*/
|
|
51
|
+
static getCacheStats(): {
|
|
52
|
+
cidr: {
|
|
53
|
+
size: number;
|
|
54
|
+
maxSize: number;
|
|
55
|
+
};
|
|
56
|
+
validation: {
|
|
57
|
+
size: number;
|
|
58
|
+
maxSize: number;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IpUtils = void 0;
|
|
4
|
+
const Address = require("ip-address");
|
|
5
|
+
const lru_cache_1 = require("lru-cache");
|
|
6
|
+
/**
|
|
7
|
+
* 缓存常量
|
|
8
|
+
*/
|
|
9
|
+
const CACHE_CONSTANTS = {
|
|
10
|
+
CIDR_CACHE_SIZE: 1000,
|
|
11
|
+
VALIDATION_CACHE_SIZE: 500,
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* IP工具类(优化版)
|
|
15
|
+
* 提供IP地址匹配和验证功能,带LRU缓存优化
|
|
16
|
+
*/
|
|
17
|
+
class IpUtils {
|
|
18
|
+
/**
|
|
19
|
+
* 检查IP地址是否匹配CIDR范围(带缓存)
|
|
20
|
+
*/
|
|
21
|
+
static matchCidr(ip, cidr) {
|
|
22
|
+
const cacheKey = `${ip}:${cidr}`;
|
|
23
|
+
const cached = this.cidrCache.get(cacheKey);
|
|
24
|
+
if (cached !== undefined) {
|
|
25
|
+
return cached;
|
|
26
|
+
}
|
|
27
|
+
const result = this.doMatchCidr(ip, cidr);
|
|
28
|
+
this.cidrCache.set(cacheKey, result);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 实际执行CIDR匹配
|
|
33
|
+
*/
|
|
34
|
+
static doMatchCidr(ip, cidr) {
|
|
35
|
+
try {
|
|
36
|
+
// 快速路径:单个IP精确匹配
|
|
37
|
+
if (!cidr.includes('/')) {
|
|
38
|
+
return ip === cidr;
|
|
39
|
+
}
|
|
40
|
+
// IPv4快速路径
|
|
41
|
+
if (!ip.includes(':') && !cidr.includes(':')) {
|
|
42
|
+
const ipv4Addr = new Address.Address4(ip);
|
|
43
|
+
const ipv4Cidr = new Address.Address4(cidr);
|
|
44
|
+
// 注意:参数顺序是 address.isInSubnet(cidr)
|
|
45
|
+
return ipv4Addr.isInSubnet(ipv4Cidr);
|
|
46
|
+
}
|
|
47
|
+
// IPv6路径
|
|
48
|
+
const addr = new Address.Address6(ip);
|
|
49
|
+
const cidrBlock = new Address.Address6(cidr);
|
|
50
|
+
return addr.isInSubnet(cidrBlock);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
// 记录错误日志但不抛出异常
|
|
54
|
+
console.warn(`CIDR match failed for ${ip} in ${cidr}:`, error);
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 检查IP地址是否在任意一个CIDR范围内(短路优化)
|
|
60
|
+
*/
|
|
61
|
+
static matchAnyCidr(ip, cidrList) {
|
|
62
|
+
if (!(cidrList === null || cidrList === void 0 ? void 0 : cidrList.length))
|
|
63
|
+
return false;
|
|
64
|
+
return cidrList.some(cidr => this.matchCidr(ip, cidr));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 验证IP地址格式是否有效(带缓存)
|
|
68
|
+
*/
|
|
69
|
+
static isValidIp(ip) {
|
|
70
|
+
if (!ip || typeof ip !== 'string')
|
|
71
|
+
return false;
|
|
72
|
+
const cached = this.validationCache.get(ip);
|
|
73
|
+
if (cached !== undefined) {
|
|
74
|
+
return cached;
|
|
75
|
+
}
|
|
76
|
+
const result = this.isValidIpSync(ip);
|
|
77
|
+
this.validationCache.set(ip, result);
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 验证IP地址格式是否有效(同步版本)
|
|
82
|
+
*/
|
|
83
|
+
static isValidIpSync(ip) {
|
|
84
|
+
if (!ip || typeof ip !== 'string')
|
|
85
|
+
return false;
|
|
86
|
+
return ip.includes(':')
|
|
87
|
+
? Address.Address6.isValid(ip)
|
|
88
|
+
: Address.Address4.isValid(ip);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 标准化IP地址(优化版)
|
|
92
|
+
*/
|
|
93
|
+
static normalizeIp(ip) {
|
|
94
|
+
if (!ip)
|
|
95
|
+
return '';
|
|
96
|
+
// 处理 IPv6 映射的 IPv4
|
|
97
|
+
if (ip.startsWith('::ffff:')) {
|
|
98
|
+
return ip.substring(7);
|
|
99
|
+
}
|
|
100
|
+
// 检查是否是 IPv6 地址(包含多个冒号或包含 ::)
|
|
101
|
+
const ipv6Pattern = /:.*:/;
|
|
102
|
+
if (ipv6Pattern.test(ip)) {
|
|
103
|
+
// 纯 IPv6 地址,保持不变
|
|
104
|
+
return ip;
|
|
105
|
+
}
|
|
106
|
+
// IPv4 地址(可能带端口号)
|
|
107
|
+
// 移除端口号
|
|
108
|
+
const lastColonIndex = ip.lastIndexOf(':');
|
|
109
|
+
if (lastColonIndex > 0) {
|
|
110
|
+
// 可能是 IPv4:port 格式
|
|
111
|
+
const potentialPort = ip.substring(lastColonIndex + 1);
|
|
112
|
+
if (/^\d+$/.test(potentialPort)) {
|
|
113
|
+
// 确认是端口号,返回 IPv4 部分
|
|
114
|
+
return ip.substring(0, lastColonIndex);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return ip;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 获取IP版本
|
|
121
|
+
*/
|
|
122
|
+
static getIpVersion(ip) {
|
|
123
|
+
if (!ip)
|
|
124
|
+
return 'unknown';
|
|
125
|
+
if (ip.includes(':'))
|
|
126
|
+
return 'IPv6';
|
|
127
|
+
if (this.isValidIpSync(ip))
|
|
128
|
+
return 'IPv4';
|
|
129
|
+
return 'unknown';
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 清空缓存(用于测试或规则变更)
|
|
133
|
+
*/
|
|
134
|
+
static clearCache() {
|
|
135
|
+
this.cidrCache.clear();
|
|
136
|
+
this.validationCache.clear();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 获取缓存统计
|
|
140
|
+
*/
|
|
141
|
+
static getCacheStats() {
|
|
142
|
+
return {
|
|
143
|
+
cidr: {
|
|
144
|
+
size: this.cidrCache.size,
|
|
145
|
+
maxSize: this.cidrCache.max,
|
|
146
|
+
},
|
|
147
|
+
validation: {
|
|
148
|
+
size: this.validationCache.size,
|
|
149
|
+
maxSize: this.validationCache.max,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.IpUtils = IpUtils;
|
|
155
|
+
// CIDR匹配缓存(使用 lru-cache 库)
|
|
156
|
+
IpUtils.cidrCache = new lru_cache_1.LRUCache({
|
|
157
|
+
max: CACHE_CONSTANTS.CIDR_CACHE_SIZE,
|
|
158
|
+
});
|
|
159
|
+
// IP验证缓存
|
|
160
|
+
IpUtils.validationCache = new lru_cache_1.LRUCache({
|
|
161
|
+
max: CACHE_CONSTANTS.VALIDATION_CACHE_SIZE,
|
|
162
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nest-omni/core",
|
|
3
|
-
"version": "4.1.3-
|
|
3
|
+
"version": "4.1.3-22",
|
|
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",
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/compression": "^1.8.1",
|
|
44
|
-
"@types/express": "^5.0.
|
|
44
|
+
"@types/express": "^5.0.6",
|
|
45
45
|
"@types/express-session": "^1.18.2",
|
|
46
|
-
"@types/lodash": "^4.17.
|
|
47
|
-
"@types/node": "^
|
|
48
|
-
"@types/sharp": "^0.
|
|
46
|
+
"@types/lodash": "^4.17.21",
|
|
47
|
+
"@types/node": "^25.0.3",
|
|
48
|
+
"@types/sharp": "^0.32.0",
|
|
49
49
|
"@types/sprintf-js": "^1.1.4",
|
|
50
50
|
"@types/uuid": "^11.0.0",
|
|
51
51
|
"sqlite3": "^5.1.7",
|
|
@@ -56,49 +56,48 @@
|
|
|
56
56
|
"@dataui/crud-typeorm": "^5.3.4",
|
|
57
57
|
"@nest-omni/transaction": "*",
|
|
58
58
|
"@nestjs/bull": "^11.0.4",
|
|
59
|
-
"@nestjs/common": "^11.1.
|
|
59
|
+
"@nestjs/common": "^11.1.11",
|
|
60
60
|
"@nestjs/config": "^4.0.2",
|
|
61
|
-
"@nestjs/core": "^11.1.
|
|
62
|
-
"@nestjs/platform-express": "^11.1.
|
|
63
|
-
"@nestjs/schedule": "^6.0
|
|
64
|
-
"@nestjs/swagger": "^11.2.
|
|
61
|
+
"@nestjs/core": "^11.1.11",
|
|
62
|
+
"@nestjs/platform-express": "^11.1.11",
|
|
63
|
+
"@nestjs/schedule": "^6.1.0",
|
|
64
|
+
"@nestjs/swagger": "^11.2.4",
|
|
65
65
|
"@nestjs/terminus": "^11.0.0",
|
|
66
66
|
"@nestjs/typeorm": "^11.0.0",
|
|
67
|
-
"@sentry/nestjs": "^10.
|
|
68
|
-
"@sentry/profiling-node": "^10.
|
|
67
|
+
"@sentry/nestjs": "^10.32.1",
|
|
68
|
+
"@sentry/profiling-node": "^10.32.1",
|
|
69
69
|
"@songkeys/nestjs-redis": "^11.0.0",
|
|
70
70
|
"@songkeys/nestjs-redis-health": "^11.0.0",
|
|
71
71
|
"axios": "^1.13.2",
|
|
72
72
|
"axios-retry": "^4.5.0",
|
|
73
73
|
"bcrypt": "^6.0.0",
|
|
74
|
-
"body-parser": "^2.2.
|
|
74
|
+
"body-parser": "^2.2.1",
|
|
75
75
|
"bull": "^4.16.5",
|
|
76
76
|
"class-transformer": "^0.5.1",
|
|
77
|
-
"class-validator": "^0.14.
|
|
77
|
+
"class-validator": "^0.14.3",
|
|
78
78
|
"compression": "^1.8.1",
|
|
79
79
|
"connect-redis": "^9.0.0",
|
|
80
80
|
"dotenv": "^17.2.3",
|
|
81
|
-
"express": "^5.1
|
|
81
|
+
"express": "^5.2.1",
|
|
82
82
|
"express-session": "^1.18.2",
|
|
83
83
|
"fast-csv": "^5.0.5",
|
|
84
84
|
"hygen": "^6.2.11",
|
|
85
|
-
"ioredis": "^5.
|
|
86
|
-
"libphonenumber-js": "^1.12.25",
|
|
85
|
+
"ioredis": "^5.9.0",
|
|
87
86
|
"lodash": "^4.17.21",
|
|
88
|
-
"mime-types": "^3.0.
|
|
87
|
+
"mime-types": "^3.0.2",
|
|
89
88
|
"moment": "^2.30.1",
|
|
90
|
-
"mysql2": "^3.
|
|
91
|
-
"nestjs-cls": "^6.0
|
|
92
|
-
"nestjs-i18n": "^10.
|
|
93
|
-
"nestjs-pino": "^4.
|
|
89
|
+
"mysql2": "^3.16.0",
|
|
90
|
+
"nestjs-cls": "^6.2.0",
|
|
91
|
+
"nestjs-i18n": "^10.6.0",
|
|
92
|
+
"nestjs-pino": "^4.5.0",
|
|
94
93
|
"node-vault": "^0.10.9",
|
|
95
94
|
"pino-http": "^11.0.0",
|
|
96
|
-
"pino-pretty": "^13.1.
|
|
95
|
+
"pino-pretty": "^13.1.3",
|
|
97
96
|
"reflect-metadata": "^0.2.2",
|
|
98
97
|
"rxjs": "^7.8.2",
|
|
99
98
|
"source-map-support": "^0.5.21",
|
|
100
99
|
"sprintf-js": "^1.1.3",
|
|
101
|
-
"typeorm": "^0.3.
|
|
100
|
+
"typeorm": "^0.3.28",
|
|
102
101
|
"uuid": "^13.0.0",
|
|
103
102
|
"xlsx": "git+https://git.sheetjs.com/sheetjs/sheetjs.git#v0.20.3"
|
|
104
103
|
},
|
|
@@ -3,6 +3,7 @@ export declare class ContextProvider {
|
|
|
3
3
|
private static readonly nameSpace;
|
|
4
4
|
private static readonly authUserKey;
|
|
5
5
|
private static readonly routerKey;
|
|
6
|
+
private static readonly sourceKey;
|
|
6
7
|
private static readonly languageKey;
|
|
7
8
|
private static readonly requestKey;
|
|
8
9
|
static get<T>(key: string): import("nestjs-cls/dist/src/types/type-if-type.type").TypeIfUndefined<T, import("nestjs-cls").ClsStore, T>;
|
|
@@ -31,4 +32,12 @@ export declare class ContextProvider {
|
|
|
31
32
|
static getRole(): string;
|
|
32
33
|
static getAdmin(): boolean;
|
|
33
34
|
static getTenantId(): string;
|
|
35
|
+
/**
|
|
36
|
+
* 设置请求来源(如业务主键ID等)
|
|
37
|
+
*/
|
|
38
|
+
static setSource(source: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* 获取请求来源
|
|
41
|
+
*/
|
|
42
|
+
static getSource(): string | undefined;
|
|
34
43
|
}
|