strażniku bramy! Konsul Caesar.js zauważył, że nasza brama miejska staje się coraz bardziej zatłoczona - niektóre oddziały próbują wejść zbyt często, blokując dostęp innym. Czas nauczyć się rate limitingu - sztuki kontrolowania ruchu, aby każdy mógł bezpiecznie korzystać z naszych usług!
Wyobraź sobie bramę rzymskiego miasta, przez którą setki oddziałów próbuje jednocześnie przejść. Bez odpowiedniej kontroli nastąpi chaos - kolizje, blokady, a nawet przestoje. Rate limiting to system kontroli ruchu, który:
1// rate-limiter.service.ts
2import { Injectable, Logger } from '@nestjs/common';
3import { ConfigService } from '@nestjs/config';
4
5export interface RateLimitConfig {
6 windowMs: number; // Okno czasowe w milisekundach
7 maxRequests: number; // Maksymalna liczba żądań w oknie
8 blockDuration: number; // Czas blokady po przekroczeniu limitu
9}
10
11export interface RateLimitInfo {
12 remaining: number;
13 resetTime: number;
14 blocked: boolean;
15 blockUntil?: number;
16}
17
18@Injectable()
19export class RateLimiterService {
20 private readonly logger = new Logger(RateLimiterService.name);
21 private readonly rateLimits = new Map<string, RateLimitData>();
22 private readonly blockedUsers = new Map<string, number>();
23
24 constructor(private configService: ConfigService) {}
25
26 async checkRateLimit(
27 clientId: string,
28 config: RateLimitConfig
29 ): Promise<RateLimitInfo> {
30 const now = Date.now();
31
32 // Sprawdź czy klient jest zablokowany
33 const blockUntil = this.blockedUsers.get(clientId);
34 if (blockUntil && now < blockUntil) {
35 return {
36 remaining: 0,
37 resetTime: blockUntil,
38 blocked: true,
39 blockUntil
40 };
41 }
42
43 // Usuń blokadę jeśli wygasła
44 if (blockUntil && now >= blockUntil) {
45 this.blockedUsers.delete(clientId);
46 }
47
48 // Pobierz lub utwórz dane rate limit
49 let rateLimitData = this.rateLimits.get(clientId);
50
51 if (!rateLimitData || now - rateLimitData.windowStart > config.windowMs) {
52 // Nowe okno czasowe
53 rateLimitData = {
54 requests: [],
55 windowStart: now,
56 consecutiveViolations: rateLimitData?.consecutiveViolations || 0
57 };
58 }
59
60 // Usuń stare żądania spoza okna
61 rateLimitData.requests = rateLimitData.requests.filter(
62 timestamp => timestamp > now - config.windowMs
63 );
64
65 // Sprawdź czy przekroczono limit
66 if (rateLimitData.requests.length >= config.maxRequests) {
67 rateLimitData.consecutiveViolations++;
68
69 // Dynamiczne wydłużanie czasu blokady przy powtarzających się naruszeniach
70 const blockDuration = config.blockDuration *
71 Math.pow(2, Math.min(rateLimitData.consecutiveViolations - 1, 5));
72
73 const blockUntilTime = now + blockDuration;
74 this.blockedUsers.set(clientId, blockUntilTime);
75
76 this.logger.warn(`Rate limit exceeded for ${clientId}. Blocked until ${new Date(blockUntilTime)}`);
77
78 return {
79 remaining: 0,
80 resetTime: rateLimitData.windowStart + config.windowMs,
81 blocked: true,
82 blockUntil: blockUntilTime
83 };
84 }
85
86 // Dodaj nowe żądanie
87 rateLimitData.requests.push(now);
88 rateLimitData.consecutiveViolations = Math.max(0, rateLimitData.consecutiveViolations - 0.1);
89 this.rateLimits.set(clientId, rateLimitData);
90
91 return {
92 remaining: config.maxRequests - rateLimitData.requests.length,
93 resetTime: rateLimitData.windowStart + config.windowMs,
94 blocked: false
95 };
96 }
97
98 // Sprawdź statusy wszystkich klientów
99 getAllLimits(): Map<string, any> {
100 const now = Date.now();
101 const result = new Map();
102
103 this.rateLimits.forEach((data, clientId) => {
104 const blocked = this.blockedUsers.get(clientId);
105 result.set(clientId, {
106 requestsInWindow: data.requests.length,
107 windowStart: data.windowStart,
108 consecutiveViolations: data.consecutiveViolations,
109 blocked: blocked && now < blocked,
110 blockUntil: blocked
111 });
112 });
113
114 return result;
115 }
116}
117
118interface RateLimitData {
119 requests: number[];
120 windowStart: number;
121 consecutiveViolations: number;
122}1// redis-rate-limiter.service.ts
2import { Injectable, Logger } from '@nestjs/common';
3import { InjectRedis } from '@nestjs-modules/ioredis';
4import Redis from 'ioredis';
5
6@Injectable()
7export class RedisRateLimiterService {
8 private readonly logger = new Logger(RedisRateLimiterService.name);
9
10 constructor(@InjectRedis() private readonly redis: Redis) {}
11
12 async checkRateLimit(
13 clientId: string,
14 config: RateLimitConfig
15 ): Promise<RateLimitInfo> {
16 const now = Date.now();
17 const windowStart = Math.floor(now / config.windowMs) * config.windowMs;
18 const windowKey = `rate_limit:${clientId}:${windowStart}`;
19 const blockKey = `blocked:${clientId}`;
20
21 // Sprawdź blokadę
22 const blockUntil = await this.redis.get(blockKey);
23 if (blockUntil && now < parseInt(blockUntil)) {
24 return {
25 remaining: 0,
26 resetTime: windowStart + config.windowMs,
27 blocked: true,
28 blockUntil: parseInt(blockUntil)
29 };
30 }
31
32 // Lua script dla atomowej operacji
33 const luaScript = `
34 local windowKey = KEYS[1]
35 local blockKey = KEYS[2]
36 local maxRequests = tonumber(ARGV[1])
37 local windowMs = tonumber(ARGV[2])
38 local blockDuration = tonumber(ARGV[3])
39 local now = tonumber(ARGV[4])
40
41 -- Pobierz bieżącą liczbę żądań
42 local current = redis.call('GET', windowKey)
43 if current == false then
44 current = 0
45 else
46 current = tonumber(current)
47 end
48
49 -- Sprawdź limit
50 if current >= maxRequests then
51 -- Ustaw blokadę
52 redis.call('SET', blockKey, now + blockDuration, 'PX', blockDuration)
53 return {current, maxRequests, 1} -- blocked = 1
54 else
55 -- Zwiększ licznik
56 current = current + 1
57 redis.call('SET', windowKey, current, 'PX', windowMs)
58 return {current, maxRequests, 0} -- blocked = 0
59 end
60 `;
61
62 const result = await this.redis.eval(
63 luaScript,
64 2,
65 windowKey,
66 blockKey,
67 config.maxRequests.toString(),
68 config.windowMs.toString(),
69 config.blockDuration.toString(),
70 now.toString()
71 ) as number[];
72
73 const [current, maxRequests, blocked] = result;
74
75 if (blocked) {
76 const blockUntilTime = now + config.blockDuration;
77 this.logger.warn(`Redis rate limit exceeded for ${clientId}`);
78
79 return {
80 remaining: 0,
81 resetTime: windowStart + config.windowMs,
82 blocked: true,
83 blockUntil: blockUntilTime
84 };
85 }
86
87 return {
88 remaining: maxRequests - current,
89 resetTime: windowStart + config.windowMs,
90 blocked: false
91 };
92 }
93
94 // Sliding Window Rate Limiter
95 async checkSlidingWindowLimit(
96 clientId: string,
97 maxRequests: number,
98 windowMs: number
99 ): Promise<RateLimitInfo> {
100 const now = Date.now();
101 const windowStart = now - windowMs;
102 const key = `sliding:${clientId}`;
103
104 const luaScript = `
105 local key = KEYS[1]
106 local windowStart = tonumber(ARGV[1])
107 local now = tonumber(ARGV[2])
108 local maxRequests = tonumber(ARGV[3])
109 local windowMs = tonumber(ARGV[4])
110
111 -- Usuń stare wpisy
112 redis.call('ZREMRANGEBYSCORE', key, 0, windowStart)
113
114 -- Pobierz liczbę żądań w oknie
115 local current = redis.call('ZCARD', key)
116
117 if current >= maxRequests then
118 return {current, maxRequests, 1}
119 else
120 -- Dodaj nowe żądanie
121 redis.call('ZADD', key, now, now)
122 redis.call('EXPIRE', key, math.ceil(windowMs / 1000))
123 return {current + 1, maxRequests, 0}
124 end
125 `;
126
127 const result = await this.redis.eval(
128 luaScript,
129 1,
130 key,
131 windowStart.toString(),
132 now.toString(),
133 maxRequests.toString(),
134 windowMs.toString()
135 ) as number[];
136
137 const [current, max, blocked] = result;
138
139 return {
140 remaining: max - current,
141 resetTime: now + windowMs,
142 blocked: !!blocked
143 };
144 }
145}1// rate-limit.guard.ts
2import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
3import { Reflector } from '@nestjs/core';
4import { RateLimiterService, RateLimitConfig } from './rate-limiter.service';
5import { TooManyRequestsException } from '@nestjs/common';
6
7export const RateLimit = (config: Partial<RateLimitConfig>) =>
8 SetMetadata('rateLimit', config);
9
10@Injectable()
11export class RateLimitGuard implements CanActivate {
12 constructor(
13 private rateLimiter: RateLimiterService,
14 private reflector: Reflector
15 ) {}
16
17 async canActivate(context: ExecutionContext): Promise<boolean> {
18 const rateLimitConfig = this.reflector.getAllAndOverride<Partial<RateLimitConfig>>('rateLimit', [
19 context.getHandler(),
20 context.getClass(),
21 ]);
22
23 if (!rateLimitConfig) {
24 return true; // Brak rate limitingu
25 }
26
27 const request = context.switchToHttp().getRequest();
28 const response = context.switchToHttp().getResponse();
29
30 // Identyfikuj klienta
31 const clientId = this.getClientId(request);
32
33 // Domyślna konfiguracja
34 const config: RateLimitConfig = {
35 windowMs: 60000, // 1 minuta
36 maxRequests: 100, // 100 żądań
37 blockDuration: 300000, // 5 minut blokady
38 ...rateLimitConfig
39 };
40
41 const rateLimitInfo = await this.rateLimiter.checkRateLimit(clientId, config);
42
43 // Dodaj headers z informacjami o rate limiting
44 response.setHeader('X-RateLimit-Limit', config.maxRequests);
45 response.setHeader('X-RateLimit-Remaining', rateLimitInfo.remaining);
46 response.setHeader('X-RateLimit-Reset', rateLimitInfo.resetTime);
47
48 if (rateLimitInfo.blocked) {
49 response.setHeader('Retry-After', Math.ceil(
50 (rateLimitInfo.blockUntil! - Date.now()) / 1000
51 ));
52
53 throw new TooManyRequestsException({
54 message: 'Przekroczono limit żądań! Port jest przeciążony! ',
55 retryAfter: rateLimitInfo.blockUntil,
56 resetTime: rateLimitInfo.resetTime,
57 clientId: this.maskClientId(clientId)
58 });
59 }
60
61 return true;
62 }
63
64 private getClientId(request: any): string {
65 // Priorytet identyfikacji:
66 // 1. Zalogowany użytkownik
67 // 2. API key
68 // 3. IP address
69
70 if (request.user?.id) {
71 return `user:${request.user.id}`;
72 }
73
74 if (request.headers['x-api-key']) {
75 return `api:${request.headers['x-api-key']}`;
76 }
77
78 // IP z proxy/load balancer
79 const forwarded = request.headers['x-forwarded-for'];
80 const ip = forwarded ? forwarded.split(',')[0] : request.connection.remoteAddress;
81
82 return `ip:${ip}`;
83 }
84
85 private maskClientId(clientId: string): string {
86 if (clientId.startsWith('ip:')) {
87 const ip = clientId.substring(3);
88 const parts = ip.split('.');
89 return parts.length === 4 ? `${parts[0]}.${parts[1]}.*.${parts[3]}` : ip;
90 }
91 return clientId;
92 }
93}1// adaptive-rate-limiter.service.ts
2@Injectable()
3export class AdaptiveRateLimiterService {
4 private readonly logger = new Logger(AdaptiveRateLimiterService.name);
5 private metrics = new Map<string, ClientMetrics>();
6
7 constructor(
8 private rateLimiter: RateLimiterService,
9 private performanceMonitor: PerformanceMonitorService
10 ) {}
11
12 async checkAdaptiveRateLimit(clientId: string): Promise<RateLimitInfo> {
13 const serverLoad = await this.performanceMonitor.getCurrentLoad();
14 const clientMetrics = this.getClientMetrics(clientId);
15
16 // Oblicz dynamiczny limit na podstawie:
17 // 1. Obciążenia serwera
18 // 2. Historii klienta
19 // 3. Czasu dnia
20 // 4. Typu klienta
21
22 const baseLimit = this.getBaseLimit(clientId);
23 const adaptiveConfig = this.calculateAdaptiveConfig(
24 baseLimit,
25 serverLoad,
26 clientMetrics
27 );
28
29 const result = await this.rateLimiter.checkRateLimit(clientId, adaptiveConfig);
30
31 // Aktualizuj metryki klienta
32 this.updateClientMetrics(clientId, result);
33
34 return result;
35 }
36
37 private calculateAdaptiveConfig(
38 baseLimit: RateLimitConfig,
39 serverLoad: ServerLoad,
40 clientMetrics: ClientMetrics
41 ): RateLimitConfig {
42 let multiplier = 1.0;
43
44 // 1. Obciążenie serwera
45 if (serverLoad.cpu > 80) {
46 multiplier *= 0.5; // Zmniejsz limit o 50% przy wysokim CPU
47 } else if (serverLoad.cpu > 60) {
48 multiplier *= 0.7; // Zmniejsz o 30%
49 } else if (serverLoad.cpu < 30) {
50 multiplier *= 1.5; // Zwiększ przy niskim obciążeniu
51 }
52
53 // 2. Obciążenie pamięci
54 if (serverLoad.memory > 85) {
55 multiplier *= 0.6;
56 }
57
58 // 3. Historia klienta
59 if (clientMetrics.trustScore > 0.8) {
60 multiplier *= 1.2; // Bonus dla zaufanych klientów
61 } else if (clientMetrics.trustScore < 0.3) {
62 multiplier *= 0.5; // Kara dla problematycznych klientów
63 }
64
65 // 4. Czas dnia (mniej ruchu w nocy)
66 const hour = new Date().getHours();
67 if (hour >= 2 && hour <= 6) {
68 multiplier *= 2.0; // Wyższe limity w nocy
69 } else if (hour >= 9 && hour <= 17) {
70 multiplier *= 0.8; // Niższe w godzinach szczytu
71 }
72
73 // 5. Typ żądań
74 if (clientMetrics.errorRate > 0.1) {
75 multiplier *= 0.3; // Drastyczne ograniczenie dla klientów z błędami
76 }
77
78 return {
79 ...baseLimit,
80 maxRequests: Math.max(1, Math.floor(baseLimit.maxRequests * multiplier)),
81 blockDuration: baseLimit.blockDuration * (2 - multiplier) // Dłuższe blokady dla problemowych
82 };
83 }
84
85 private getBaseLimit(clientId: string): RateLimitConfig {
86 // Różne limity dla różnych typów klientów
87 if (clientId.startsWith('user:')) {
88 return { windowMs: 60000, maxRequests: 1000, blockDuration: 300000 };
89 }
90 if (clientId.startsWith('api:')) {
91 return { windowMs: 60000, maxRequests: 5000, blockDuration: 180000 };
92 }
93 // IP-based (najniższy limit)
94 return { windowMs: 60000, maxRequests: 100, blockDuration: 600000 };
95 }
96
97 private getClientMetrics(clientId: string): ClientMetrics {
98 if (!this.metrics.has(clientId)) {
99 this.metrics.set(clientId, {
100 trustScore: 0.5,
101 errorRate: 0,
102 avgResponseTime: 0,
103 totalRequests: 0,
104 violations: 0,
105 lastSeen: Date.now()
106 });
107 }
108 return this.metrics.get(clientId)!;
109 }
110
111 private updateClientMetrics(clientId: string, rateLimitResult: RateLimitInfo) {
112 const metrics = this.getClientMetrics(clientId);
113
114 metrics.totalRequests++;
115 metrics.lastSeen = Date.now();
116
117 if (rateLimitResult.blocked) {
118 metrics.violations++;
119 metrics.trustScore = Math.max(0, metrics.trustScore - 0.1);
120 } else {
121 // Powolne zwiększanie trust score
122 metrics.trustScore = Math.min(1, metrics.trustScore + 0.001);
123 }
124
125 // Oblicz nowy trust score na podstawie historii
126 const violationRate = metrics.violations / metrics.totalRequests;
127 metrics.trustScore = Math.max(0, 1 - (violationRate * 2));
128 }
129}
130
131interface ClientMetrics {
132 trustScore: number; // 0-1, gdzie 1 = w pełni zaufany
133 errorRate: number; // Procent błędnych żądań
134 avgResponseTime: number; // Średni czas odpowiedzi
135 totalRequests: number; // Łączna liczba żądań
136 violations: number; // Liczba naruszeń rate limit
137 lastSeen: number; // Ostatnie żądanie
138}
139
140interface ServerLoad {
141 cpu: number; // 0-100%
142 memory: number; // 0-100%
143 activeConnections: number;
144}1// rate-limit.middleware.ts
2@Injectable()
3export class RateLimitMiddleware implements NestMiddleware {
4 constructor(
5 private adaptiveRateLimiter: AdaptiveRateLimiterService,
6 private logger: Logger
7 ) {}
8
9 async use(req: Request, res: Response, next: NextFunction) {
10 const startTime = Date.now();
11
12 try {
13 const clientId = this.getClientId(req);
14 const rateLimitResult = await this.adaptiveRateLimiter.checkAdaptiveRateLimit(clientId);
15
16 // Dodaj informacje do request
17 req['rateLimitInfo'] = rateLimitResult;
18 req['clientId'] = clientId;
19
20 // Dodaj headers
21 res.setHeader('X-RateLimit-Remaining', rateLimitResult.remaining);
22 res.setHeader('X-RateLimit-Reset', rateLimitResult.resetTime);
23
24 if (rateLimitResult.blocked) {
25 return res.status(429).json({
26 error: 'Rate limit exceeded',
27 message: 'Port jest przeciążony! Spróbuj później! 🦅',
28 retryAfter: rateLimitResult.blockUntil,
29 clientId: this.maskClientId(clientId)
30 });
31 }
32
33 // Monitoring wydajności rate limitera
34 const duration = Date.now() - startTime;
35 if (duration > 10) {
36 this.logger.warn(`Slow rate limit check: ${duration}ms for ${clientId}`);
37 }
38
39 next();
40 } catch (error) {
41 this.logger.error('Rate limit middleware error:', error);
42 // W przypadku błędu, pozwól na przejście (fail open)
43 next();
44 }
45 }
46
47 private getClientId(req: Request): string {
48 // Implementacja identyczna jak w guard
49 return 'ip:' + (req.ip || req.connection.remoteAddress);
50 }
51
52 private maskClientId(clientId: string): string {
53 // Implementacja identyczna jak w guard
54 return clientId;
55 }
56}1// legion-api.controller.ts
2@Controller('api/legion')
3@UseGuards(RateLimitGuard)
4@RateLimit({ windowMs: 60000, maxRequests: 1000 }) // Domyślny limit dla kontrolera
5export class LegionApiController {
6
7 // Endpoint publiczny - niższy limit
8 @Get('status')
9 @RateLimit({ maxRequests: 10, windowMs: 60000 })
10 getLegionStatus() {
11 return { status: 'Legion operational', cohorts: 12 };
12 }
13
14 // Endpoint wymagający uwierzytelnienia - wyższy limit
15 @Post('cohorts')
16 @UseGuards(AuthGuard)
17 @RateLimit({ maxRequests: 100, windowMs: 60000 })
18 createLegion(@Body() cohortData: CreateLegionDto) {
19 return this.legionService.createLegion(cohortData);
20 }
21
22 // Operacja kosztowna - bardzo niski limit
23 @Post('calculate-route')
24 @RateLimit({ maxRequests: 5, windowMs: 300000, blockDuration: 600000 })
25 calculateOptimalRoute(@Body() routeData: RouteCalculationDto) {
26 return this.navigationService.calculateComplexRoute(routeData);
27 }
28
29 // Endpoint do upload'u - specjalny limit na podstawie rozmiaru
30 @Post('upload-map')
31 @UseInterceptors(FileInterceptor('map'))
32 @RateLimit({ maxRequests: 3, windowMs: 600000 }) // 3 pliki na 10 minut
33 uploadMap(@UploadedFile() file: Express.Multer.File) {
34 return this.mapService.processMap(file);
35 }
36
37 // Admin endpoint - bez limitów dla adminów
38 @Get('admin/metrics')
39 @UseGuards(AdminGuard)
40 getMetrics() {
41 return this.rateLimiter.getAllLimits();
42 }
43}1// rate-limit-monitor.controller.ts
2@Controller('admin/rate-limits')
3@UseGuards(AdminGuard)
4export class RateLimitMonitorController {
5 constructor(
6 private rateLimiter: RateLimiterService,
7 private adaptiveRateLimiter: AdaptiveRateLimiterService
8 ) {}
9
10 @Get('dashboard')
11 getDashboard() {
12 const allLimits = this.rateLimiter.getAllLimits();
13 const stats = this.calculateStats(allLimits);
14
15 return {
16 summary: stats,
17 clients: Array.from(allLimits.entries()).map(([clientId, data]) => ({
18 clientId: this.maskSensitiveInfo(clientId),
19 ...data,
20 riskLevel: this.calculateRiskLevel(data)
21 })),
22 timestamp: new Date()
23 };
24 }
25
26 @Get('top-violators')
27 getTopViolators(@Query('limit') limit: string = '10') {
28 const allLimits = this.rateLimiter.getAllLimits();
29
30 return Array.from(allLimits.entries())
31 .filter(([_, data]) => data.consecutiveViolations > 0)
32 .sort(([,a], [,b]) => b.consecutiveViolations - a.consecutiveViolations)
33 .slice(0, parseInt(limit))
34 .map(([clientId, data]) => ({
35 clientId: this.maskSensitiveInfo(clientId),
36 violations: data.consecutiveViolations,
37 blocked: data.blocked,
38 riskLevel: this.calculateRiskLevel(data)
39 }));
40 }
41
42 @Post('unblock/:clientId')
43 unblockClient(@Param('clientId') clientId: string) {
44 // Implementacja odblokowania klienta
45 return { message: `Client ${clientId} has been unblocked` };
46 }
47
48 private calculateStats(allLimits: Map<string, any>) {
49 let totalClients = allLimits.size;
50 let blockedClients = 0;
51 let totalViolations = 0;
52 let highRiskClients = 0;
53
54 allLimits.forEach(data => {
55 if (data.blocked) blockedClients++;
56 totalViolations += data.consecutiveViolations;
57 if (data.consecutiveViolations > 5) highRiskClients++;
58 });
59
60 return {
61 totalClients,
62 blockedClients,
63 activeClients: totalClients - blockedClients,
64 totalViolations,
65 highRiskClients,
66 blockRate: totalClients > 0 ? (blockedClients / totalClients * 100).toFixed(2) + '%' : '0%'
67 };
68 }
69
70 private calculateRiskLevel(data: any): 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' {
71 if (data.consecutiveViolations > 10) return 'CRITICAL';
72 if (data.consecutiveViolations > 5) return 'HIGH';
73 if (data.consecutiveViolations > 2) return 'MEDIUM';
74 return 'LOW';
75 }
76
77 private maskSensitiveInfo(clientId: string): string {
78 if (clientId.startsWith('ip:')) {
79 const ip = clientId.substring(3);
80 const parts = ip.split('.');
81 return parts.length === 4 ? `ip:${parts[0]}.${parts[1]}.*.${parts[3]}` : clientId;
82 }
83 if (clientId.startsWith('user:')) {
84 return `user:****${clientId.slice(-4)}`;
85 }
86 return clientId;
87 }
88}1// rate-limit.config.ts
2export class RateLimitConfig {
3 static readonly DEFAULT_CONFIGS = {
4 // Publiczne API
5 public: {
6 windowMs: 60000, // 1 minuta
7 maxRequests: 100, // 100 żądań
8 blockDuration: 300000 // 5 minut
9 },
10
11 // Uwierzytelnieni użytkownicy
12 authenticated: {
13 windowMs: 60000,
14 maxRequests: 1000,
15 blockDuration: 180000 // 3 minuty
16 },
17
18 // API keys
19 apiKey: {
20 windowMs: 60000,
21 maxRequests: 5000,
22 blockDuration: 120000 // 2 minuty
23 },
24
25 // Operacje kosztowne
26 expensive: {
27 windowMs: 300000, // 5 minut
28 maxRequests: 10,
29 blockDuration: 900000 // 15 minut
30 },
31
32 // Upload plików
33 upload: {
34 windowMs: 600000, // 10 minut
35 maxRequests: 5,
36 blockDuration: 1800000 // 30 minut
37 }
38 };
39
40 static getConfig(type: string): RateLimitConfig {
41 return this.DEFAULT_CONFIGS[type] || this.DEFAULT_CONFIGS.public;
42 }
43}
44
45// main.ts - globalna konfiguracja
46async function bootstrap() {
47 const app = await NestFactory.create(AppModule);
48
49 // Globalny rate limiting middleware
50 app.use(new RateLimitMiddleware(
51 app.get(AdaptiveRateLimiterService),
52 new Logger('RateLimit')
53 ).use.bind(app.get(RateLimitMiddleware)));
54
55 await app.listen(3000);
56}Rate limiting to sztuka zachowania równowagi między ochroną systemu a dostępnością usług. Podobnie jak doświadczony strażnik bramy, musisz znać swoje limity i elastycznie dostosowywać reguły do bieżących warunków!