rate-limiter-flexible 2.1.3 → 2.1.7
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/README.md +8 -1
- package/lib/RateLimiterMySQL.js +38 -21
- package/lib/RateLimiterPostgres.js +44 -24
- package/lib/RateLimiterQueue.js +36 -2
- package/lib/index.d.ts +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,6 +126,10 @@ Some copy/paste examples on Wiki:
|
|
|
126
126
|
* [RLWrapperBlackAndWhite](https://github.com/animir/node-rate-limiter-flexible/wiki/Black-and-White-lists) Black and White lists
|
|
127
127
|
* [RateLimiterQueue](https://github.com/animir/node-rate-limiter-flexible/wiki/RateLimiterQueue) Rate limiter with FIFO queue
|
|
128
128
|
|
|
129
|
+
### Changelog
|
|
130
|
+
|
|
131
|
+
See [releases](https://github.com/animir/node-rate-limiter-flexible/releases) for detailed changelog.
|
|
132
|
+
|
|
129
133
|
## Basic Options
|
|
130
134
|
|
|
131
135
|
* **points**
|
|
@@ -157,9 +161,10 @@ Some copy/paste examples on Wiki:
|
|
|
157
161
|
* [storeType](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#storetype) Have to be set to `knex`, if you use it.
|
|
158
162
|
* [dbName](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#dbname) Where to store points.
|
|
159
163
|
* [tableName](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#tablename) Table/collection.
|
|
164
|
+
* [tableCreated](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#tablecreated) Is table already created in MySQL or PostgreSQL.
|
|
160
165
|
* [clearExpiredByTimeout](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#clearexpiredbytimeout) For MySQL and PostgreSQL.
|
|
161
166
|
|
|
162
|
-
|
|
167
|
+
Smooth out traffic picks:
|
|
163
168
|
* [execEvenly](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#execevenly)
|
|
164
169
|
* [execEvenlyMinDelayMs](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#execevenlymindelayms)
|
|
165
170
|
|
|
@@ -218,3 +223,5 @@ It has to implement at least 4 methods:
|
|
|
218
223
|
* `_delete` deletes all key related data and returns `true` on deleted, `false` if key is not found.
|
|
219
224
|
|
|
220
225
|
All other methods depends on store. See `RateLimiterRedis` or `RateLimiterPostgres` for example.
|
|
226
|
+
|
|
227
|
+
Note: all changes should be covered by tests.
|
package/lib/RateLimiterMySQL.js
CHANGED
|
@@ -28,24 +28,33 @@ class RateLimiterMySQL extends RateLimiterStoreAbstract {
|
|
|
28
28
|
|
|
29
29
|
this.clearExpiredByTimeout = opts.clearExpiredByTimeout;
|
|
30
30
|
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
cb
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
cb
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
this.tableCreated = opts.tableCreated;
|
|
32
|
+
if (!this.tableCreated) {
|
|
33
|
+
this._createDbAndTable()
|
|
34
|
+
.then(() => {
|
|
35
|
+
this.tableCreated = true;
|
|
36
|
+
if (this.clearExpiredByTimeout) {
|
|
37
|
+
this._clearExpiredHourAgo();
|
|
38
|
+
}
|
|
39
|
+
if (typeof cb === 'function') {
|
|
40
|
+
cb();
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
.catch((err) => {
|
|
44
|
+
if (typeof cb === 'function') {
|
|
45
|
+
cb(err);
|
|
46
|
+
} else {
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
if (this.clearExpiredByTimeout) {
|
|
52
|
+
this._clearExpiredHourAgo();
|
|
53
|
+
}
|
|
54
|
+
if (typeof cb === 'function') {
|
|
55
|
+
cb();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
clearExpired(expire) {
|
|
@@ -189,6 +198,14 @@ class RateLimiterMySQL extends RateLimiterStoreAbstract {
|
|
|
189
198
|
this._tableName = typeof value === 'undefined' ? this.keyPrefix : value;
|
|
190
199
|
}
|
|
191
200
|
|
|
201
|
+
get tableCreated() {
|
|
202
|
+
return this._tableCreated
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
set tableCreated(value) {
|
|
206
|
+
this._tableCreated = typeof value === 'undefined' ? false : !!value;
|
|
207
|
+
}
|
|
208
|
+
|
|
192
209
|
get clearExpiredByTimeout() {
|
|
193
210
|
return this._clearExpiredByTimeout;
|
|
194
211
|
}
|
|
@@ -277,7 +294,7 @@ class RateLimiterMySQL extends RateLimiterStoreAbstract {
|
|
|
277
294
|
}
|
|
278
295
|
|
|
279
296
|
_upsert(key, points, msDuration, forceExpire = false) {
|
|
280
|
-
if (!this.
|
|
297
|
+
if (!this.tableCreated) {
|
|
281
298
|
return Promise.reject(Error('Table is not created yet'));
|
|
282
299
|
}
|
|
283
300
|
|
|
@@ -301,7 +318,7 @@ class RateLimiterMySQL extends RateLimiterStoreAbstract {
|
|
|
301
318
|
}
|
|
302
319
|
|
|
303
320
|
_get(rlKey) {
|
|
304
|
-
if (!this.
|
|
321
|
+
if (!this.tableCreated) {
|
|
305
322
|
return Promise.reject(Error('Table is not created yet'));
|
|
306
323
|
}
|
|
307
324
|
|
|
@@ -331,7 +348,7 @@ class RateLimiterMySQL extends RateLimiterStoreAbstract {
|
|
|
331
348
|
}
|
|
332
349
|
|
|
333
350
|
_delete(rlKey) {
|
|
334
|
-
if (!this.
|
|
351
|
+
if (!this.tableCreated) {
|
|
335
352
|
return Promise.reject(Error('Table is not created yet'));
|
|
336
353
|
}
|
|
337
354
|
|
|
@@ -26,24 +26,30 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract {
|
|
|
26
26
|
|
|
27
27
|
this.clearExpiredByTimeout = opts.clearExpiredByTimeout;
|
|
28
28
|
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
cb
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
cb
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
29
|
+
this.tableCreated = opts.tableCreated;
|
|
30
|
+
if (!this.tableCreated) {
|
|
31
|
+
this._createTable()
|
|
32
|
+
.then(() => {
|
|
33
|
+
this.tableCreated = true;
|
|
34
|
+
if (this.clearExpiredByTimeout) {
|
|
35
|
+
this._clearExpiredHourAgo();
|
|
36
|
+
}
|
|
37
|
+
if (typeof cb === 'function') {
|
|
38
|
+
cb();
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
.catch((err) => {
|
|
42
|
+
if (typeof cb === 'function') {
|
|
43
|
+
cb(err);
|
|
44
|
+
} else {
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
} else {
|
|
49
|
+
if (typeof cb === 'function') {
|
|
50
|
+
cb();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
clearExpired(expire) {
|
|
@@ -152,17 +158,23 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract {
|
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
set clientType(value) {
|
|
161
|
+
const constructorName = this.client.constructor.name;
|
|
162
|
+
|
|
155
163
|
if (typeof value === 'undefined') {
|
|
156
|
-
if (
|
|
164
|
+
if (constructorName === 'Client') {
|
|
157
165
|
value = 'client';
|
|
158
|
-
} else if (
|
|
166
|
+
} else if (
|
|
167
|
+
constructorName === 'Pool' ||
|
|
168
|
+
constructorName === 'BoundPool'
|
|
169
|
+
) {
|
|
159
170
|
value = 'pool';
|
|
160
|
-
} else if (
|
|
171
|
+
} else if (constructorName === 'Sequelize') {
|
|
161
172
|
value = 'sequelize';
|
|
162
173
|
} else {
|
|
163
174
|
throw new Error('storeType is not defined');
|
|
164
175
|
}
|
|
165
176
|
}
|
|
177
|
+
|
|
166
178
|
this._clientType = value.toLowerCase();
|
|
167
179
|
}
|
|
168
180
|
|
|
@@ -174,6 +186,14 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract {
|
|
|
174
186
|
this._tableName = typeof value === 'undefined' ? this.keyPrefix : value;
|
|
175
187
|
}
|
|
176
188
|
|
|
189
|
+
get tableCreated() {
|
|
190
|
+
return this._tableCreated
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
set tableCreated(value) {
|
|
194
|
+
this._tableCreated = typeof value === 'undefined' ? false : !!value;
|
|
195
|
+
}
|
|
196
|
+
|
|
177
197
|
get clearExpiredByTimeout() {
|
|
178
198
|
return this._clearExpiredByTimeout;
|
|
179
199
|
}
|
|
@@ -220,7 +240,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract {
|
|
|
220
240
|
}
|
|
221
241
|
|
|
222
242
|
_upsert(key, points, msDuration, forceExpire = false) {
|
|
223
|
-
if (!this.
|
|
243
|
+
if (!this.tableCreated) {
|
|
224
244
|
return Promise.reject(Error('Table is not created yet'));
|
|
225
245
|
}
|
|
226
246
|
|
|
@@ -248,7 +268,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract {
|
|
|
248
268
|
}
|
|
249
269
|
|
|
250
270
|
_get(rlKey) {
|
|
251
|
-
if (!this.
|
|
271
|
+
if (!this.tableCreated) {
|
|
252
272
|
return Promise.reject(Error('Table is not created yet'));
|
|
253
273
|
}
|
|
254
274
|
|
|
@@ -272,7 +292,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract {
|
|
|
272
292
|
}
|
|
273
293
|
|
|
274
294
|
_delete(rlKey) {
|
|
275
|
-
if (!this.
|
|
295
|
+
if (!this.tableCreated) {
|
|
276
296
|
return Promise.reject(Error('Table is not created yet'));
|
|
277
297
|
}
|
|
278
298
|
|
package/lib/RateLimiterQueue.js
CHANGED
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
const RateLimiterQueueError = require('./component/RateLimiterQueueError')
|
|
2
2
|
const MAX_QUEUE_SIZE = 4294967295;
|
|
3
|
+
const KEY_DEFAULT = 'limiter';
|
|
3
4
|
|
|
4
5
|
module.exports = class RateLimiterQueue {
|
|
6
|
+
constructor(limiterFlexible, opts = {
|
|
7
|
+
maxQueueSize: MAX_QUEUE_SIZE,
|
|
8
|
+
}) {
|
|
9
|
+
this._queueLimiters = {
|
|
10
|
+
KEY_DEFAULT: new RateLimiterQueueInternal(limiterFlexible, opts)
|
|
11
|
+
};
|
|
12
|
+
this._limiterFlexible = limiterFlexible;
|
|
13
|
+
this._maxQueueSize = opts.maxQueueSize
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getTokensRemaining(key = KEY_DEFAULT) {
|
|
17
|
+
if (this._queueLimiters[key]) {
|
|
18
|
+
return this._queueLimiters[key].getTokensRemaining()
|
|
19
|
+
} else {
|
|
20
|
+
return Promise.resolve(this._limiterFlexible.points)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
removeTokens(tokens, key = KEY_DEFAULT) {
|
|
25
|
+
if (!this._queueLimiters[key]) {
|
|
26
|
+
this._queueLimiters[key] = new RateLimiterQueueInternal(
|
|
27
|
+
this._limiterFlexible, {
|
|
28
|
+
key,
|
|
29
|
+
maxQueueSize: this._maxQueueSize,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return this._queueLimiters[key].removeTokens(tokens)
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
class RateLimiterQueueInternal {
|
|
5
38
|
|
|
6
39
|
constructor(limiterFlexible, opts = {
|
|
7
40
|
maxQueueSize: MAX_QUEUE_SIZE,
|
|
41
|
+
key: KEY_DEFAULT,
|
|
8
42
|
}) {
|
|
9
|
-
this._key =
|
|
43
|
+
this._key = opts.key;
|
|
10
44
|
this._waitTimeout = null;
|
|
11
45
|
this._queue = [];
|
|
12
46
|
this._limiterFlexible = limiterFlexible;
|
|
@@ -90,4 +124,4 @@ module.exports = class RateLimiterQueue {
|
|
|
90
124
|
}
|
|
91
125
|
});
|
|
92
126
|
}
|
|
93
|
-
}
|
|
127
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -67,6 +67,7 @@ interface IRateLimiterStoreOptions extends IRateLimiterOptions{
|
|
|
67
67
|
insuranceLimiter?: RateLimiterAbstract;
|
|
68
68
|
dbName?: string;
|
|
69
69
|
tableName?: string;
|
|
70
|
+
tableCreated?: boolean;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
interface IRateLimiterMongoOptions extends IRateLimiterStoreOptions{
|
|
@@ -157,7 +158,11 @@ interface IRateLimiterQueueOpts {
|
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
export class RateLimiterQueue {
|
|
160
|
-
constructor(limiterFlexible: RateLimiterAbstract, opts?: IRateLimiterQueueOpts)
|
|
161
|
+
constructor(limiterFlexible: RateLimiterAbstract, opts?: IRateLimiterQueueOpts);
|
|
162
|
+
|
|
163
|
+
getTokensRemaining(key?: string | number): Promise<RateLimiterRes>;
|
|
164
|
+
|
|
165
|
+
removeTokens(tokens: number, key?: string | number): Promise<RateLimiterRes>;
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
export class BurstyRateLimiter {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rate-limiter-flexible",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.7",
|
|
4
4
|
"description": "Node.js rate limiter by key and protection from DDoS and Brute-Force attacks in process Memory, Redis, MongoDb, Memcached, MySQL, PostgreSQL, Cluster or PM",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|