-
Notifications
You must be signed in to change notification settings - Fork 0
/
rate-limiter.js
79 lines (68 loc) · 2.01 KB
/
rate-limiter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
let instance;
import RedisStorage from "./storage-strategies/redis-storage.js";
import InMemoryStorage from "./storage-strategies/in-memory-storage.js";
class RateLimiter {
constructor({
window,
maxHits,
identifier = 'ip',
customIdentifierExtractor = null,
storage = 'memory',
redisConfig = null
}) {
if (instance) {
return instance;
}
this.window = window;
this.maxHits = maxHits;
this.identifier = identifier;
this.customIdentifierExtractor = customIdentifierExtractor;
// Initialize storage strategy
if (storage === 'redis') {
if (!redisConfig) {
throw new Error('Redis configuration is required when using Redis storage');
}
this.storage = new RedisStorage(redisConfig);
} else {
this.storage = new InMemoryStorage();
}
instance = this;
}
getIdentifier(req) {
if (this.customIdentifierExtractor) {
return this.customIdentifierExtractor(req);
}
switch (this.identifier) {
case 'ip':
return req.ip || req.connection.remoteAddress || req.headers['x-forwarded-for'];
case 'authToken':
return req.headers['authorization'] || '';
case 'cookie':
return req.cookies?.session || '';
default:
return '';
}
}
guard() {
return async (req, res, next) => {
try {
const id = this.getIdentifier(req);
const requestData = await this.storage.increment(id, this.window);
// Only check rate limit if we have valid request data
if (requestData.count > this.maxHits) {
const retryAfter = Math.ceil((requestData.timestamp + this.window - Date.now()) / 1000);
return res.status(429).json({
error: "Too many requests",
retryAfter
});
}
next();
} catch (error) {
console.error('Rate limiter error:', error);
// In case of storage errors, allow the request to proceed
next();
}
};
}
}
export default RateLimiter;