@upstash/ratelimit 1.2.0 → 1.2.1-canary
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/dist/index.d.mts +58 -2
- package/dist/index.d.ts +58 -2
- package/dist/index.js +135 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +140 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Aggregate } from '@upstash/core-analytics';
|
|
2
|
+
import { Pipeline } from '@upstash/redis';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* EphemeralCache is used to block certain identifiers right away in case they have already exceeded the ratelimit.
|
|
@@ -83,7 +84,7 @@ type RatelimitResponse = {
|
|
|
83
84
|
/**
|
|
84
85
|
* The value which was in the deny list if reason: "denyList"
|
|
85
86
|
*/
|
|
86
|
-
deniedValue?:
|
|
87
|
+
deniedValue?: DeniedValue;
|
|
87
88
|
};
|
|
88
89
|
type Algorithm<TContext> = () => {
|
|
89
90
|
limit: (ctx: TContext, identifier: string, rate?: number, opts?: {
|
|
@@ -93,6 +94,7 @@ type Algorithm<TContext> = () => {
|
|
|
93
94
|
resetTokens: (ctx: TContext, identifier: string) => Promise<void>;
|
|
94
95
|
};
|
|
95
96
|
type IsDenied = 0 | 1;
|
|
97
|
+
type DeniedValue = string | undefined;
|
|
96
98
|
type LimitOptions = {
|
|
97
99
|
geo?: Geo;
|
|
98
100
|
rate?: number;
|
|
@@ -112,6 +114,7 @@ interface Redis {
|
|
|
112
114
|
evalsha: <TArgs extends unknown[], TData = unknown>(...args: [sha1: string, keys: string[], args: TArgs]) => Promise<TData>;
|
|
113
115
|
scriptLoad: (...args: [script: string]) => Promise<string>;
|
|
114
116
|
smismember: (key: string, members: string[]) => Promise<IsDenied[]>;
|
|
117
|
+
multi: () => Pipeline;
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
type Geo = {
|
|
@@ -237,6 +240,7 @@ type RatelimitConfig<TContext> = {
|
|
|
237
240
|
* @default false
|
|
238
241
|
*/
|
|
239
242
|
enableProtection?: boolean;
|
|
243
|
+
denyListThreshold?: number;
|
|
240
244
|
};
|
|
241
245
|
/**
|
|
242
246
|
* Ratelimiter using serverless redis from https://upstash.com/
|
|
@@ -261,6 +265,7 @@ declare abstract class Ratelimit<TContext extends Context> {
|
|
|
261
265
|
protected readonly primaryRedis: Redis;
|
|
262
266
|
protected readonly analytics?: Analytics;
|
|
263
267
|
protected readonly enableProtection: boolean;
|
|
268
|
+
protected readonly denyListThreshold: number;
|
|
264
269
|
constructor(config: RatelimitConfig<TContext>);
|
|
265
270
|
/**
|
|
266
271
|
* Determine if a request should pass or be rejected based on the identifier and previously chosen ratelimit.
|
|
@@ -561,6 +566,10 @@ type RegionRatelimitConfig = {
|
|
|
561
566
|
* @default false
|
|
562
567
|
*/
|
|
563
568
|
enableProtection?: boolean;
|
|
569
|
+
/**
|
|
570
|
+
* @default 6
|
|
571
|
+
*/
|
|
572
|
+
denyListThreshold?: number;
|
|
564
573
|
};
|
|
565
574
|
/**
|
|
566
575
|
* Ratelimiter using serverless redis from https://upstash.com/
|
|
@@ -699,4 +708,51 @@ declare class RegionRatelimit extends Ratelimit<RegionContext> {
|
|
|
699
708
|
window: Duration): Algorithm<RegionContext>;
|
|
700
709
|
}
|
|
701
710
|
|
|
702
|
-
|
|
711
|
+
declare class ThresholdError extends Error {
|
|
712
|
+
constructor(threshold: number);
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Gets the list of ips from the github source which are not in the
|
|
716
|
+
* deny list already
|
|
717
|
+
*
|
|
718
|
+
* First, gets the ip list from github using the threshold. Then, calls redis with
|
|
719
|
+
* a transaction which does the following:
|
|
720
|
+
* - subtract the current ip deny list from all
|
|
721
|
+
* - delete current ip deny list
|
|
722
|
+
* - recreate ip deny list with the ips from github. Ips already in the users own lists
|
|
723
|
+
* are excluded.
|
|
724
|
+
* - status key is set to valid with ttl until next 2 AM UTC, which is a bit later than
|
|
725
|
+
* when the list is updated on github.
|
|
726
|
+
*
|
|
727
|
+
* @param redis redis instance
|
|
728
|
+
* @param prefix ratelimit prefix
|
|
729
|
+
* @param threshold ips with less than or equal to the threshold are not included
|
|
730
|
+
* @param ttl time to live in milliseconds for the status flag. Optional. If not
|
|
731
|
+
* passed, ttl is infferred from current time.
|
|
732
|
+
* @returns list of ips which are not in the deny list
|
|
733
|
+
*/
|
|
734
|
+
declare const updateIpDenyList: (redis: Redis, prefix: string, threshold: number, ttl?: number) => Promise<unknown[]>;
|
|
735
|
+
/**
|
|
736
|
+
* Disables the ip deny list by removing the ip deny list from the all
|
|
737
|
+
* set and removing the ip deny list. Also sets the status key to disabled
|
|
738
|
+
* with no ttl.
|
|
739
|
+
*
|
|
740
|
+
* @param redis redis instance
|
|
741
|
+
* @param prefix ratelimit prefix
|
|
742
|
+
* @returns
|
|
743
|
+
*/
|
|
744
|
+
declare const disableIpDenyList: (redis: Redis, prefix: string) => Promise<unknown[]>;
|
|
745
|
+
|
|
746
|
+
type ipDenyList_ThresholdError = ThresholdError;
|
|
747
|
+
declare const ipDenyList_ThresholdError: typeof ThresholdError;
|
|
748
|
+
declare const ipDenyList_disableIpDenyList: typeof disableIpDenyList;
|
|
749
|
+
declare const ipDenyList_updateIpDenyList: typeof updateIpDenyList;
|
|
750
|
+
declare namespace ipDenyList {
|
|
751
|
+
export {
|
|
752
|
+
ipDenyList_ThresholdError as ThresholdError,
|
|
753
|
+
ipDenyList_disableIpDenyList as disableIpDenyList,
|
|
754
|
+
ipDenyList_updateIpDenyList as updateIpDenyList,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
export { Algorithm, Analytics, AnalyticsConfig, ipDenyList as IpDenyList, MultiRegionRatelimit, MultiRegionRatelimitConfig, RegionRatelimit as Ratelimit, RegionRatelimitConfig as RatelimitConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Aggregate } from '@upstash/core-analytics';
|
|
2
|
+
import { Pipeline } from '@upstash/redis';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* EphemeralCache is used to block certain identifiers right away in case they have already exceeded the ratelimit.
|
|
@@ -83,7 +84,7 @@ type RatelimitResponse = {
|
|
|
83
84
|
/**
|
|
84
85
|
* The value which was in the deny list if reason: "denyList"
|
|
85
86
|
*/
|
|
86
|
-
deniedValue?:
|
|
87
|
+
deniedValue?: DeniedValue;
|
|
87
88
|
};
|
|
88
89
|
type Algorithm<TContext> = () => {
|
|
89
90
|
limit: (ctx: TContext, identifier: string, rate?: number, opts?: {
|
|
@@ -93,6 +94,7 @@ type Algorithm<TContext> = () => {
|
|
|
93
94
|
resetTokens: (ctx: TContext, identifier: string) => Promise<void>;
|
|
94
95
|
};
|
|
95
96
|
type IsDenied = 0 | 1;
|
|
97
|
+
type DeniedValue = string | undefined;
|
|
96
98
|
type LimitOptions = {
|
|
97
99
|
geo?: Geo;
|
|
98
100
|
rate?: number;
|
|
@@ -112,6 +114,7 @@ interface Redis {
|
|
|
112
114
|
evalsha: <TArgs extends unknown[], TData = unknown>(...args: [sha1: string, keys: string[], args: TArgs]) => Promise<TData>;
|
|
113
115
|
scriptLoad: (...args: [script: string]) => Promise<string>;
|
|
114
116
|
smismember: (key: string, members: string[]) => Promise<IsDenied[]>;
|
|
117
|
+
multi: () => Pipeline;
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
type Geo = {
|
|
@@ -237,6 +240,7 @@ type RatelimitConfig<TContext> = {
|
|
|
237
240
|
* @default false
|
|
238
241
|
*/
|
|
239
242
|
enableProtection?: boolean;
|
|
243
|
+
denyListThreshold?: number;
|
|
240
244
|
};
|
|
241
245
|
/**
|
|
242
246
|
* Ratelimiter using serverless redis from https://upstash.com/
|
|
@@ -261,6 +265,7 @@ declare abstract class Ratelimit<TContext extends Context> {
|
|
|
261
265
|
protected readonly primaryRedis: Redis;
|
|
262
266
|
protected readonly analytics?: Analytics;
|
|
263
267
|
protected readonly enableProtection: boolean;
|
|
268
|
+
protected readonly denyListThreshold: number;
|
|
264
269
|
constructor(config: RatelimitConfig<TContext>);
|
|
265
270
|
/**
|
|
266
271
|
* Determine if a request should pass or be rejected based on the identifier and previously chosen ratelimit.
|
|
@@ -561,6 +566,10 @@ type RegionRatelimitConfig = {
|
|
|
561
566
|
* @default false
|
|
562
567
|
*/
|
|
563
568
|
enableProtection?: boolean;
|
|
569
|
+
/**
|
|
570
|
+
* @default 6
|
|
571
|
+
*/
|
|
572
|
+
denyListThreshold?: number;
|
|
564
573
|
};
|
|
565
574
|
/**
|
|
566
575
|
* Ratelimiter using serverless redis from https://upstash.com/
|
|
@@ -699,4 +708,51 @@ declare class RegionRatelimit extends Ratelimit<RegionContext> {
|
|
|
699
708
|
window: Duration): Algorithm<RegionContext>;
|
|
700
709
|
}
|
|
701
710
|
|
|
702
|
-
|
|
711
|
+
declare class ThresholdError extends Error {
|
|
712
|
+
constructor(threshold: number);
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Gets the list of ips from the github source which are not in the
|
|
716
|
+
* deny list already
|
|
717
|
+
*
|
|
718
|
+
* First, gets the ip list from github using the threshold. Then, calls redis with
|
|
719
|
+
* a transaction which does the following:
|
|
720
|
+
* - subtract the current ip deny list from all
|
|
721
|
+
* - delete current ip deny list
|
|
722
|
+
* - recreate ip deny list with the ips from github. Ips already in the users own lists
|
|
723
|
+
* are excluded.
|
|
724
|
+
* - status key is set to valid with ttl until next 2 AM UTC, which is a bit later than
|
|
725
|
+
* when the list is updated on github.
|
|
726
|
+
*
|
|
727
|
+
* @param redis redis instance
|
|
728
|
+
* @param prefix ratelimit prefix
|
|
729
|
+
* @param threshold ips with less than or equal to the threshold are not included
|
|
730
|
+
* @param ttl time to live in milliseconds for the status flag. Optional. If not
|
|
731
|
+
* passed, ttl is infferred from current time.
|
|
732
|
+
* @returns list of ips which are not in the deny list
|
|
733
|
+
*/
|
|
734
|
+
declare const updateIpDenyList: (redis: Redis, prefix: string, threshold: number, ttl?: number) => Promise<unknown[]>;
|
|
735
|
+
/**
|
|
736
|
+
* Disables the ip deny list by removing the ip deny list from the all
|
|
737
|
+
* set and removing the ip deny list. Also sets the status key to disabled
|
|
738
|
+
* with no ttl.
|
|
739
|
+
*
|
|
740
|
+
* @param redis redis instance
|
|
741
|
+
* @param prefix ratelimit prefix
|
|
742
|
+
* @returns
|
|
743
|
+
*/
|
|
744
|
+
declare const disableIpDenyList: (redis: Redis, prefix: string) => Promise<unknown[]>;
|
|
745
|
+
|
|
746
|
+
type ipDenyList_ThresholdError = ThresholdError;
|
|
747
|
+
declare const ipDenyList_ThresholdError: typeof ThresholdError;
|
|
748
|
+
declare const ipDenyList_disableIpDenyList: typeof disableIpDenyList;
|
|
749
|
+
declare const ipDenyList_updateIpDenyList: typeof updateIpDenyList;
|
|
750
|
+
declare namespace ipDenyList {
|
|
751
|
+
export {
|
|
752
|
+
ipDenyList_ThresholdError as ThresholdError,
|
|
753
|
+
ipDenyList_disableIpDenyList as disableIpDenyList,
|
|
754
|
+
ipDenyList_updateIpDenyList as updateIpDenyList,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
export { Algorithm, Analytics, AnalyticsConfig, ipDenyList as IpDenyList, MultiRegionRatelimit, MultiRegionRatelimitConfig, RegionRatelimit as Ratelimit, RegionRatelimitConfig as RatelimitConfig };
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
Analytics: () => Analytics,
|
|
24
|
+
IpDenyList: () => ip_deny_list_exports,
|
|
24
25
|
MultiRegionRatelimit: () => MultiRegionRatelimit,
|
|
25
26
|
Ratelimit: () => RegionRatelimit
|
|
26
27
|
});
|
|
@@ -294,7 +295,107 @@ var resetScript = `
|
|
|
294
295
|
until cursor == "0"
|
|
295
296
|
`;
|
|
296
297
|
|
|
297
|
-
// src/
|
|
298
|
+
// src/types.ts
|
|
299
|
+
var DenyListExtension = "denyList";
|
|
300
|
+
var IpDenyListKey = "ipDenyList";
|
|
301
|
+
var IpDenyListStatusKey = "ipDenyListStatus";
|
|
302
|
+
|
|
303
|
+
// src/deny-list/scripts.ts
|
|
304
|
+
var checkDenyListScript = `
|
|
305
|
+
-- Checks if values provideed in ARGV are present in the deny lists.
|
|
306
|
+
-- This is done using the allDenyListsKey below.
|
|
307
|
+
|
|
308
|
+
-- Additionally, checks the status of the ip deny list using the
|
|
309
|
+
-- ipDenyListStatusKey below. Here are the possible states of the
|
|
310
|
+
-- ipDenyListStatusKey key:
|
|
311
|
+
-- * status == -1: set to "disabled" with no TTL
|
|
312
|
+
-- * status == -2: not set, meaning that is was set before but expired
|
|
313
|
+
-- * status > 0: set to "valid", with a TTL
|
|
314
|
+
--
|
|
315
|
+
-- In the case of status == -2, we set the status to "pending" with
|
|
316
|
+
-- 30 second ttl. During this time, the process which got status == -2
|
|
317
|
+
-- will update the ip deny list.
|
|
318
|
+
|
|
319
|
+
local allDenyListsKey = KEYS[1]
|
|
320
|
+
local ipDenyListStatusKey = KEYS[2]
|
|
321
|
+
|
|
322
|
+
local results = redis.call('SMISMEMBER', allDenyListsKey, unpack(ARGV))
|
|
323
|
+
local status = redis.call('TTL', ipDenyListStatusKey)
|
|
324
|
+
if status == -2 then
|
|
325
|
+
redis.call('SETEX', ipDenyListStatusKey, 30, "pending")
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
return { results, status }
|
|
329
|
+
`;
|
|
330
|
+
|
|
331
|
+
// src/deny-list/ip-deny-list.ts
|
|
332
|
+
var ip_deny_list_exports = {};
|
|
333
|
+
__export(ip_deny_list_exports, {
|
|
334
|
+
ThresholdError: () => ThresholdError,
|
|
335
|
+
disableIpDenyList: () => disableIpDenyList,
|
|
336
|
+
updateIpDenyList: () => updateIpDenyList
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// src/deny-list/time.ts
|
|
340
|
+
var MILLISECONDS_IN_HOUR = 60 * 60 * 1e3;
|
|
341
|
+
var MILLISECONDS_IN_DAY = 24 * MILLISECONDS_IN_HOUR;
|
|
342
|
+
var MILLISECONDS_TO_2AM = 2 * MILLISECONDS_IN_HOUR;
|
|
343
|
+
var getIpListTTL = (time) => {
|
|
344
|
+
const now = time || Date.now();
|
|
345
|
+
const timeSinceLast2AM = (now - MILLISECONDS_TO_2AM) % MILLISECONDS_IN_DAY;
|
|
346
|
+
return MILLISECONDS_IN_DAY - timeSinceLast2AM;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// src/deny-list/ip-deny-list.ts
|
|
350
|
+
var baseUrl = "https://raw.githubusercontent.com/stamparm/ipsum/master/levels";
|
|
351
|
+
var ThresholdError = class extends Error {
|
|
352
|
+
constructor(threshold) {
|
|
353
|
+
super(`Allowed threshold values are from 1 to 8, 1 and 8 included. Received: ${threshold}`);
|
|
354
|
+
this.name = "ThresholdError";
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
var getIpDenyList = async (threshold) => {
|
|
358
|
+
if (typeof threshold !== "number" || threshold < 1 || threshold > 8) {
|
|
359
|
+
throw new ThresholdError(threshold);
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const response = await fetch(`${baseUrl}/${threshold}.txt`);
|
|
363
|
+
if (!response.ok) {
|
|
364
|
+
throw new Error(`Error fetching data: ${response.statusText}`);
|
|
365
|
+
}
|
|
366
|
+
const data = await response.text();
|
|
367
|
+
const lines = data.split("\n");
|
|
368
|
+
return lines.filter((value) => value.length > 0);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
throw new Error(`Failed to fetch ip deny list: ${error}`);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
var updateIpDenyList = async (redis, prefix, threshold, ttl) => {
|
|
374
|
+
const allIps = await getIpDenyList(threshold);
|
|
375
|
+
const allDenyLists = [prefix, DenyListExtension, "all"].join(":");
|
|
376
|
+
const ipDenyList = [prefix, DenyListExtension, IpDenyListKey].join(":");
|
|
377
|
+
const statusKey = [prefix, IpDenyListStatusKey].join(":");
|
|
378
|
+
const transaction = redis.multi();
|
|
379
|
+
transaction.sdiffstore(allDenyLists, allDenyLists, ipDenyList);
|
|
380
|
+
transaction.del(ipDenyList);
|
|
381
|
+
transaction.sadd(ipDenyList, ...allIps);
|
|
382
|
+
transaction.sdiffstore(ipDenyList, ipDenyList, allDenyLists);
|
|
383
|
+
transaction.sunionstore(allDenyLists, allDenyLists, ipDenyList);
|
|
384
|
+
transaction.set(statusKey, "valid", { px: ttl ?? getIpListTTL() });
|
|
385
|
+
return await transaction.exec();
|
|
386
|
+
};
|
|
387
|
+
var disableIpDenyList = async (redis, prefix) => {
|
|
388
|
+
const allDenyListsKey = [prefix, DenyListExtension, "all"].join(":");
|
|
389
|
+
const ipDenyListKey = [prefix, DenyListExtension, IpDenyListKey].join(":");
|
|
390
|
+
const statusKey = [prefix, IpDenyListStatusKey].join(":");
|
|
391
|
+
const transaction = redis.multi();
|
|
392
|
+
transaction.sdiffstore(allDenyListsKey, allDenyListsKey, ipDenyListKey);
|
|
393
|
+
transaction.del(ipDenyListKey);
|
|
394
|
+
transaction.set(statusKey, "disabled");
|
|
395
|
+
return await transaction.exec();
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// src/deny-list/deny-list.ts
|
|
298
399
|
var denyListCache = new Cache(/* @__PURE__ */ new Map());
|
|
299
400
|
var checkDenyListCache = (members) => {
|
|
300
401
|
return members.find(
|
|
@@ -307,25 +408,39 @@ var blockMember = (member) => {
|
|
|
307
408
|
denyListCache.blockUntil(member, Date.now() + 6e4);
|
|
308
409
|
};
|
|
309
410
|
var checkDenyList = async (redis, prefix, members) => {
|
|
310
|
-
const
|
|
311
|
-
|
|
411
|
+
const [deniedValues, ipDenyListStatus] = await redis.eval(
|
|
412
|
+
checkDenyListScript,
|
|
413
|
+
[
|
|
414
|
+
[prefix, DenyListExtension, "all"].join(":"),
|
|
415
|
+
[prefix, IpDenyListStatusKey].join(":")
|
|
416
|
+
],
|
|
312
417
|
members
|
|
313
418
|
);
|
|
314
|
-
let
|
|
315
|
-
|
|
419
|
+
let deniedValue = void 0;
|
|
420
|
+
deniedValues.map((memberDenied, index) => {
|
|
316
421
|
if (memberDenied) {
|
|
317
422
|
blockMember(members[index]);
|
|
318
|
-
|
|
423
|
+
deniedValue = members[index];
|
|
319
424
|
}
|
|
320
425
|
});
|
|
321
|
-
return
|
|
426
|
+
return {
|
|
427
|
+
deniedValue,
|
|
428
|
+
invalidIpDenyList: ipDenyListStatus === -2
|
|
429
|
+
};
|
|
322
430
|
};
|
|
323
|
-
var
|
|
324
|
-
if (denyListResponse) {
|
|
431
|
+
var resolveLimitPayload = (redis, prefix, [ratelimitResponse, denyListResponse], threshold) => {
|
|
432
|
+
if (denyListResponse.deniedValue) {
|
|
325
433
|
ratelimitResponse.success = false;
|
|
326
434
|
ratelimitResponse.remaining = 0;
|
|
327
435
|
ratelimitResponse.reason = "denyList";
|
|
328
|
-
ratelimitResponse.deniedValue = denyListResponse;
|
|
436
|
+
ratelimitResponse.deniedValue = denyListResponse.deniedValue;
|
|
437
|
+
}
|
|
438
|
+
if (denyListResponse.invalidIpDenyList) {
|
|
439
|
+
const updatePromise = updateIpDenyList(redis, prefix, threshold);
|
|
440
|
+
ratelimitResponse.pending = Promise.all([
|
|
441
|
+
ratelimitResponse.pending,
|
|
442
|
+
updatePromise
|
|
443
|
+
]);
|
|
329
444
|
}
|
|
330
445
|
return ratelimitResponse;
|
|
331
446
|
};
|
|
@@ -350,12 +465,14 @@ var Ratelimit = class {
|
|
|
350
465
|
primaryRedis;
|
|
351
466
|
analytics;
|
|
352
467
|
enableProtection;
|
|
468
|
+
denyListThreshold;
|
|
353
469
|
constructor(config) {
|
|
354
470
|
this.ctx = config.ctx;
|
|
355
471
|
this.limiter = config.limiter;
|
|
356
472
|
this.timeout = config.timeout ?? 5e3;
|
|
357
473
|
this.prefix = config.prefix ?? "@upstash/ratelimit";
|
|
358
474
|
this.enableProtection = config.enableProtection ?? false;
|
|
475
|
+
this.denyListThreshold = config.denyListThreshold ?? 6;
|
|
359
476
|
this.primaryRedis = "redis" in this.ctx ? this.ctx.redis : this.ctx.regionContexts[0].redis;
|
|
360
477
|
this.analytics = config.analytics ? new Analytics({
|
|
361
478
|
redis: this.primaryRedis,
|
|
@@ -485,21 +602,17 @@ var Ratelimit = class {
|
|
|
485
602
|
getRatelimitResponse = async (identifier, req) => {
|
|
486
603
|
const key = this.getKey(identifier);
|
|
487
604
|
const definedMembers = this.getDefinedMembers(identifier, req);
|
|
488
|
-
const
|
|
605
|
+
const deniedValue = checkDenyListCache(definedMembers);
|
|
489
606
|
let result;
|
|
490
|
-
if (
|
|
491
|
-
result = [defaultDeniedResponse(
|
|
607
|
+
if (deniedValue) {
|
|
608
|
+
result = [defaultDeniedResponse(deniedValue), { deniedValue, invalidIpDenyList: false }];
|
|
492
609
|
} else {
|
|
493
610
|
result = await Promise.all([
|
|
494
611
|
this.limiter().limit(this.ctx, key, req?.rate),
|
|
495
|
-
checkDenyList(
|
|
496
|
-
this.primaryRedis,
|
|
497
|
-
this.prefix,
|
|
498
|
-
definedMembers
|
|
499
|
-
)
|
|
612
|
+
this.enableProtection ? checkDenyList(this.primaryRedis, this.prefix, definedMembers) : { deniedValue: void 0, invalidIpDenyList: false }
|
|
500
613
|
]);
|
|
501
614
|
}
|
|
502
|
-
return
|
|
615
|
+
return resolveLimitPayload(this.primaryRedis, this.prefix, result, this.denyListThreshold);
|
|
503
616
|
};
|
|
504
617
|
/**
|
|
505
618
|
* Creates an array with the original response promise and a timeout promise
|
|
@@ -1109,7 +1222,8 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1109
1222
|
cacheScripts: config.cacheScripts ?? true
|
|
1110
1223
|
},
|
|
1111
1224
|
ephemeralCache: config.ephemeralCache,
|
|
1112
|
-
enableProtection: config.enableProtection
|
|
1225
|
+
enableProtection: config.enableProtection,
|
|
1226
|
+
denyListThreshold: config.denyListThreshold
|
|
1113
1227
|
});
|
|
1114
1228
|
}
|
|
1115
1229
|
/**
|
|
@@ -1476,6 +1590,7 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1476
1590
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1477
1591
|
0 && (module.exports = {
|
|
1478
1592
|
Analytics,
|
|
1593
|
+
IpDenyList,
|
|
1479
1594
|
MultiRegionRatelimit,
|
|
1480
1595
|
Ratelimit
|
|
1481
1596
|
});
|