api-server/src/common/utils/rate-limit.service.ts

47 lines
1.4 KiB
TypeScript

import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { RedisService } from '../../infrastructure/redis/redis.service';
@Injectable()
export class RateLimitService {
constructor(private readonly redis: RedisService) {}
async checkLimit(
key: string,
maxRequests: number,
windowSeconds: number,
): Promise<void> {
const count = await this.redis.incr(key);
if (count === 1) {
await this.redis.expire(key, windowSeconds);
}
if (count > maxRequests) {
throw new HttpException(
{
statusCode: HttpStatus.TOO_MANY_REQUESTS,
message: `请求过于频繁,请${windowSeconds}秒后再试`,
retryAfter: windowSeconds,
},
HttpStatus.TOO_MANY_REQUESTS,
);
}
}
async loginLimit(ip: string): Promise<void> {
const today = new Date().toISOString().split('T')[0];
await this.checkLimit(`rate:ip:${ip}:login:${today}`, 20, 1800);
}
async feedbackLimit(ip: string): Promise<void> {
await this.checkLimit(`rate:ip:${ip}:feedback:hourly`, 5, 3600);
}
async aiAnalysisLimit(userId: string): Promise<void> {
const today = new Date().toISOString().split('T')[0];
await this.checkLimit(`rate:user:${userId}:ai:daily:${today}`, 50, 86400);
}
async fileUploadLimit(userId: string): Promise<void> {
await this.checkLimit(`rate:user:${userId}:upload:hourly`, 10, 3600);
}
}