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 { AdminAuthModule } from './modules/admin-auth/admin-auth.module';
|
||||||
import { AdminDashboardModule } from './modules/admin-dashboard/admin-dashboard.module';
|
import { AdminDashboardModule } from './modules/admin-dashboard/admin-dashboard.module';
|
||||||
import { AdminUsersModule } from './modules/admin-users/admin-users.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 { AppConfigModule } from './modules/config/config.module';
|
||||||
import { AdminEventsModule } from './modules/admin-events/admin-events.module';
|
import { AdminEventsModule } from './modules/admin-events/admin-events.module';
|
||||||
import { AdminKnowledgeModule } from './modules/admin-knowledge/admin-knowledge.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 { StrictValidationPipe } from './common/pipes/strict-validation.pipe';
|
||||||
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
||||||
import { TraceIdInterceptor } from './common/interceptors/trace-id.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 { AiAnalysisWorker } from './workers/ai-analysis.worker';
|
||||||
import { DocumentImportWorker } from './workers/document-import.worker';
|
import { DocumentImportWorker } from './workers/document-import.worker';
|
||||||
@ -88,6 +91,7 @@ import appleConfig from './config/apple.config';
|
|||||||
}),
|
}),
|
||||||
PrismaModule,
|
PrismaModule,
|
||||||
RedisModule,
|
RedisModule,
|
||||||
|
AppThrottleModule,
|
||||||
EventBusModule,
|
EventBusModule,
|
||||||
QueueModule,
|
QueueModule,
|
||||||
AiModule,
|
AiModule,
|
||||||
@ -98,6 +102,7 @@ import appleConfig from './config/apple.config';
|
|||||||
AdminAuthModule,
|
AdminAuthModule,
|
||||||
AdminDashboardModule,
|
AdminDashboardModule,
|
||||||
AdminUsersModule,
|
AdminUsersModule,
|
||||||
|
AdminThrottleModule,
|
||||||
AppConfigModule,
|
AppConfigModule,
|
||||||
AdminEventsModule,
|
AdminEventsModule,
|
||||||
AdminKnowledgeModule,
|
AdminKnowledgeModule,
|
||||||
@ -132,6 +137,7 @@ import appleConfig from './config/apple.config';
|
|||||||
{ provide: APP_FILTER, useClass: GlobalExceptionFilter },
|
{ provide: APP_FILTER, useClass: GlobalExceptionFilter },
|
||||||
{ provide: APP_PIPE, useClass: StrictValidationPipe },
|
{ provide: APP_PIPE, useClass: StrictValidationPipe },
|
||||||
{ provide: APP_INTERCEPTOR, useClass: TraceIdInterceptor },
|
{ provide: APP_INTERCEPTOR, useClass: TraceIdInterceptor },
|
||||||
|
{ provide: APP_INTERCEPTOR, useClass: TimeoutInterceptor },
|
||||||
{ provide: APP_INTERCEPTOR, useClass: ResponseInterceptor },
|
{ provide: APP_INTERCEPTOR, useClass: ResponseInterceptor },
|
||||||
AiAnalysisWorker,
|
AiAnalysisWorker,
|
||||||
DocumentImportWorker,
|
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