({
fastify,
max,
timeWindow,
keyGenerator,
}: {
fastify: FastifyInstance;
max: number;
timeWindow?: string;
keyGenerator?: (req: T) => string | undefined;
})
| 2 | import type { FastifyInstance, FastifyRequest } from 'fastify'; |
| 3 | |
| 4 | export async function activateRateLimiter<T extends FastifyRequest>({ |
| 5 | fastify, |
| 6 | max, |
| 7 | timeWindow, |
| 8 | keyGenerator, |
| 9 | }: { |
| 10 | fastify: FastifyInstance; |
| 11 | max: number; |
| 12 | timeWindow?: string; |
| 13 | keyGenerator?: (req: T) => string | undefined; |
| 14 | }) { |
| 15 | await fastify.register(import('@fastify/rate-limit'), { |
| 16 | max, |
| 17 | timeWindow: timeWindow || '1 minute', |
| 18 | errorResponseBuilder: (req, reply) => { |
| 19 | return { |
| 20 | statusCode: 429, |
| 21 | error: 'Too Many Requests', |
| 22 | message: 'You have exceeded the rate limit for this endpoint.', |
| 23 | }; |
| 24 | }, |
| 25 | // In test mode use in-memory storage so tests don't need a running Redis |
| 26 | redis: process.env.NODE_ENV !== 'test' ? getRedisCache() : undefined, |
| 27 | keyGenerator(req) { |
| 28 | if (keyGenerator) { |
| 29 | const key = keyGenerator(req as T); |
| 30 | if (key) { |
| 31 | return key; |
| 32 | } |
| 33 | } |
| 34 | return (req.headers['openpanel-client-id'] || |
| 35 | req.headers['x-real-ip'] || |
| 36 | req.headers['x-client-ip'] || |
| 37 | req.headers['x-forwarded-for']) as string; |
| 38 | }, |
| 39 | onExceeded: (req, reply) => { |
| 40 | req.log.warn( |
| 41 | { clientId: req.headers['openpanel-client-id'] }, |
| 42 | 'Rate limit exceeded', |
| 43 | ); |
| 44 | }, |
| 45 | }); |
| 46 | } |
no test coverage detected