forked from Chatbot/v3-api
49 lines
1.6 KiB
TypeScript
49 lines
1.6 KiB
TypeScript
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { Request, Response } from 'express';
|
|
import { rateLimit } from 'express-rate-limit';
|
|
|
|
@Injectable()
|
|
export class IpRateLimiterGuard implements CanActivate {
|
|
private readonly limiter: ReturnType<typeof rateLimit>;
|
|
|
|
constructor(private readonly configService: ConfigService) {
|
|
const isProduction = this.configService.get('NODE_ENV') === 'production';
|
|
this.limiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: isProduction ? 50 : 100, // Stricter in production
|
|
standardHeaders: false,
|
|
legacyHeaders: false,
|
|
skip: () =>
|
|
!isProduction &&
|
|
this.configService.get('DISABLE_RATE_LIMIT') === 'true', // Skip in dev if needed
|
|
keyGenerator: (req) => {
|
|
// Use IP or user ID if available
|
|
return isProduction ? req.ip : ''; // Less strict in development
|
|
},
|
|
handler: (req, res) => {
|
|
res.status(429).json({
|
|
statusCode: 429,
|
|
message: isProduction
|
|
? 'Too many requests'
|
|
: 'Dev Mode: Rate limit warning (not enforced)',
|
|
});
|
|
},
|
|
});
|
|
}
|
|
|
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
const httpContext = context.switchToHttp();
|
|
const req = httpContext.getRequest<Request>();
|
|
const res = httpContext.getResponse<Response>();
|
|
return new Promise((resolve) => {
|
|
this.limiter(req, res, (err?: unknown) => {
|
|
if (err) {
|
|
return resolve(false);
|
|
}
|
|
resolve(true);
|
|
});
|
|
});
|
|
}
|
|
}
|