mm_ip 1.0.2 → 1.0.3

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