1
1
import { Ratelimit } from "@upstash/ratelimit" ;
2
2
import { Request as ExpressRequest , Response as ExpressResponse , NextFunction } from "express" ;
3
- import Redis , { RedisOptions } from "ioredis" ;
3
+ import { RedisOptions } from "ioredis" ;
4
4
import { createHash } from "node:crypto" ;
5
5
import { env } from "~/env.server" ;
6
6
import { logger } from "./logger.server" ;
7
-
8
- function createRedisRateLimitClient (
9
- redisOptions : RedisOptions
10
- ) : ConstructorParameters < typeof Ratelimit > [ 0 ] [ "redis" ] {
11
- const redis = new Redis ( redisOptions ) ;
12
-
13
- return {
14
- sadd : async < TData > ( key : string , ...members : TData [ ] ) : Promise < number > => {
15
- return redis . sadd ( key , members as ( string | number | Buffer ) [ ] ) ;
16
- } ,
17
- eval : < TArgs extends unknown [ ] , TData = unknown > (
18
- ...args : [ script : string , keys : string [ ] , args : TArgs ]
19
- ) : Promise < TData > => {
20
- const script = args [ 0 ] ;
21
- const keys = args [ 1 ] ;
22
- const argsArray = args [ 2 ] ;
23
- return redis . eval (
24
- script ,
25
- keys . length ,
26
- ...keys ,
27
- ...( argsArray as ( string | Buffer | number ) [ ] )
28
- ) as Promise < TData > ;
29
- } ,
30
- } ;
31
- }
7
+ import { Duration , Limiter , RateLimiter , createRedisRateLimitClient } from "./rateLimiter.server" ;
32
8
33
9
type Options = {
10
+ redis ?: RedisOptions ;
11
+ keyPrefix : string ;
12
+ pathMatchers : ( RegExp | string ) [ ] ;
13
+ pathWhiteList ?: ( RegExp | string ) [ ] ;
14
+ limiter : Limiter ;
34
15
log ?: {
35
16
requests ?: boolean ;
36
17
rejections ?: boolean ;
37
18
} ;
38
- redis : RedisOptions ;
39
- keyPrefix : string ;
40
- pathMatchers : ( RegExp | string ) [ ] ;
41
- pathWhiteList ?: ( RegExp | string ) [ ] ;
42
- limiter : ConstructorParameters < typeof Ratelimit > [ 0 ] [ "limiter" ] ;
43
19
} ;
44
20
45
21
//returns an Express middleware that rate limits using the Bearer token in the Authorization header
@@ -54,12 +30,20 @@ export function authorizationRateLimitMiddleware({
54
30
requests : true ,
55
31
} ,
56
32
} : Options ) {
57
- const rateLimiter = new Ratelimit ( {
58
- redis : createRedisRateLimitClient ( redis ) ,
59
- limiter : limiter ,
60
- ephemeralCache : new Map ( ) ,
61
- analytics : false ,
62
- prefix : keyPrefix ,
33
+ // const rateLimiter = new Ratelimit({
34
+ // redis: createRedisRateLimitClient(redis),
35
+ // limiter: limiter,
36
+ // ephemeralCache: new Map(),
37
+ // analytics: false,
38
+ // prefix: keyPrefix,
39
+ // });
40
+
41
+ const rateLimiter = new RateLimiter ( {
42
+ redis,
43
+ keyPrefix,
44
+ limiter,
45
+ logSuccess : log . requests ,
46
+ logFailure : log . rejections ,
63
47
} ) ;
64
48
65
49
return async ( req : ExpressRequest , res : ExpressResponse , next : NextFunction ) => {
@@ -135,27 +119,9 @@ export function authorizationRateLimitMiddleware({
135
119
res . set ( "x-ratelimit-reset" , reset . toString ( ) ) ;
136
120
137
121
if ( success ) {
138
- if ( log . requests ) {
139
- logger . info ( `RateLimiter (${ keyPrefix } ): under rate limit` , {
140
- limit,
141
- reset,
142
- remaining,
143
- hashedAuthorizationValue,
144
- } ) ;
145
- }
146
122
return next ( ) ;
147
123
}
148
124
149
- if ( log . rejections ) {
150
- logger . warn ( `RateLimiter (${ keyPrefix } ): rate limit exceeded` , {
151
- limit,
152
- reset,
153
- remaining,
154
- pending,
155
- hashedAuthorizationValue,
156
- } ) ;
157
- }
158
-
159
125
res . setHeader ( "Content-Type" , "application/problem+json" ) ;
160
126
const secondsUntilReset = Math . max ( 0 , ( reset - new Date ( ) . getTime ( ) ) / 1000 ) ;
161
127
return res . status ( 429 ) . send (
@@ -167,6 +133,7 @@ export function authorizationRateLimitMiddleware({
167
133
detail : `Rate limit exceeded ${ remaining } /${ limit } requests remaining. Retry in ${ secondsUntilReset } seconds.` ,
168
134
reset,
169
135
limit,
136
+ remaining,
170
137
secondsUntilReset,
171
138
error : `Rate limit exceeded ${ remaining } /${ limit } requests remaining. Retry in ${ secondsUntilReset } seconds.` ,
172
139
} ,
@@ -177,18 +144,8 @@ export function authorizationRateLimitMiddleware({
177
144
} ;
178
145
}
179
146
180
- type Duration = Parameters < typeof Ratelimit . slidingWindow > [ 1 ] ;
181
-
182
147
export const apiRateLimiter = authorizationRateLimitMiddleware ( {
183
148
keyPrefix : "ratelimit:api" ,
184
- redis : {
185
- port : env . REDIS_PORT ,
186
- host : env . REDIS_HOST ,
187
- username : env . REDIS_USERNAME ,
188
- password : env . REDIS_PASSWORD ,
189
- enableAutoPipelining : true ,
190
- ...( env . REDIS_TLS_DISABLED === "true" ? { } : { tls : { } } ) ,
191
- } ,
192
149
limiter : Ratelimit . slidingWindow ( env . API_RATE_LIMIT_MAX , env . API_RATE_LIMIT_WINDOW as Duration ) ,
193
150
pathMatchers : [ / ^ \/ a p i / ] ,
194
151
// Allow /api/v1/tasks/:id/callback/:secret
0 commit comments