feat: add rate limit plugin with Redis
Made-with: Cursor
This commit is contained in:
59
src/plugins/rateLimit.ts
Normal file
59
src/plugins/rateLimit.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { FastifyInstance, FastifyPluginAsync } from 'fastify';
|
||||||
|
import rateLimit from '@fastify/rate-limit';
|
||||||
|
import fp from 'fastify-plugin';
|
||||||
|
import { env } from '../config/env.js';
|
||||||
|
|
||||||
|
declare module 'fastify' {
|
||||||
|
interface FastifyInstance {
|
||||||
|
rateLimitOptions: {
|
||||||
|
login: { max: number; timeWindow: string };
|
||||||
|
register: { max: number; timeWindow: string };
|
||||||
|
forgotPassword: { max: number; timeWindow: string };
|
||||||
|
verifyEmail: { max: number; timeWindow: string };
|
||||||
|
apiAuthed: { max: number; timeWindow: string };
|
||||||
|
apiGuest: { max: number; timeWindow: string };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rateLimitPlugin: FastifyPluginAsync = async (app: FastifyInstance) => {
|
||||||
|
const options = {
|
||||||
|
login: { max: env.RATE_LIMIT_LOGIN, timeWindow: '15 minutes' },
|
||||||
|
register: { max: env.RATE_LIMIT_REGISTER, timeWindow: '1 hour' },
|
||||||
|
forgotPassword: { max: env.RATE_LIMIT_FORGOT_PASSWORD, timeWindow: '1 hour' },
|
||||||
|
verifyEmail: { max: env.RATE_LIMIT_VERIFY_EMAIL, timeWindow: '15 minutes' },
|
||||||
|
apiAuthed: { max: env.RATE_LIMIT_API_AUTHED, timeWindow: '1 minute' },
|
||||||
|
apiGuest: { max: env.RATE_LIMIT_API_GUEST, timeWindow: '1 minute' },
|
||||||
|
};
|
||||||
|
|
||||||
|
app.decorate('rateLimitOptions', options);
|
||||||
|
|
||||||
|
await app.register(rateLimit, {
|
||||||
|
max: options.apiGuest.max,
|
||||||
|
timeWindow: options.apiGuest.timeWindow,
|
||||||
|
keyGenerator: (req) => {
|
||||||
|
return (req.ip ?? 'unknown') as string;
|
||||||
|
},
|
||||||
|
redis: app.redis,
|
||||||
|
addHeadersOnExceeding: {
|
||||||
|
'x-ratelimit-limit': true,
|
||||||
|
'x-ratelimit-remaining': true,
|
||||||
|
'x-ratelimit-reset': true,
|
||||||
|
},
|
||||||
|
addHeaders: {
|
||||||
|
'x-ratelimit-limit': true,
|
||||||
|
'x-ratelimit-remaining': true,
|
||||||
|
'x-ratelimit-reset': true,
|
||||||
|
'retry-after': true,
|
||||||
|
},
|
||||||
|
errorResponseBuilder: (_req, context) => ({
|
||||||
|
error: {
|
||||||
|
code: 'RATE_LIMIT_EXCEEDED',
|
||||||
|
message: 'Too many requests, please try again later',
|
||||||
|
retryAfter: context.ttl,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default fp(rateLimitPlugin, { name: 'rateLimit', dependencies: ['redis'] });
|
||||||
Reference in New Issue
Block a user