mm_ip 1.0.2 → 1.0.4

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/index.js CHANGED
@@ -9,50 +9,62 @@ require('mm_expand');
9
9
  * @class
10
10
  */
11
11
  class Ip {
12
- /**
13
- * 创建IP管理实例
14
- * @param {Object} config - 配置选项
15
- */
16
- constructor(config) {
17
- this._config = Object.assign({
18
- max_white: 1000,
19
- max_black: 1000,
20
- check_interval: 60000, // 1分钟
21
- max_req_per_min: 100,
22
- auto_black_enable: true, // 启用自动高频拉黑
23
- auto_black_threshold: 50, // 自动拉黑阈值(每分钟请求数)
24
- auto_black_duration: 3600000, // 自动拉黑持续时间(1小时)
25
- violation_enable: true, // 启用违规记录
26
- violation_max_count: 10, // 最大违规次数
27
- violation_reset_time: 86400000, // 违规记录重置时间(24小时)
28
- violation_auto_black: true, // 违规次数超限自动拉黑
29
- dir: './cache/ip' // 持久化存储目录
30
- }, config || {});
31
-
32
- this._white = new Set();
33
- this._black = new Set();
34
- this._req_count = new Map();
35
- this._req_time = new Map();
36
- this._black_time = new Map(); // 记录拉黑时间
37
- this._violation_count = new Map(); // 违规次数记录
38
- this._violation_time = new Map(); // 违规时间记录
39
- this._logger = $.log || console;
40
-
41
- // 自动加载持久化数据
42
- if (this._config.dir) {
43
- this.load();
44
- }
45
- }
12
+ static config = {
13
+ max_white: 1000,
14
+ max_black: 1000,
15
+ check_interval: 60000, // 1分钟
16
+ max_req_per_min: 100,
17
+ auto_black_enable: true, // 启用自动高频拉黑
18
+ auto_black_threshold: 50, // 自动拉黑阈值(每分钟请求数)
19
+ auto_black_duration: 3600000, // 自动拉黑持续时间(1小时)
20
+ violation_enable: true, // 启用违规记录
21
+ violation_max_count: 10, // 最大违规次数
22
+ violation_reset_time: 86400000, // 违规记录重置时间(24小时)
23
+ violation_auto_black: true, // 违规次数超限自动拉黑
24
+ dir: './cache/ip' // 持久化存储目录
25
+ }
26
+ /**
27
+ * 创建IP管理实例
28
+ * @param {Object} config - 配置选项
29
+ */
30
+ constructor(config) {
31
+ this._config = Object.assign({}, Ip.config);
32
+
33
+ this._white = new Set();
34
+ this._black = new Set();
35
+ this._req_count = new Map();
36
+ this._req_time = new Map();
37
+ this._black_time = new Map(); // 记录拉黑时间
38
+ this._violation_count = new Map(); // 违规次数记录
39
+ this._violation_time = new Map(); // 违规时间记录
40
+ this._logger = $.log || console;
41
+
42
+ this.setConfig(config);
43
+ // 自动加载持久化数据
44
+ if (this._config.dir) {
45
+ this.load();
46
+ }
47
+ }
48
+ }
49
+
50
+ /**
51
+ * 设置配置选项
52
+ * @param {Object} config - 配置选项
53
+ */
54
+ Ip.prototype.setConfig = function(config) {
55
+ if (config) {
56
+ Object.assign(this._config, config);
57
+ }
46
58
  }
47
59
 
48
60
  /**
49
61
  * 设置日志器
50
62
  * @param {Object} logger - 日志对象
51
63
  */
