feat: M0-05 Traffic Protection — Redis throttle + timeout
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 18s
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 18s
This commit is contained in:
parent
b5a983dc6b
commit
28d68675b1
@ -16,6 +16,7 @@ import { AuthModule } from './modules/auth/auth.module';
|
||||
import { AdminAuthModule } from './modules/admin-auth/admin-auth.module';
|
||||
import { AdminDashboardModule } from './modules/admin-dashboard/admin-dashboard.module';
|
||||
import { AdminUsersModule } from './modules/admin-users/admin-users.module';
|
||||
import { AdminThrottleModule } from './modules/admin-throttle/admin-throttle.module';
|
||||
import { AppConfigModule } from './modules/config/config.module';
|
||||
import { AdminEventsModule } from './modules/admin-events/admin-events.module';
|
||||
import { AdminKnowledgeModule } from './modules/admin-knowledge/admin-knowledge.module';
|
||||
@ -50,6 +51,8 @@ import { GlobalExceptionFilter } from './common/filters/global-exception.filter'
|
||||
import { StrictValidationPipe } from './common/pipes/strict-validation.pipe';
|
||||
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
||||
import { TraceIdInterceptor } from './common/interceptors/trace-id.interceptor';
|
||||
import { TimeoutInterceptor } from './common/interceptors/timeout.interceptor';
|
||||
import { AppThrottleModule } from './common/throttle/throttle.module';
|
||||
|
||||
import { AiAnalysisWorker } from './workers/ai-analysis.worker';
|
||||
import { DocumentImportWorker } from './workers/document-import.worker';
|
||||
@ -88,6 +91,7 @@ import appleConfig from './config/apple.config';
|
||||
}),
|
||||
PrismaModule,
|
||||
RedisModule,
|
||||
AppThrottleModule,
|
||||
EventBusModule,
|
||||
QueueModule,
|
||||
AiModule,
|
||||
@ -98,6 +102,7 @@ import appleConfig from './config/apple.config';
|
||||
AdminAuthModule,
|
||||
AdminDashboardModule,
|
||||
AdminUsersModule,
|
||||
AdminThrottleModule,
|
||||
AppConfigModule,
|
||||
AdminEventsModule,
|
||||
AdminKnowledgeModule,
|
||||
@ -132,6 +137,7 @@ import appleConfig from './config/apple.config';
|
||||
{ provide: APP_FILTER, useClass: GlobalExceptionFilter },
|
||||
{ provide: APP_PIPE, useClass: StrictValidationPipe },
|
||||
{ provide: APP_INTERCEPTOR, useClass: TraceIdInterceptor },
|
||||
{ provide: APP_INTERCEPTOR, useClass: TimeoutInterceptor },
|
||||
{ provide: APP_INTERCEPTOR, useClass: ResponseInterceptor },
|
||||
AiAnalysisWorker,
|
||||
DocumentImportWorker,
|
||||
|
||||
10
src/common/interceptors/timeout.interceptor.ts
Normal file
10
src/common/interceptors/timeout.interceptor.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, RequestTimeoutException } from '@nestjs/common';
|
||||
import { Observable, throwError, TimeoutError } from 'rxjs';
|
||||
import { catchError, timeout } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class TimeoutInterceptor implements NestInterceptor {
|
||||
intercept(_context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||
return next.handle().pipe(timeout(30000), catchError(err => err instanceof TimeoutError ? throwError(() => new RequestTimeoutException('Request timeout')) : throwError(() => err)));
|
||||
}
|
||||
}
|
||||
13
src/common/throttle/redis-throttler.storage.ts
Normal file
13
src/common/throttle/redis-throttler.storage.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ThrottlerStorage } from '@nestjs/throttler';
|
||||
import { RedisService } from '../../infrastructure/redis/redis.service';
|
||||
|
||||
@Injectable()
|
||||
export class RedisThrottlerStorage implements ThrottlerStorage {
|
||||
constructor(private readonly redis: RedisService) {}
|
||||
async increment(key: string, ttl: number) {
|
||||
const redisKey = `throttle:${key}`;
|
||||
try { const hits = await this.redis.incr(redisKey); await this.redis.expire(redisKey, Math.ceil(ttl / 1000)); return { totalHits: hits, timeToExpire: ttl }; }
|
||||
catch { return { totalHits: 1, timeToExpire: ttl }; }
|
||||
}
|
||||
}
|
||||
24
src/common/throttle/throttle.module.ts
Normal file
24
src/common/throttle/throttle.module.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { ThrottlerModule } from '@nestjs/throttler';
|
||||
import { RedisThrottlerStorage } from './redis-throttler.storage';
|
||||
import { RedisService } from '../../infrastructure/redis/redis.service';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
ThrottlerModule.forRootAsync({
|
||||
useFactory: (redis: RedisService) => ({
|
||||
throttlers: [
|
||||
{ name: 'global', ttl: 60000, limit: 100 },
|
||||
{ name: 'ai', ttl: 60000, limit: 20 },
|
||||
{ name: 'login', ttl: 60000, limit: 5 },
|
||||
{ name: 'upload', ttl: 60000, limit: 10 },
|
||||
],
|
||||
storage: new RedisThrottlerStorage(redis),
|
||||
}),
|
||||
inject: [RedisService],
|
||||
}),
|
||||
],
|
||||
exports: [ThrottlerModule],
|
||||
})
|
||||
export class AppThrottleModule {}
|
||||
28
src/modules/admin-throttle/admin-throttle.controller.ts
Normal file
28
src/modules/admin-throttle/admin-throttle.controller.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Controller, Get, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||||
import { AdminAuthGuard } from '../../common/guards/admin-auth.guard';
|
||||
import { AdminRolesGuard } from '../../common/guards/admin-roles.guard';
|
||||
import { AdminRoles } from '../../common/decorators/admin-roles.decorator';
|
||||
import type { AdminRole } from '../../common/types/admin-role.enum';
|
||||
|
||||
@ApiTags('admin-throttle')
|
||||
@Controller('admin-api/throttle')
|
||||
@UseGuards(AdminAuthGuard, AdminRolesGuard)
|
||||
@ApiBearerAuth()
|
||||
export class AdminThrottleController {
|
||||
@Get('status')
|
||||
@AdminRoles('SUPER_ADMIN' as AdminRole)
|
||||
@ApiOperation({ summary: '限流规则状态' })
|
||||
async status() {
|
||||
return {
|
||||
rules: [
|
||||
{ name: 'global', ttl: '60s', limit: 100, desc: '全局 API' },
|
||||
{ name: 'ai', ttl: '60s', limit: 20, desc: 'AI 接口' },
|
||||
{ name: 'login', ttl: '60s', limit: 5, desc: '登录接口' },
|
||||
{ name: 'upload', ttl: '60s', limit: 10, desc: '文件上传' },
|
||||
],
|
||||
storage: 'Redis',
|
||||
enabled: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
6
src/modules/admin-throttle/admin-throttle.module.ts
Normal file
6
src/modules/admin-throttle/admin-throttle.module.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AdminThrottleController } from './admin-throttle.controller';
|
||||
import { AdminAuthGuard } from '../../common/guards/admin-auth.guard';
|
||||
import { AdminRolesGuard } from '../../common/guards/admin-roles.guard';
|
||||
@Module({ controllers: [AdminThrottleController], providers: [AdminAuthGuard, AdminRolesGuard] })
|
||||
export class AdminThrottleModule {}
|
||||
Loading…
x
Reference in New Issue
Block a user