52
- Ip.prototype.setup = function (logger) {
53
- if (logger && typeof logger === 'object') {
54
- this._logger = logger;
55
- }
64
+ Ip.prototype.init = function(logger) {
65
+ if (logger) {
66
+ this._logger = logger;
67
+ }
56
68
  };
57
69
 
58
70
  /**
@@ -61,8 +73,8 @@ Ip.prototype.setup = function (logger) {
61
73
  * @param {string} message - 日志消息
62
74
  * @param {...*} args - 可选的日志参数
63
75
  */
64
- Ip.prototype.logger = function (level, message, ...args) {
65
- this._logger[level](`${this.constructor.name} ${message}`, ...args);
76
+ Ip.prototype.log = function(level, message, ...args) {
77
+ this._logger[level](`${this.constructor.name} ${message}`, ...args);
66
78
  };
67
79
 
68
80
  /**
@@ -71,23 +83,22 @@ Ip.prototype.logger = function (level, message, ...args) {
71
83
  * @returns {boolean} 是否添加成功
72
84
  * @throws {TypeError} 当IP非字符串时
73
85
  */
74
- Ip.prototype.addWhite = function (ip) {
75
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
86
+ Ip.prototype.addWhite = function(ip) {
87
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
76
88
 
77
- if (this.getAllWhite().length >= this._config.max_white) {
78
- this.logger('warn', '白名单已满,无法添加IP:', ip);
79
- return false;
80
- }
89
+ if (this.getAllWhite().length >= this._config.max_white) {
90
+ this.log('warn', '白名单已满,无法添加IP:', ip);
91
+ return false;
92
+ }
81
93
 
82
- this._white.add(ip);
83
- this.logger('info', '添加白名单IP:', ip);
84
-
85
- // 自动保存到持久化存储
86
- if (this._config.dir) {
87
- this.save();
88
- }
89
-
90
- return true;
94
+ this._white.add(ip);
95
+
96
+ // 自动保存到持久化存储
97
+ if (this._config.dir) {
98
+ this.save();
99
+ }
100
+
101
+ return true;
91
102
  };
92
103
 
93
104
  /**
@@ -96,23 +107,23 @@ Ip.prototype.addWhite = function (ip) {
96
107
  * @returns {boolean} 是否添加成功
97
108
  * @throws {TypeError} 当IP非字符串时
98
109
  */
99
- Ip.prototype.addBlack = function (ip) {
100
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
110
+ Ip.prototype.addBlack = function(ip) {
111
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
112
+
113
+ if (this.getAllBlack().length >= this._config.max_black) {
114
+ this.log('warn', '黑名单已满,无法添加IP:', ip);
115
+ return false;
116
+ }
101
117
 
102
- if (this.getAllBlack().length >= this._config.max_black) {
103
- this.logger('warn', '黑名单已满,无法添加IP:', ip);
104
- return false;
105
- }
118
+ this._black.add(ip);
119
+ this.log('info', '添加黑名单IP:', ip);
106
120
 
107
- this._black.add(ip);
108
- this.logger('info', '添加黑名单IP:', ip);
109
-
110
- // 自动保存到持久化存储
111
- if (this._config.dir) {
112
- this.save();
113
- }
114
-
115
- return true;
121
+ // 自动保存到持久化存储
122
+ if (this._config.dir) {
123
+ this.save();
124
+ }
125
+
126
+ return true;
116
127
  };
117
128
 
118
129
  /**
@@ -121,19 +132,19 @@ Ip.prototype.addBlack = function (ip) {
121
132
  * @returns {boolean} 是否删除成功
122
133
  * @throws {TypeError} 当IP非字符串时
123
134
  */
124
- Ip.prototype.delWhite = function (ip) {
125
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
135
+ Ip.prototype.delWhite = function(ip) {
136
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
137
+
138
+ const result = this._white.delete(ip);
139
+ if (result) {
140
+ this.log('info', '删除白名单IP:', ip);
126
141
 
127
- const result = this._white.delete(ip);
128
- if (result) {
129
- this.logger('info', '删除白名单IP:', ip);
130
-
131
- // 自动保存到持久化存储
132
- if (this._config.dir) {
133
- this.save();
134
- }
135
- }
136
- return result;
142
+ // 自动保存到持久化存储
143
+ if (this._config.dir) {
144
+ this.save();
145
+ }
146
+ }
147
+ return result;
137
148
  };
138
149
 
139
150
  /**
@@ -142,19 +153,19 @@ Ip.prototype.delWhite = function (ip) {
142
153
  * @returns {boolean} 是否删除成功
143
154
  * @throws {TypeError} 当IP非字符串时
144
155
  */
145
- Ip.prototype.delBlack = function (ip) {
146
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
156
+ Ip.prototype.delBlack = function(ip) {
157
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
158
+
159
+ const result = this._black.delete(ip);
160
+ if (result) {
161
+ this.log('info', '删除黑名单IP:', ip);
147
162
 
148
- const result = this._black.delete(ip);
149
- if (result) {
150
- this.logger('info', '删除黑名单IP:', ip);
151
-
152
- // 自动保存到持久化存储
153
- if (this._config.dir) {
154
- this.save();
155
- }
156
- }
157
- return result;
163
+ // 自动保存到持久化存储
164
+ if (this._config.dir) {
165
+ this.save();
166
+ }
167
+ }
168
+ return result;
158
169
  };
159
170
 
160
171
  /**
@@ -163,9 +174,9 @@ Ip.prototype.delBlack = function (ip) {
163
174
  * @returns {boolean} 是否在白名单
164
175
  * @throws {TypeError} 当IP非字符串时
165
176
  */
166
- Ip.prototype.isWhite = function (ip) {
167
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
168
- return this._white.has(ip);
177
+ Ip.prototype.isWhite = function(ip) {
178
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
179
+ return this._white.has(ip);
169
180
  };
170
181
 
171
182
  /**
@@ -174,9 +185,9 @@ Ip.prototype.isWhite = function (ip) {
174
185
  * @returns {boolean} 是否在黑名单
175
186
  * @throws {TypeError} 当IP非字符串时
176
187
  */
177
- Ip.prototype.isBlack = function (ip) {
178
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
179
- return this._black.has(ip);
188
+ Ip.prototype.isBlack = function(ip) {
189
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
190
+ return this._black.has(ip);
180
191
  };
181
192
 
182
193
  /**
@@ -185,54 +196,53 @@ Ip.prototype.isBlack = function (ip) {
185
196
  * @returns {boolean} 是否允许请求
186
197
  * @throws {TypeError} 当IP非字符串时
187
198
  */
188
- Ip.prototype.record = function (ip) {
189
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
190
-
191
- // 检查黑名单(包括过期检查)
192
- if (this.isBlack(ip)) {
193
- // 检查是否过期
194
- if (this._isBlackExpired(ip)) {
195
- this.delBlack(ip);
196
- this.logger('info', '黑名单IP已过期,自动移除:', ip);
197
- } else {
198
- this.logger('warn', '黑名单IP请求被拒绝:', ip);
199
- // 记录违规:黑名单IP尝试访问
200
- if (this._config.violation_enable) {
201
- this.recordViolation(ip, '黑名单IP尝试访问');
202
- }
203
- return false;
204
- }
205
- }
206
-
207
- // 白名单直接通过
208
- if (this.isWhite(ip)) {
209
- this._updateReqCount(ip);
210
- return true;
211
- }
212
-
213
- // 检查频率限制
214
- if (!this._checkRateLimit(ip)) {
215
- this.logger('warn', 'IP请求频率超限:', ip);
216
-
217
- // 记录违规:请求频率超限
218
- if (this._config.violation_enable) {
219
- this.recordViolation(ip, '请求频率超限');
220
- }
221
-
222
- // 检查是否触发自动拉黑
223
- if (this._config.auto_black_enable) {
224
- const rate = this.getReqRate(ip);
225
- if (rate >= this._config.auto_black_threshold) {
226
- this._autoBlack(ip);
227
- return false;
228
- }
229
- }
230
-
231
- return false;
232
- }
233
-
234
- this._updateReqCount(ip);
235
- return true;
199
+ Ip.prototype.record = function(ip) {
200
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
201
+
202
+ // 检查黑名单(包括过期检查)
203
+ if (this.isBlack(ip)) {
204
+ // 检查是否过期
205
+ if (this._isBlackExpired(ip)) {
206
+ this.delBlack(ip);
207
+ } else {
208
+ this.log('warn', '黑名单IP请求被拒绝:', ip);
209
+ // 记录违规:黑名单IP尝试访问
210
+ if (this._config.violation_enable) {
211
+ this.recordViolation(ip, '黑名单IP尝试访问');
212
+ }
213
+ return false;
214
+ }
215
+ }
216
+
217
+ // 白名单直接通过
218
+ if (this.isWhite(ip)) {
219
+ this._updateReqCount(ip);
220
+ return true;
221
+ }
222
+
223
+ // 检查频率限制
224
+ if (!this._checkRateLimit(ip)) {
225
+ this.log('warn', 'IP请求频率超限:', ip);
226
+
227
+ // 记录违规:请求频率超限
228
+ if (this._config.violation_enable) {
229
+ this.recordViolation(ip, '请求频率超限');
230
+ }
231
+
232
+ // 检查是否触发自动拉黑
233
+ if (this._config.auto_black_enable) {
234
+ const rate = this.getReqRate(ip);
235
+ if (rate >= this._config.auto_black_threshold) {
236
+ this._autoBlack(ip);
237
+ return false;
238
+ }
239
+ }
240
+
241
+ return false;
242
+ }
243
+
244
+ this._updateReqCount(ip);
245
+ return true;
236
246
  };
237
247
 
238
248
  /**
@@ -241,9 +251,9 @@ Ip.prototype.record = function (ip) {
241
251
  * @returns {number} 请求次数
242
252
  * @throws {TypeError} 当IP非字符串时
243
253
  */
244
- Ip.prototype.getReqCount = function (ip) {
245
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
246
- return this._req_count.get(ip) || 0;
254
+ Ip.prototype.getReqCount = function(ip) {
255
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
256
+ return this._req_count.get(ip) || 0;
247
257
  };
248
258
 
249
259
  /**
@@ -252,106 +262,106 @@ Ip.prototype.getReqCount = function (ip) {
252
262
  * @returns {number} 请求频率(每分钟请求数)
253
263
  * @throws {TypeError} 当IP非字符串时
254
264
  */
255
- Ip.prototype.getReqRate = function (ip) {
256
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
265
+ Ip.prototype.getReqRate = function(ip) {
266
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
257
267
 
258
- const now = Date.now();
259
- const lastTime = this._req_time.get(ip) || now;
260
- const count = this._req_count.get(ip) || 0;
268
+ const now = Date.now();
269
+ const lastTime = this._req_time.get(ip) || now;
270
+ const count = this._req_count.get(ip) || 0;
261
271
 
262
- if (now - lastTime < this._config.check_interval) {
263
- // 在检查间隔内,计算实际频率
264
- const elapsed = Math.max(1, now - lastTime); // 避免除零
265
- return Math.round((count / elapsed) * this._config.check_interval);
266
- } else {
267
- // 超过检查间隔,频率为0
268
- return 0;
269
- }
272
+ if (now - lastTime < this._config.check_interval) {
273
+ // 在检查间隔内,计算实际频率
274
+ const elapsed = Math.max(1, now - lastTime); // 避免除零
275
+ return Math.round((count / elapsed) * this._config.check_interval);
276
+ } else {
277
+ // 超过检查间隔,频率为0
278
+ return 0;
279
+ }
270
280
  };
271
281
 
272
282
  /**
273
283
  * 获取所有IP的请求统计信息
274
284
  * @returns {Object} IP统计信息对象
275
285
  */
276
- Ip.prototype.getAllStats = function () {
277
- const stats = {};
278
- const now = Date.now();
286
+ Ip.prototype.getAllStats = function() {
287
+ const stats = {};
288
+ const now = Date.now();
279
289
 
280
- // 收集所有有请求记录的IP
281
- for (const ip of this.getAllReqIps()) {
282
- const lastTime = this._req_time.get(ip) || now;
283
- const isActive = now - lastTime < this._config.check_interval;
290
+ // 收集所有有请求记录的IP
291
+ for (const ip of this.getAllReqIps()) {
292
+ const lastTime = this._req_time.get(ip) || now;
293
+ const isActive = now - lastTime < this._config.check_interval;
284
294
 
285
- stats[ip] = {
286
- count: this.getReqCount(ip),
287
- rate: isActive ? this.getReqRate(ip) : 0,
288
- last_time: lastTime,
289
- is_white: this.isWhite(ip),
290
- is_black: this.isBlack(ip),
291
- black_time: this.getBlackExpireTime(ip)
292
- };
293
- }
295
+ stats[ip] = {
296
+ count: this.getReqCount(ip),
297
+ rate: isActive ? this.getReqRate(ip) : 0,
298
+ last_time: lastTime,
299
+ is_white: this.isWhite(ip),
300
+ is_black: this.isBlack(ip),
301
+ black_time: this.getBlackExpireTime(ip)
302
+ };
303
+ }
294
304
 
295
- return stats;
305
+ return stats;
296
306
  };
297
307
 
298
308
  /**
299
309
  * 获取所有白名单IP
300
310
  * @returns {Array} 白名单IP数组
301
311
  */
302
- Ip.prototype.getAllWhite = function () {
303
- return Array.from(this._white);
312
+ Ip.prototype.getAllWhite = function() {
313
+ return Array.from(this._white);
304
314
  };
305
315
 
306
316
  /**
307
317
  * 获取所有黑名单IP
308
318
  * @returns {Array} 黑名单IP数组
309
319
  */
310
- Ip.prototype.getAllBlack = function () {
311
- return Array.from(this._black);
320
+ Ip.prototype.getAllBlack = function() {
321
+ return Array.from(this._black);
312
322
  };
313
323
 
314
324
  /**
315
325
  * 获取所有有请求记录的IP
316
326
  * @returns {Array} 有请求记录的IP数组
317
327
  */
318
- Ip.prototype.getAllReqIps = function () {
319
- return Array.from(this._req_count.keys());
328
+ Ip.prototype.getAllReqIps = function() {
329
+ return Array.from(this._req_count.keys());
320
330
  };
321
331
 
322
332
  /**
323
333
  * 清空白名单
324
334
  */
325
335
  Ip.prototype.clearWhite = function() {
326
- this._white.clear();
327
- this.logger('info', '清空白名单');
328
-
329
- // 自动保存到持久化存储
330
- if (this._config.dir) {
331
- this.save();
332
- }
336
+ this._white.clear();
337
+ this.log('info', '清空白名单');
338
+
339
+ // 自动保存到持久化存储
340
+ if (this._config.dir) {
341
+ this.save();
342
+ }
333
343
  };
334
344
 
335
345
  /**
336
346
  * 清空黑名单
337
347
  */
338
348
  Ip.prototype.clearBlack = function() {
339
- this._black.clear();
340
- this.logger('info', '清空黑名单');
341
-
342
- // 自动保存到持久化存储
343
- if (this._config.dir) {
344
- this.save();
345
- }
349
+ this._black.clear();
350
+ this.log('info', '清空黑名单');
351
+
352
+ // 自动保存到持久化存储
353
+ if (this._config.dir) {
354
+ this.save();
355
+ }
346
356
  };
347
357
 
348
358
  /**
349
359
  * 清空所有请求记录
350
360
  */
351
- Ip.prototype.clearReq = function () {
352
- this._req_count.clear();
353
- this._req_time.clear();
354
- this.logger('info', '清空请求记录');
361
+ Ip.prototype.clearReq = function() {
362
+ this._req_count.clear();
363
+ this._req_time.clear();
364
+ this.log('info', '清空请求记录');
355
365
  };
356
366
 
357
367
  /**
@@ -360,22 +370,22 @@ Ip.prototype.clearReq = function () {
360
370
  * @param {string} ip - IP地址
361
371
  * @returns {boolean} 是否允许请求
362
372
  */
363
- Ip.prototype._checkRateLimit = function (ip) {
364
- const now = Date.now();
365
- const lastTime = this._req_time.get(ip) || 0;
373
+ Ip.prototype._checkRateLimit = function(ip) {
374
+ const now = Date.now();
375
+ const lastTime = this._req_time.get(ip) || 0;
366
376
 
367
- // 检查时间间隔
368
- if (now - lastTime < this._config.check_interval) {
369
- const count = this.getReqCount(ip);
370
- if (count >= this._config.max_req_per_min) {
371
- return false;
372
- }
373
- } else {
374
- // 重置计数
375
- this._req_count.set(ip, 0);
376
- }
377
+ // 检查时间间隔
378
+ if (now - lastTime < this._config.check_interval) {
379
+ const count = this.getReqCount(ip);
380
+ if (count >= this._config.max_req_per_min) {
381
+ return false;
382
+ }
383
+ } else {
384
+ // 重置计数
385
+ this._req_count.set(ip, 0);
386
+ }
377
387
 
378
- return true;
388
+ return true;
379
389
  };
380
390
 
381
391
  /**
@@ -383,10 +393,10 @@ Ip.prototype._checkRateLimit = function (ip) {
383
393
  * @private
384
394
  * @param {string} ip - IP地址
385
395
  */
386
- Ip.prototype._updateReqCount = function (ip) {
387
- const count = this.getReqCount(ip) + 1;
388
- this._req_count.set(ip, count);
389
- this._req_time.set(ip, Date.now());
396
+ Ip.prototype._updateReqCount = function(ip) {
397
+ const count = this.getReqCount(ip) + 1;
398
+ this._req_count.set(ip, count);
399
+ this._req_time.set(ip, Date.now());
390
400
  };
391
401
 
392
402
  /**
@@ -395,9 +405,9 @@ Ip.prototype._updateReqCount = function (ip) {
395
405
  * @param {string} ip - IP地址
396
406
  * @returns {boolean} 是否有效IP
397
407
  */
398
- Ip.prototype._validIp = function (ip) {
399
- const ipRegex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/;
400
- return ipRegex.test(ip);
408
+ Ip.prototype._validIp = function(ip) {
409
+ const ipRegex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/;
410
+ return ipRegex.test(ip);
401
411
  };
402
412
 
403
413
  /**
@@ -405,18 +415,18 @@ Ip.prototype._validIp = function (ip) {
405
415
  * @param {Array} ip_list - IP地址数组
406
416
  * @returns {number} 成功添加的数量
407
417
  */
408
- Ip.prototype.addWhiteBatch = function (ip_list) {
409
- if (!Array.isArray(ip_list)) throw new TypeError('ip_list must be array');
418
+ Ip.prototype.addWhiteBatch = function(ip_list) {
419
+ if (!Array.isArray(ip_list)) throw new TypeError('ip_list must be array');
410
420
 
411
- let success = 0;
412
- for (const ip of ip_list) {
413
- if (typeof ip === 'string' && this._validIp(ip)) {
414
- if (this.addWhite(ip)) {
415
- success++;
416
- }
417
- }
418
- }
419
- return success;
421
+ let success = 0;
422
+ for (const ip of ip_list) {
423
+ if (typeof ip === 'string' && this._validIp(ip)) {
424
+ if (this.addWhite(ip)) {
425
+ success++;
426
+ }
427
+ }
428
+ }
429
+ return success;
420
430
  };
421
431
 
422
432
  /**
@@ -424,18 +434,18 @@ Ip.prototype.addWhiteBatch = function (ip_list) {
424
434
  * @param {Array} ip_list - IP地址数组
425
435
  * @returns {number} 成功添加的数量
426
436
  */
427
- Ip.prototype.addBlackBatch = function (ip_list) {
428
- if (!Array.isArray(ip_list)) throw new TypeError('ip_list must be array');
437
+ Ip.prototype.addBlackBatch = function(ip_list) {
438
+ if (!Array.isArray(ip_list)) throw new TypeError('ip_list must be array');
429
439
 
430
- let success = 0;
431
- for (const ip of ip_list) {
432
- if (typeof ip === 'string' && this._validIp(ip)) {
433
- if (this.addBlack(ip)) {
434
- success++;
435
- }
436
- }
437
- }
438
- return success;
440
+ let success = 0;
441
+ for (const ip of ip_list) {
442
+ if (typeof ip === 'string' && this._validIp(ip)) {
443
+ if (this.addBlack(ip)) {
444
+ success++;
445
+ }
446
+ }
447
+ }
448
+ return success;
439
449
  };
440
450
 
441
451
  /**
@@ -445,41 +455,40 @@ Ip.prototype.addBlackBatch = function (ip_list) {
445
455
  * @returns {boolean} 是否拉黑成功
446
456
  * @throws {TypeError} 当IP非字符串时
447
457
  */
448
- Ip.prototype.blackByHighFreq = function (ip, duration) {
449
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
458
+ Ip.prototype.blackByHighFreq = function(ip, duration) {
459
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
450
460
 
451
- const black_duration = duration || this._config.auto_black_duration;
461
+ const black_duration = duration || this._config.auto_black_duration;
452
462
 
453
- if (this.addBlack(ip)) {
454
- this._black_time.set(ip, Date.now() + black_duration);
455
- this.logger('warn', '手动高频拉黑IP:', ip, '持续时间:', black_duration + 'ms');
456
- return true;
457
- }
463
+ if (this.addBlack(ip)) {
464
+ this._black_time.set(ip, Date.now() + black_duration);
465
+ this.log('warn', '手动高频拉黑IP:', ip, '持续时间:', black_duration + 'ms');
466
+ return true;
467
+ }
458
468
 
459
- return false;
469
+ return false;
460
470
  };
461
471
 
462
472
  /**
463
473
  * 检查并清理过期的黑名单IP(私有方法)
464
474
  * @private
465
475
  */
466
- Ip.prototype._cleanExpiredBlack = function () {
467
- const now = Date.now();
468
- const expired = [];
476
+ Ip.prototype._cleanExpiredBlack = function() {
477
+ const now = Date.now();
478
+ const expired = [];
469
479
 
470
- // 获取所有有过期时间的黑名单IP
471
- for (const ip of this.getAllBlack()) {
472
- const expireTime = this.getBlackExpireTime(ip);
473
- if (expireTime && now > expireTime) {
474
- expired.push(ip);
475
- }
476
- }
480
+ // 获取所有有过期时间的黑名单IP
481
+ for (const ip of this.getAllBlack()) {
482
+ const expireTime = this.getBlackExpireTime(ip);
483
+ if (expireTime && now > expireTime) {
484
+ expired.push(ip);
485
+ }
486
+ }
477
487
 
478
- for (const ip of expired) {
479
- this.delBlack(ip);
480
- this._black_time.delete(ip);
481
- this.logger('info', '清理过期黑名单IP:', ip);
482
- }
488
+ for (const ip of expired) {
489
+ this.delBlack(ip);
490
+ this._black_time.delete(ip);
491
+ }
483
492
  };
484
493
 
485
494
  /**
@@ -488,11 +497,11 @@ Ip.prototype._cleanExpiredBlack = function () {
488
497
  * @param {string} ip - IP地址
489
498
  * @returns {boolean} 是否过期
490
499
  */
491
- Ip.prototype._isBlackExpired = function (ip) {
492
- const expireTime = this.getBlackExpireTime(ip);
493
- if (!expireTime) return false; // 没有设置过期时间,永不过期
500
+ Ip.prototype._isBlackExpired = function(ip) {
501
+ const expireTime = this.getBlackExpireTime(ip);
502
+ if (!expireTime) return false; // 没有设置过期时间,永不过期
494
503
 
495
- return Date.now() > expireTime;
504
+ return Date.now() > expireTime;
496
505
  };
497
506
 
498
507
  /**
@@ -500,13 +509,13 @@ Ip.prototype._isBlackExpired = function (ip) {
500
509
  * @private
501
510
  * @param {string} ip - IP地址
502
511
  */
503
- Ip.prototype._autoBlack = function (ip) {
504
- if (this.addBlack(ip)) {
505
- this._black_time.set(ip, Date.now() + this._config.auto_black_duration);
506
- this.logger('warn', '自动高频拉黑IP:', ip,
507
- '频率:', this.getReqRate(ip) + '/分钟',
508
- '持续时间:', this._config.auto_black_duration + 'ms');
509
- }
512
+ Ip.prototype._autoBlack = function(ip) {
513
+ if (this.addBlack(ip)) {
514
+ this._black_time.set(ip, Date.now() + this._config.auto_black_duration);
515
+ this.log('warn', '自动高频拉黑IP:', ip,
516
+ '频率:', this.getReqRate(ip) + '/分钟',
517
+ '持续时间:', this._config.auto_black_duration + 'ms');
518
+ }
510
519
  };
511
520
 
512
521
  /**
@@ -515,23 +524,23 @@ Ip.prototype._autoBlack = function (ip) {
515
524
  * @returns {number|null} 过期时间戳(毫秒),null表示永不过期
516
525
  * @throws {TypeError} 当IP非字符串时
517
526
  */
518
- Ip.prototype.getBlackExpireTime = function (ip) {
519
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
527
+ Ip.prototype.getBlackExpireTime = function(ip) {
528
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
520
529
 
521
- if (!this.isBlack(ip)) return null;
530
+ if (!this.isBlack(ip)) return null;
522
531
 
523
- const expireTime = this._black_time.get(ip);
524
- return expireTime || null;
532
+ const expireTime = this._black_time.get(ip);
533
+ return expireTime || null;
525
534
  };
526
535
 
527
536
  /**
528
537
  * 清理所有过期的黑名单IP
529
538
  * @returns {number} 清理的数量
530
539
  */
531
- Ip.prototype.cleanExpiredBlack = function () {
532
- const beforeCount = this.getAllBlack().length;
533
- this._cleanExpiredBlack();
534
- return beforeCount - this.getAllBlack().length;
540
+ Ip.prototype.cleanExpiredBlack = function() {
541
+ const beforeCount = this.getAllBlack().length;
542
+ this._cleanExpiredBlack();
543
+ return beforeCount - this.getAllBlack().length;
535
544
  };
536
545
 
537
546
  /**
@@ -541,26 +550,28 @@ Ip.prototype.cleanExpiredBlack = function () {
541
550
  * @returns {number} 当前违规次数
542
551
  */
543
552
  Ip.prototype.recordViolation = function(ip, reason) {
544
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
545
- if (!this._config.violation_enable) return 0;
546
-
547
- this._cleanExpiredViolation();
548
-
549
- const now = Date.now();
550
- const count = this.getViolationCount(ip) + 1;
551
-
552
- this._violation_count.set(ip, count);
553
- this._violation_time.set(ip, now);
554
-
555
- this.logger('info', 'IP违规记录:', { ip, reason, count, time: new Date(now).toISOString() });
556
-
557
- // 检查是否达到自动拉黑条件
558
- if (this._config.violation_auto_black && count >= this._config.violation_max_count) {
559
- this.blackByHighFreq(ip, this._config.auto_black_duration);
560
- this.logger('warn', 'IP因违规次数超限自动拉黑:', { ip, count, duration: this._config.auto_black_duration });
561
- }
562
-
563
- return count;
553
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
554
+ if (!this._config.violation_enable) return 0;
555
+
556
+ this._cleanExpiredViolation();
557
+
558
+ const now = Date.now();
559
+ const count = this.getViolationCount(ip) + 1;
560
+
561
+ this._violation_count.set(ip, count);
562
+ this._violation_time.set(ip, now);
563
+
564
+ // 检查是否达到自动拉黑条件
565
+ if (this._config.violation_auto_black && count >= this._config.violation_max_count) {
566
+ this.blackByHighFreq(ip, this._config.auto_black_duration);
567
+ this.log('warn', 'IP因违规次数超限自动拉黑:', {
568
+ ip,
569
+ count,
570
+ duration: this._config.auto_black_duration
571
+ });
572
+ }
573
+
574
+ return count;
564
575
  };
565
576
 
566
577
  /**
@@ -569,10 +580,10 @@ Ip.prototype.recordViolation = function(ip, reason) {
569
580
  * @returns {number} 违规次数
570
581
  */
571
582
  Ip.prototype.getViolationCount = function(ip) {
572
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
573
-
574
- this._cleanExpiredViolation();
575
- return this._violation_count.get(ip) || 0;
583
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
584
+
585
+ this._cleanExpiredViolation();
586
+ return this._violation_count.get(ip) || 0;
576
587
  };
577
588
 
578
589
  /**
@@ -581,13 +592,13 @@ Ip.prototype.getViolationCount = function(ip) {
581
592
  * @returns {boolean} 是否成功重置
582
593
  */
583
594
  Ip.prototype.resetViolation = function(ip) {
584
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
585
-
586
- const hadRecord = this._violation_count.has(ip);
587
- this._violation_count.delete(ip);
588
- this._violation_time.delete(ip);
589
-
590
- return hadRecord;
595
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
596
+
597
+ const hadRecord = this._violation_count.has(ip);
598
+ this._violation_count.delete(ip);
599
+ this._violation_time.delete(ip);
600
+
601
+ return hadRecord;
591
602
  };
592
603
 
593
604
  /**
@@ -595,7 +606,7 @@ Ip.prototype.resetViolation = function(ip) {
595
606
  * @returns {Array} 有违规记录的IP数组
596
607
  */
597
608
  Ip.prototype.getAllViolationIps = function() {
598
- return Array.from(this._violation_count.keys());
609
+ return Array.from(this._violation_count.keys());
599
610
  };
600
611
 
601
612
  /**
@@ -604,15 +615,15 @@ Ip.prototype.getAllViolationIps = function() {
604
615
  * @returns {Object|null} 违规信息
605
616
  */
606
617
  Ip.prototype.getViolationInfo = function(ip) {
607
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
608
-
609
- const count = this.getViolationCount(ip);
610
- if (count === 0) return null;
611
-
612
- return {
613
- count: count,
614
- time: this._violation_time.get(ip)
615
- };
618
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
619
+
620
+ const count = this.getViolationCount(ip);
621
+ if (count === 0) return null;
622
+
623
+ return {
624
+ count: count,
625
+ time: this._violation_time.get(ip)
626
+ };
616
627
  };
617
628
 
618
629
  /**
@@ -620,14 +631,14 @@ Ip.prototype.getViolationInfo = function(ip) {
620
631
  * @returns {Object} 违规统计信息
621
632
  */
622
633
  Ip.prototype.getAllViolations = function() {
623
- this._cleanExpiredViolation();
624
-
625
- const violations = {};
626
- for (const ip of this.getAllViolationIps()) {
627
- violations[ip] = this.getViolationInfo(ip);
628
- }
629
-
630
- return violations;
634
+ this._cleanExpiredViolation();
635
+
636
+ const violations = {};
637
+ for (const ip of this.getAllViolationIps()) {
638
+ violations[ip] = this.getViolationInfo(ip);
639
+ }
640
+
641
+ return violations;
631
642
  };
632
643
 
633
644
  /**
@@ -635,34 +646,34 @@ Ip.prototype.getAllViolations = function() {
635
646
  * @private
636
647
  */
637
648
  Ip.prototype._cleanExpiredViolation = function() {
638
- const now = Date.now();
639
- const expired = [];
649
+ const now = Date.now();
650
+ const expired = [];
640
651
 
641
- // 直接访问内部数据结构避免递归调用
642
- for (const [ip, time] of this._violation_time) {
643
- if (now - time > this._config.violation_reset_time) {
644
- expired.push(ip);
645
- }
646
- }
652
+ // 直接访问内部数据结构避免递归调用
653
+ for (const [ip, time] of this._violation_time) {
654
+ if (now - time > this._config.violation_reset_time) {
655
+ expired.push(ip);
656
+ }
657
+ }
647
658
 
648
- for (const ip of expired) {
649
- this._violation_count.delete(ip);
650
- this._violation_time.delete(ip);
651
- }
659
+ for (const ip of expired) {
660
+ this._violation_count.delete(ip);
661
+ this._violation_time.delete(ip);
662
+ }
652
663
  };
653
664
 
654
665
  /**
655
666
  * 清空所有违规记录
656
667
  */
657
668
  Ip.prototype.clearViolations = function() {
658
- this._violation_count.clear();
659
- this._violation_time.clear();
660
- this.logger('info', '清空所有违规记录');
661
-
662
- // 自动保存到持久化存储
663
- if (this._config.dir) {
664
- this.save();
665
- }
669
+ this._violation_count.clear();
670
+ this._violation_time.clear();
671
+ this.log('info', '清空所有违规记录');
672
+
673
+ // 自动保存到持久化存储
674
+ if (this._config.dir) {
675
+ this.save();
676
+ }
666
677
  };
667
678
 
668
679
  /**
@@ -670,53 +681,53 @@ Ip.prototype.clearViolations = function() {
670
681
  * @returns {boolean} 是否保存成功
671
682
  */
672
683
  Ip.prototype.save = function() {
673
- try {
674
- const fs = require('fs');
675
- const path = require('path');
676
-
677
- // 确保存储目录存在
678
- const dir = this._config.dir;
679
- dir.addDir();
680
-
681
- // 分别保存不同数据到不同文件
682
- const timestamp = Date.now();
683
-
684
- // 保存白名单数据
685
- const whiteData = {
686
- white: this.getAllWhite(),
687
- timestamp: timestamp
688
- };
689
- const whitePath = path.join(dir, 'white.json');
690
- fs.writeFileSync(whitePath, JSON.stringify(whiteData, null, 2), 'utf8');
691
-
692
- // 保存黑名单数据
693
- const blackData = {
694
- black: this.getAllBlack(),
695
- black_time: Array.from(this._black_time.entries()),
696
- timestamp: timestamp
697
- };
698
- const blackPath = path.join(dir, 'black.json');
699
- fs.writeFileSync(blackPath, JSON.stringify(blackData, null, 2), 'utf8');
700
-
701
- // 保存违规记录数据
702
- const violationData = {
703
- violation_count: Array.from(this._violation_count.entries()),
704
- violation_time: Array.from(this._violation_time.entries()),
705
- timestamp: timestamp
706
- };
707
- const violationPath = path.join(dir, 'violation.json');
708
- fs.writeFileSync(violationPath, JSON.stringify(violationData, null, 2), 'utf8');
709
-
710
- this.logger('info', 'IP数据已分文件保存:', {
711
- white: whitePath,
712
- black: blackPath,
713
- violation: violationPath
714
- });
715
- return true;
716
- } catch (err) {
717
- this.logger('error', '保存IP数据失败:', err.message);
718
- return false;
719
- }
684
+ try {
685
+ const fs = require('fs');
686
+ const path = require('path');
687
+
688
+ // 确保存储目录存在
689
+ const dir = this._config.dir;
690
+ dir.addDir();
691
+
692
+ // 分别保存不同数据到不同文件
693
+ const timestamp = Date.now();
694
+
695
+ // 保存白名单数据
696
+ const whiteData = {
697
+ white: this.getAllWhite(),
698
+ timestamp: timestamp
699
+ };
700
+ const whitePath = path.join(dir, 'white.json');
701
+ fs.writeFileSync(whitePath, JSON.stringify(whiteData, null, 2), 'utf8');
702
+
703
+ // 保存黑名单数据
704
+ const blackData = {
705
+ black: this.getAllBlack(),
706
+ black_time: Array.from(this._black_time.entries()),
707
+ timestamp: timestamp
708
+ };
709
+ const blackPath = path.join(dir, 'black.json');
710
+ fs.writeFileSync(blackPath, JSON.stringify(blackData, null, 2), 'utf8');
711
+
712
+ // 保存违规记录数据
713
+ const violationData = {
714
+ violation_count: Array.from(this._violation_count.entries()),
715
+ violation_time: Array.from(this._violation_time.entries()),
716
+ timestamp: timestamp
717
+ };
718
+ const violationPath = path.join(dir, 'violation.json');
719
+ fs.writeFileSync(violationPath, JSON.stringify(violationData, null, 2), 'utf8');
720
+
721
+ this.log('info', 'IP数据已分文件保存:', {
722
+ white: whitePath,
723
+ black: blackPath,
724
+ violation: violationPath
725
+ });
726
+ return true;
727
+ } catch (err) {
728
+ this.log('error', '保存IP数据失败:', err.message);
729
+ return false;
730
+ }
720
731
  };
721
732
 
722
733
  /**
@@ -724,101 +735,87 @@ Ip.prototype.save = function() {
724
735
  * @returns {boolean} 是否加载成功
725
736
  */
726
737
  Ip.prototype.load = function() {
727
- try {
728
- const fs = require('fs');
729
- const path = require('path');
730
-
731
- const dir = this._config.dir;
732
- let loadedCount = 0;
733
-
734
- // 加载白名单数据
735
- const whitePath = path.join(dir, 'white.json');
736
- if (fs.existsSync(whitePath)) {
737
- const content = fs.readFileSync(whitePath, 'utf8');
738
- const data = JSON.parse(content);
739
-
740
- if (Array.isArray(data.white)) {
741
- for (const ip of data.white) {
742
- if (typeof ip === 'string') {
743
- this._white.add(ip);
744
- }
745
- }
746
- loadedCount++;
747
- this.logger('info', '白名单数据已加载:', whitePath);
748
- }
749
- }
750
-
751
- // 加载黑名单数据
752
- const blackPath = path.join(dir, 'black.json');
753
- if (fs.existsSync(blackPath)) {
754
- const content = fs.readFileSync(blackPath, 'utf8');
755
- const data = JSON.parse(content);
756
-
757
- // 加载黑名单
758
- if (Array.isArray(data.black)) {
759
- for (const ip of data.black) {
760
- if (typeof ip === 'string') {
761
- this._black.add(ip);
762
- }
763
- }
764
- }
765
-
766
- // 加载黑名单过期时间
767
- if (Array.isArray(data.black_time)) {
768
- for (const [ip, time] of data.black_time) {
769
- if (typeof ip === 'string' && typeof time === 'number') {
770
- this._black_time.set(ip, time);
771
- }
772
- }
773
- }
774
-
775
- loadedCount++;
776
- this.logger('info', '黑名单数据已加载:', blackPath);
777
- }
778
-
779
- // 加载违规记录数据
780
- const violationPath = path.join(dir, 'violation.json');
781
- if (fs.existsSync(violationPath)) {
782
- const content = fs.readFileSync(violationPath, 'utf8');
783
- const data = JSON.parse(content);
784
-
785
- // 加载违规记录
786
- if (Array.isArray(data.violation_count)) {
787
- for (const [ip, count] of data.violation_count) {
788
- if (typeof ip === 'string' && typeof count === 'number') {
789
- this._violation_count.set(ip, count);
790
- }
791
- }
792
- }
793
-
794
- if (Array.isArray(data.violation_time)) {
795
- for (const [ip, time] of data.violation_time) {
796
- if (typeof ip === 'string' && typeof time === 'number') {
797
- this._violation_time.set(ip, time);
798
- }
799
- }
800
- }
801
-
802
- loadedCount++;
803
- this.logger('info', '违规记录数据已加载:', violationPath);
804
- }
805
-
806
- if (loadedCount === 0) {
807
- this.logger('info', '未找到任何IP数据文件,跳过加载');
808
- } else {
809
- this.logger('info', 'IP数据加载完成,统计:', {
810
- white_count: this.getAllWhite().length,
811
- black_count: this.getAllBlack().length,
812
- violation_count: this.getAllViolationIps().length,
813
- files_loaded: loadedCount
814
- });
815
- }
816
-
817
- return true;
818
- } catch (err) {
819
- this.logger('error', '加载IP数据失败:', err.message);
820
- return false;
821
- }
738
+ try {
739
+ const fs = require('fs');
740
+ const path = require('path');
741
+
742
+ const dir = this._config.dir;
743
+ let loadedCount = 0;
744
+
745
+ // 加载白名单数据
746
+ const whitePath = path.join(dir, 'white.json');
747
+ if (fs.existsSync(whitePath)) {
748
+ const content = fs.readFileSync(whitePath, 'utf8');
749
+ const data = JSON.parse(content);
750
+
751
+ if (Array.isArray(data.white)) {
752
+ for (const ip of data.white) {
753
+ if (typeof ip === 'string') {
754
+ this._white.add(ip);
755
+ }
756
+ }
757
+ loadedCount++;
758
+ }
759
+ }
760
+
761
+ // 加载黑名单数据
762
+ const blackPath = path.join(dir, 'black.json');
763
+ if (fs.existsSync(blackPath)) {
764
+ const content = fs.readFileSync(blackPath, 'utf8');
765
+ const data = JSON.parse(content);
766
+
767
+ // 加载黑名单
768
+ if (Array.isArray(data.black)) {
769
+ for (const ip of data.black) {
770
+ if (typeof ip === 'string') {
771
+ this._black.add(ip);
772
+ }
773
+ }
774
+ }
775
+
776
+ // 加载黑名单过期时间
777
+ if (Array.isArray(data.black_time)) {
778
+ for (const [ip, time] of data.black_time) {
779
+ if (typeof ip === 'string' && typeof time === 'number') {
780
+ this._black_time.set(ip, time);
781
+ }
782
+ }
783
+ }
784
+
785
+ loadedCount++;
786
+ }
787
+
788
+ // 加载违规记录数据
789
+ const violationPath = path.join(dir, 'violation.json');
790
+ if (fs.existsSync(violationPath)) {
791
+ const content = fs.readFileSync(violationPath, 'utf8');
792
+ const data = JSON.parse(content);
793
+
794
+ // 加载违规记录
795
+ if (Array.isArray(data.violation_count)) {
796
+ for (const [ip, count] of data.violation_count) {
797
+ if (typeof ip === 'string' && typeof count === 'number') {
798
+ this._violation_count.set(ip, count);
799
+ }
800
+ }
801
+ }
802
+
803
+ if (Array.isArray(data.violation_time)) {
804
+ for (const [ip, time] of data.violation_time) {
805
+ if (typeof ip === 'string' && typeof time === 'number') {
806
+ this._violation_time.set(ip, time);
807
+ }
808
+ }
809
+ }
810
+
811
+ loadedCount++;
812
+ }
813
+
814
+ return true;
815
+ } catch (err) {
816
+ this.log('error', '加载IP数据失败:', err.message);
817
+ return false;
818
+ }
822
819
  };
823
820
 
824
821
  /**
@@ -826,26 +823,23 @@ Ip.prototype.load = function() {
826
823
  * @param {number} interval - 保存间隔(毫秒),默认5分钟
827
824
  */
828
825
  Ip.prototype.autoSave = function(interval = 300000) {
829
- if (this._autoSaveTimer) {
830
- clearInterval(this._autoSaveTimer);
831
- }
832
-
833
- this._autoSaveTimer = setInterval(() => {
834
- this.save();
835
- }, interval);
836
-
837
- this.logger('info', '自动保存已启动,间隔:', interval, 'ms');
826
+ if (this._autoSaveTimer) {
827
+ clearInterval(this._autoSaveTimer);
828
+ }
829
+
830
+ this._autoSaveTimer = setInterval(() => {
831
+ this.save();
832
+ }, interval);
838
833
  };
839
834
 
840
835
  /**
841
836
  * 停止自动保存
842
837
  */
843
838
  Ip.prototype.stopAutoSave = function() {
844
- if (this._autoSaveTimer) {
845
- clearInterval(this._autoSaveTimer);
846
- this._autoSaveTimer = null;
847
- this.logger('info', '自动保存已停止');
848
- }
839
+ if (this._autoSaveTimer) {
840
+ clearInterval(this._autoSaveTimer);
841
+ this._autoSaveTimer = null;
842
+ }
849
843
  };
850
844
 
851
845
  /**
@@ -855,61 +849,61 @@ Ip.prototype.stopAutoSave = function() {
855
849
  * @throws {TypeError} 当IP非字符串时
856
850
  */
857
851
  Ip.prototype.del = function(ip) {
858
- if (typeof ip !== 'string') throw new TypeError('ip must be string');
859
-
860
- const result = {
861
- black: false,
862
- white: false,
863
- violation: false,
864
- black_time: false,
865
- violation_count: false,
866
- violation_time: false
867
- };
868
-
869
- // 从黑名单删除
870
- if (this._black.has(ip)) {
871
- this._black.delete(ip);
872
- result.black = true;
873
- }
874
-
875
- // 从白名单删除
876
- if (this._white.has(ip)) {
877
- this._white.delete(ip);
878
- result.white = true;
879
- }
880
-
881
- // 从违规记录删除
882
- if (this._violation_count.has(ip)) {
883
- this._violation_count.delete(ip);
884
- result.violation_count = true;
885
- }
886
-
887
- if (this._violation_time.has(ip)) {
888
- this._violation_time.delete(ip);
889
- result.violation_time = true;
890
- }
891
-
892
- // 从黑名单过期时间记录删除
893
- if (this._black_time.has(ip)) {
894
- this._black_time.delete(ip);
895
- result.black_time = true;
896
- }
897
-
898
- // 如果有任何删除操作,记录日志并自动保存
899
- if (Object.values(result).some(Boolean)) {
900
- this.logger('info', '删除IP记录:', { ip, result });
901
-
902
- // 自动保存到持久化存储
903
- if (this._config.dir) {
904
- this.save();
905
- }
906
- }
907
-
908
- return result;
852
+ if (typeof ip !== 'string') throw new TypeError('ip must be string');
853
+
854
+ const result = {
855
+ black: false,
856
+ white: false,
857
+ violation: false,
858
+ black_time: false,
859
+ violation_count: false,
860
+ violation_time: false
861
+ };
862
+
863
+ // 从黑名单删除
864
+ if (this._black.has(ip)) {
865
+ this._black.delete(ip);
866
+ result.black = true;
867
+ }
868
+
869
+ // 从白名单删除
870
+ if (this._white.has(ip)) {
871
+ this._white.delete(ip);
872
+ result.white = true;
873
+ }
874
+
875
+ // 从违规记录删除
876
+ if (this._violation_count.has(ip)) {
877
+ this._violation_count.delete(ip);
878
+ result.violation_count = true;
879
+ }
880
+
881
+ if (this._violation_time.has(ip)) {
882
+ this._violation_time.delete(ip);
883
+ result.violation_time = true;
884
+ }
885
+
886
+ // 从黑名单过期时间记录删除
887
+ if (this._black_time.has(ip)) {
888
+ this._black_time.delete(ip);
889
+ result.black_time = true;
890
+ }
891
+
892
+ // 如果有任何删除操作,记录日志并自动保存
893
+ if (Object.values(result).some(Boolean)) {
894
+ // 自动保存到持久化存储
895
+ if (this._config.dir) {
896
+ this.save();
897
+ }
898
+ }
899
+
900
+ return result;
909
901
  };
910
902
 
911
903
  if (global.$ && !$.ip) {
912
- $.ip = new Ip();
904
+ $.ip = new Ip();
913
905
  }
914
906
 
915
- module.exports = { Ip };
907
+ module.exports = {
908
+ Ip
909
+ };