@noony-serverless/core 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/core/core.d.ts +16 -48
- package/build/core/core.js +2 -61
- package/build/core/handler.d.ts +37 -16
- package/build/core/handler.js +131 -42
- package/build/core/index.d.ts +0 -1
- package/build/core/index.js +0 -1
- package/build/middlewares/ConsolidatedValidationMiddleware.d.ts +126 -0
- package/build/middlewares/ConsolidatedValidationMiddleware.js +330 -0
- package/build/middlewares/ProcessingMiddleware.d.ts +138 -0
- package/build/middlewares/ProcessingMiddleware.js +425 -0
- package/build/middlewares/SecurityMiddleware.d.ts +157 -0
- package/build/middlewares/SecurityMiddleware.js +307 -0
- package/build/middlewares/bodyValidationMiddleware.d.ts +12 -10
- package/build/middlewares/bodyValidationMiddleware.js +10 -8
- package/build/middlewares/guards/RouteGuards.d.ts +239 -4
- package/build/middlewares/guards/RouteGuards.js +301 -8
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.d.ts +271 -0
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.js +301 -0
- package/build/middlewares/guards/config/GuardConfiguration.d.ts +50 -0
- package/build/middlewares/guards/config/GuardConfiguration.js +59 -0
- package/build/middlewares/guards/guards/FastAuthGuard.d.ts +5 -5
- package/build/middlewares/guards/guards/PermissionGuardFactory.d.ts +5 -13
- package/build/middlewares/guards/guards/PermissionGuardFactory.js +4 -4
- package/build/middlewares/guards/index.d.ts +43 -1
- package/build/middlewares/guards/index.js +46 -1
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.js +1 -1
- package/build/middlewares/guards/resolvers/PermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/PlainPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/WildcardPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/services/FastUserContextService.d.ts +20 -33
- package/build/middlewares/guards/services/FastUserContextService.js +17 -4
- package/build/middlewares/httpAttributesMiddleware.js +1 -1
- package/build/middlewares/index.d.ts +3 -1
- package/build/middlewares/index.js +6 -1
- package/build/middlewares/rateLimitingMiddleware.d.ts +492 -4
- package/build/middlewares/rateLimitingMiddleware.js +514 -6
- package/package.json +11 -9
- package/build/core/containerPool.d.ts +0 -44
- package/build/core/containerPool.js +0 -103
- package/build/middlewares/validationMiddleware.d.ts +0 -154
- package/build/middlewares/validationMiddleware.js +0 -185
|
@@ -89,6 +89,37 @@ declare class MemoryStore implements RateLimitStore {
|
|
|
89
89
|
* Rate Limiting Middleware with sliding window implementation.
|
|
90
90
|
* Implements comprehensive rate limiting with dynamic limits, custom storage, and security features.
|
|
91
91
|
*
|
|
92
|
+
* ## When to Use RateLimitingMiddleware
|
|
93
|
+
*
|
|
94
|
+
* ### ✅ Recommended Use Cases:
|
|
95
|
+
* - **Business Logic Rate Limiting**: User-specific quotas, subscription-based limits
|
|
96
|
+
* - **Authentication & Security**: Login attempts, password resets, token refresh
|
|
97
|
+
* - **Content Creation**: Post creation, comment submission, profile updates
|
|
98
|
+
* - **Resource-Intensive Operations**: File uploads, data processing, complex queries
|
|
99
|
+
* - **Advanced Scenarios**: A/B testing limits, geographic restrictions, time-based rules
|
|
100
|
+
*
|
|
101
|
+
* ### ❌ Not Recommended Use Cases:
|
|
102
|
+
* - **Basic DDoS Protection**: Use WAF/CloudFlare instead
|
|
103
|
+
* - **Static Asset Protection**: Use CDN rate limiting
|
|
104
|
+
* - **Simple Volumetric Attacks**: Network-level solutions more effective
|
|
105
|
+
*
|
|
106
|
+
* ## Architecture Integration
|
|
107
|
+
*
|
|
108
|
+
* ### With WAF (Web Application Firewall):
|
|
109
|
+
* - WAF handles: IP blocking, DDoS protection, bot detection
|
|
110
|
+
* - Application handles: Business logic, user-aware limits, complex rules
|
|
111
|
+
* - Focus on complementary functionality, not duplication
|
|
112
|
+
*
|
|
113
|
+
* ### With API Gateway:
|
|
114
|
+
* - Gateway handles: Service-level limits, routing quotas, load balancing
|
|
115
|
+
* - Application handles: User context, subscription limits, feature-specific rules
|
|
116
|
+
* - Different layers for different concerns
|
|
117
|
+
*
|
|
118
|
+
* ### Direct Exposure (No WAF/Gateway):
|
|
119
|
+
* - Application must handle comprehensive protection
|
|
120
|
+
* - Implement multiple protection layers within middleware
|
|
121
|
+
* - Critical for security in simple deployments
|
|
122
|
+
*
|
|
92
123
|
* @implements {BaseMiddleware}
|
|
93
124
|
*
|
|
94
125
|
* @example
|
|
@@ -149,6 +180,99 @@ declare class MemoryStore implements RateLimitStore {
|
|
|
149
180
|
* return { success: true, limit: 'applied dynamically' };
|
|
150
181
|
* });
|
|
151
182
|
* ```
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* Multi-layer defense (with WAF):
|
|
186
|
+
* ```typescript
|
|
187
|
+
* // WAF handles basic IP limits (10,000/min), DDoS protection
|
|
188
|
+
* // Application refines with business logic
|
|
189
|
+
* const wafAwareHandler = new Handler()
|
|
190
|
+
* .use(new RateLimitingMiddleware({
|
|
191
|
+
* maxRequests: 100, // Refined limit after WAF filtering
|
|
192
|
+
* windowMs: 60000,
|
|
193
|
+
* dynamicLimits: {
|
|
194
|
+
* premium: {
|
|
195
|
+
* maxRequests: 500,
|
|
196
|
+
* windowMs: 60000,
|
|
197
|
+
* matcher: (context) => context.user?.plan === 'premium'
|
|
198
|
+
* }
|
|
199
|
+
* },
|
|
200
|
+
* keyGenerator: (context) => `user:${context.user?.id || context.req.ip}`
|
|
201
|
+
* }))
|
|
202
|
+
* .handle(async (context) => {
|
|
203
|
+
* // Business logic with user-aware limits
|
|
204
|
+
* return await processUserRequest(context);
|
|
205
|
+
* });
|
|
206
|
+
* ```
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* API Gateway integration:
|
|
210
|
+
* ```typescript
|
|
211
|
+
* // Gateway: 10,000 req/hour per service, 1,000 req/min per endpoint
|
|
212
|
+
* // Application: User-specific limits within gateway envelope
|
|
213
|
+
* const gatewayAwareHandler = new Handler()
|
|
214
|
+
* .use(new RateLimitingMiddleware({
|
|
215
|
+
* maxRequests: 100, // Per user within gateway limits
|
|
216
|
+
* windowMs: 60000,
|
|
217
|
+
* dynamicLimits: {
|
|
218
|
+
* freeTrial: {
|
|
219
|
+
* maxRequests: 10,
|
|
220
|
+
* windowMs: 60000,
|
|
221
|
+
* matcher: (context) => context.user?.trialExpired === false
|
|
222
|
+
* },
|
|
223
|
+
* enterprise: {
|
|
224
|
+
* maxRequests: 5000,
|
|
225
|
+
* windowMs: 60000,
|
|
226
|
+
* matcher: (context) => context.user?.plan === 'enterprise'
|
|
227
|
+
* }
|
|
228
|
+
* },
|
|
229
|
+
* keyGenerator: (context) => `user:${context.user?.id}`
|
|
230
|
+
* }))
|
|
231
|
+
* .handle(async (context) => {
|
|
232
|
+
* // Refined business logic limits
|
|
233
|
+
* return await handleApiRequest(context);
|
|
234
|
+
* });
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* Comprehensive protection (no WAF/Gateway):
|
|
239
|
+
* ```typescript
|
|
240
|
+
* // Application must handle all protection layers
|
|
241
|
+
* const comprehensiveHandler = new Handler()
|
|
242
|
+
* .use(new RateLimitingMiddleware({
|
|
243
|
+
* maxRequests: 100,
|
|
244
|
+
* windowMs: 60000,
|
|
245
|
+
* dynamicLimits: {
|
|
246
|
+
* // IP-based protection (WAF-like)
|
|
247
|
+
* suspicious_ip: {
|
|
248
|
+
* maxRequests: 10,
|
|
249
|
+
* windowMs: 60000,
|
|
250
|
+
* matcher: (context) => detectSuspiciousIP(context.req.ip)
|
|
251
|
+
* },
|
|
252
|
+
* // Endpoint-specific (Gateway-like)
|
|
253
|
+
* auth_endpoint: {
|
|
254
|
+
* maxRequests: 5,
|
|
255
|
+
* windowMs: 60000,
|
|
256
|
+
* matcher: (context) => context.req.path?.includes('/auth/')
|
|
257
|
+
* },
|
|
258
|
+
* // Business logic (Application-specific)
|
|
259
|
+
* user_specific: {
|
|
260
|
+
* maxRequests: 1000,
|
|
261
|
+
* windowMs: 60000,
|
|
262
|
+
* matcher: (context) => !!context.user
|
|
263
|
+
* }
|
|
264
|
+
* },
|
|
265
|
+
* keyGenerator: (context) => {
|
|
266
|
+
* const user = context.user?.id;
|
|
267
|
+
* const ip = context.req.ip;
|
|
268
|
+
* const endpoint = context.req.path;
|
|
269
|
+
* return user ? `user:${user}` : `ip:${ip}:${endpoint}`;
|
|
270
|
+
* }
|
|
271
|
+
* }))
|
|
272
|
+
* .handle(async (context) => {
|
|
273
|
+
* return await processRequest(context);
|
|
274
|
+
* });
|
|
275
|
+
* ```
|
|
152
276
|
*/
|
|
153
277
|
export declare class RateLimitingMiddleware implements BaseMiddleware {
|
|
154
278
|
private store;
|
|
@@ -160,6 +284,70 @@ export declare class RateLimitingMiddleware implements BaseMiddleware {
|
|
|
160
284
|
* Factory function that creates a rate limiting middleware.
|
|
161
285
|
* Provides flexible rate limiting with configurable options and presets.
|
|
162
286
|
*
|
|
287
|
+
* ## Architecture Decision Matrix
|
|
288
|
+
*
|
|
289
|
+
* | Infrastructure | WAF Rate Limiting | Gateway Rate Limiting | Application Rate Limiting |
|
|
290
|
+
* |----------------|------------------|---------------------|-------------------------|
|
|
291
|
+
* | **WAF + Gateway + App** | ✅ Basic DDoS protection | ✅ Service-level limits | ✅ Business logic |
|
|
292
|
+
* | **Gateway + App** | ❌ Not available | ✅ Service + IP limits | ✅ User context + business |
|
|
293
|
+
* | **WAF + App** | ✅ Network protection | ❌ Not available | ✅ All business logic |
|
|
294
|
+
* | **App Only** | ❌ Must implement | ❌ Must implement | ✅ Everything |
|
|
295
|
+
*
|
|
296
|
+
* ## Implementation Strategy by Architecture
|
|
297
|
+
*
|
|
298
|
+
* ### Multi-Layer Defense (Recommended for Enterprise)
|
|
299
|
+
* ```typescript
|
|
300
|
+
* // WAF Layer: 10,000 req/min per IP (CloudFlare/AWS WAF)
|
|
301
|
+
* // Gateway Layer: 1,000 req/min per API key (Kong/AWS API Gateway)
|
|
302
|
+
* // Application Layer: User-specific business rules (This middleware)
|
|
303
|
+
*
|
|
304
|
+
* const enterprise = rateLimiting({
|
|
305
|
+
* maxRequests: 100, // Refined after other layers
|
|
306
|
+
* dynamicLimits: {
|
|
307
|
+
* premium: { maxRequests: 500, matcher: (ctx) => ctx.user?.plan === 'premium' }
|
|
308
|
+
* }
|
|
309
|
+
* });
|
|
310
|
+
* ```
|
|
311
|
+
*
|
|
312
|
+
* ### Gateway + Application (Good for Most Applications)
|
|
313
|
+
* ```typescript
|
|
314
|
+
* // Gateway: Service capacity protection
|
|
315
|
+
* // Application: Business logic enforcement
|
|
316
|
+
*
|
|
317
|
+
* const standard = rateLimiting({
|
|
318
|
+
* maxRequests: 200, // Higher since Gateway pre-filters
|
|
319
|
+
* dynamicLimits: {
|
|
320
|
+
* authenticated: { maxRequests: 1000, matcher: (ctx) => !!ctx.user }
|
|
321
|
+
* }
|
|
322
|
+
* });
|
|
323
|
+
* ```
|
|
324
|
+
*
|
|
325
|
+
* ### Application Only (Comprehensive Protection Required)
|
|
326
|
+
* ```typescript
|
|
327
|
+
* // Must handle all layers of protection
|
|
328
|
+
*
|
|
329
|
+
* const comprehensive = rateLimiting({
|
|
330
|
+
* maxRequests: 50, // Conservative default
|
|
331
|
+
* dynamicLimits: {
|
|
332
|
+
* // WAF-like: IP protection
|
|
333
|
+
* suspicious: { maxRequests: 5, matcher: (ctx) => detectSuspicious(ctx.req.ip) },
|
|
334
|
+
* // Gateway-like: Endpoint protection
|
|
335
|
+
* auth: { maxRequests: 10, matcher: (ctx) => ctx.req.path?.includes('/auth/') },
|
|
336
|
+
* // Business: User-specific
|
|
337
|
+
* premium: { maxRequests: 1000, matcher: (ctx) => ctx.user?.plan === 'premium' }
|
|
338
|
+
* }
|
|
339
|
+
* });
|
|
340
|
+
* ```
|
|
341
|
+
*
|
|
342
|
+
* ## Cost-Benefit Analysis
|
|
343
|
+
*
|
|
344
|
+
* | Architecture | Setup Complexity | Runtime Cost | Protection Level | Maintenance |
|
|
345
|
+
* |-------------|-----------------|-------------|-----------------|-------------|
|
|
346
|
+
* | **WAF + Gateway + App** | High | High | Maximum | Medium |
|
|
347
|
+
* | **Gateway + App** | Medium | Medium | Good | Low |
|
|
348
|
+
* | **WAF + App** | Medium | Medium | Good | Medium |
|
|
349
|
+
* | **App Only** | Low | Low | Variable | High |
|
|
350
|
+
*
|
|
163
351
|
* @param options - Rate limiting configuration options
|
|
164
352
|
* @returns BaseMiddleware instance
|
|
165
353
|
*
|
|
@@ -203,14 +391,113 @@ export declare class RateLimitingMiddleware implements BaseMiddleware {
|
|
|
203
391
|
* return { success: true, message: 'Request processed' };
|
|
204
392
|
* });
|
|
205
393
|
* ```
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* Production Redis store integration:
|
|
397
|
+
* ```typescript
|
|
398
|
+
* import Redis from 'ioredis';
|
|
399
|
+
*
|
|
400
|
+
* class RedisRateLimitStore implements RateLimitStore {
|
|
401
|
+
* constructor(private redis: Redis) {}
|
|
402
|
+
*
|
|
403
|
+
* async increment(key: string, windowMs: number) {
|
|
404
|
+
* const multi = this.redis.multi();
|
|
405
|
+
* multi.incr(key);
|
|
406
|
+
* multi.expire(key, Math.ceil(windowMs / 1000));
|
|
407
|
+
* const results = await multi.exec();
|
|
408
|
+
* return { count: results![0][1] as number, resetTime: Date.now() + windowMs };
|
|
409
|
+
* }
|
|
410
|
+
* }
|
|
411
|
+
*
|
|
412
|
+
* const productionHandler = new Handler()
|
|
413
|
+
* .use(rateLimiting({
|
|
414
|
+
* store: new RedisRateLimitStore(redisClient),
|
|
415
|
+
* maxRequests: 1000,
|
|
416
|
+
* windowMs: 60000
|
|
417
|
+
* }))
|
|
418
|
+
* .handle(async (context) => {
|
|
419
|
+
* return await handleHighVolumeAPI(context);
|
|
420
|
+
* });
|
|
421
|
+
* ```
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* Multi-dimensional rate limiting:
|
|
425
|
+
* ```typescript
|
|
426
|
+
* const advancedHandler = new Handler()
|
|
427
|
+
* .use(rateLimiting({
|
|
428
|
+
* maxRequests: 100,
|
|
429
|
+
* windowMs: 60000,
|
|
430
|
+
* dynamicLimits: {
|
|
431
|
+
* // Different limits by operation type
|
|
432
|
+
* read_operations: {
|
|
433
|
+
* maxRequests: 1000,
|
|
434
|
+
* windowMs: 60000,
|
|
435
|
+
* matcher: (context) => context.req.method === 'GET'
|
|
436
|
+
* },
|
|
437
|
+
* write_operations: {
|
|
438
|
+
* maxRequests: 50,
|
|
439
|
+
* windowMs: 60000,
|
|
440
|
+
* matcher: (context) => ['POST', 'PUT', 'DELETE'].includes(context.req.method || '')
|
|
441
|
+
* },
|
|
442
|
+
* // Different limits by user tier
|
|
443
|
+
* enterprise_users: {
|
|
444
|
+
* maxRequests: 5000,
|
|
445
|
+
* windowMs: 60000,
|
|
446
|
+
* matcher: (context) => context.user?.tier === 'enterprise'
|
|
447
|
+
* }
|
|
448
|
+
* },
|
|
449
|
+
* keyGenerator: (context) => {
|
|
450
|
+
* // Multi-dimensional key: user + operation type
|
|
451
|
+
* const userId = context.user?.id || context.req.ip;
|
|
452
|
+
* const operation = context.req.method === 'GET' ? 'read' : 'write';
|
|
453
|
+
* return `${operation}:${userId}`;
|
|
454
|
+
* }
|
|
455
|
+
* }))
|
|
456
|
+
* .handle(async (context) => {
|
|
457
|
+
* return await processAdvancedRequest(context);
|
|
458
|
+
* });
|
|
459
|
+
* ```
|
|
206
460
|
*/
|
|
207
461
|
export declare const rateLimiting: (options?: RateLimitOptions) => BaseMiddleware;
|
|
208
462
|
/**
|
|
209
|
-
* Predefined rate limit configurations
|
|
463
|
+
* Predefined rate limit configurations for common use cases.
|
|
464
|
+
*
|
|
465
|
+
* These presets are designed to work well in different infrastructure scenarios:
|
|
466
|
+
* - WAF + Application: Higher limits since WAF pre-filters traffic
|
|
467
|
+
* - Gateway + Application: Moderate limits complementing gateway quotas
|
|
468
|
+
* - Application Only: Conservative limits for comprehensive protection
|
|
469
|
+
*
|
|
470
|
+
* ## Preset Selection Guide
|
|
471
|
+
*
|
|
472
|
+
* | Preset | Use Case | Infrastructure | Requests/Min |
|
|
473
|
+
* |--------|----------|---------------|-------------|
|
|
474
|
+
* | `STRICT` | Sensitive operations | Any | 5 |
|
|
475
|
+
* | `AUTH` | Authentication endpoints | Any | 10 |
|
|
476
|
+
* | `PUBLIC` | Public/unauthenticated | App Only | 50 |
|
|
477
|
+
* | `API` | Standard API endpoints | WAF/Gateway + App | 100-1000 |
|
|
478
|
+
* | `DEVELOPMENT` | Development/testing | Development | 10,000 |
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* Choosing the right preset:
|
|
482
|
+
* ```typescript
|
|
483
|
+
* // High-security endpoint (password reset)
|
|
484
|
+
* .use(rateLimiting(RateLimitPresets.STRICT))
|
|
485
|
+
*
|
|
486
|
+
* // Login/registration
|
|
487
|
+
* .use(rateLimiting(RateLimitPresets.AUTH))
|
|
488
|
+
*
|
|
489
|
+
* // Public API with WAF protection
|
|
490
|
+
* .use(rateLimiting(RateLimitPresets.API))
|
|
491
|
+
*
|
|
492
|
+
* // Public API without WAF (direct exposure)
|
|
493
|
+
* .use(rateLimiting(RateLimitPresets.PUBLIC))
|
|
494
|
+
* ```
|
|
210
495
|
*/
|
|
211
496
|
export declare const RateLimitPresets: {
|
|
212
497
|
/**
|
|
213
498
|
* Very strict limits for sensitive endpoints
|
|
499
|
+
* Use for: Password resets, account changes, payment operations
|
|
500
|
+
* Infrastructure: Any (universal protection)
|
|
214
501
|
*/
|
|
215
502
|
readonly STRICT: {
|
|
216
503
|
maxRequests: number;
|
|
@@ -218,7 +505,9 @@ export declare const RateLimitPresets: {
|
|
|
218
505
|
message: string;
|
|
219
506
|
};
|
|
220
507
|
/**
|
|
221
|
-
* Standard API limits
|
|
508
|
+
* Standard API limits with dynamic scaling for authenticated users
|
|
509
|
+
* Use for: Main API endpoints, data retrieval, business operations
|
|
510
|
+
* Infrastructure: Best with WAF or Gateway (higher baseline limits)
|
|
222
511
|
*/
|
|
223
512
|
readonly API: {
|
|
224
513
|
maxRequests: number;
|
|
@@ -233,29 +522,228 @@ export declare const RateLimitPresets: {
|
|
|
233
522
|
};
|
|
234
523
|
/**
|
|
235
524
|
* Authentication endpoint limits
|
|
525
|
+
* Use for: Login, registration, token refresh, password operations
|
|
526
|
+
* Infrastructure: Any (essential security protection)
|
|
236
527
|
*/
|
|
237
528
|
readonly AUTH: {
|
|
238
529
|
maxRequests: number;
|
|
239
530
|
windowMs: number;
|
|
240
531
|
message: string;
|
|
532
|
+
keyGenerator: (context: Context) => string;
|
|
241
533
|
};
|
|
242
534
|
/**
|
|
243
|
-
* Public endpoint limits
|
|
535
|
+
* Public endpoint limits for direct application exposure
|
|
536
|
+
* Use for: Public APIs, webhooks, health checks
|
|
537
|
+
* Infrastructure: Application only (no WAF/Gateway protection)
|
|
244
538
|
*/
|
|
245
539
|
readonly PUBLIC: {
|
|
246
540
|
maxRequests: number;
|
|
247
541
|
windowMs: number;
|
|
542
|
+
dynamicLimits: {
|
|
543
|
+
suspicious: {
|
|
544
|
+
maxRequests: number;
|
|
545
|
+
windowMs: number;
|
|
546
|
+
matcher: (context: Context) => boolean;
|
|
547
|
+
};
|
|
548
|
+
};
|
|
248
549
|
};
|
|
249
550
|
/**
|
|
250
|
-
* Development mode - very permissive
|
|
551
|
+
* Development mode - very permissive limits
|
|
552
|
+
* Use for: Development, testing, debugging
|
|
553
|
+
* Infrastructure: Development environment only
|
|
251
554
|
*/
|
|
252
555
|
readonly DEVELOPMENT: {
|
|
253
556
|
maxRequests: number;
|
|
254
557
|
windowMs: number;
|
|
558
|
+
skip: (context: Context) => boolean;
|
|
559
|
+
};
|
|
560
|
+
/**
|
|
561
|
+
* Enterprise-grade configuration with multi-tier support
|
|
562
|
+
* Use for: Production SaaS applications, enterprise APIs
|
|
563
|
+
* Infrastructure: WAF + Gateway + Application (full stack protection)
|
|
564
|
+
*/
|
|
565
|
+
readonly ENTERPRISE: {
|
|
566
|
+
maxRequests: number;
|
|
567
|
+
windowMs: number;
|
|
568
|
+
dynamicLimits: {
|
|
569
|
+
free: {
|
|
570
|
+
maxRequests: number;
|
|
571
|
+
windowMs: number;
|
|
572
|
+
matcher: (context: Context) => boolean;
|
|
573
|
+
};
|
|
574
|
+
premium: {
|
|
575
|
+
maxRequests: number;
|
|
576
|
+
windowMs: number;
|
|
577
|
+
matcher: (context: Context) => boolean;
|
|
578
|
+
};
|
|
579
|
+
enterprise: {
|
|
580
|
+
maxRequests: number;
|
|
581
|
+
windowMs: number;
|
|
582
|
+
matcher: (context: Context) => boolean;
|
|
583
|
+
};
|
|
584
|
+
admin: {
|
|
585
|
+
maxRequests: number;
|
|
586
|
+
windowMs: number;
|
|
587
|
+
matcher: (context: Context) => boolean;
|
|
588
|
+
};
|
|
589
|
+
};
|
|
590
|
+
keyGenerator: (context: Context) => string;
|
|
255
591
|
};
|
|
256
592
|
};
|
|
257
593
|
/**
|
|
258
594
|
* Export memory store for testing and custom implementations
|
|
259
595
|
*/
|
|
260
596
|
export { MemoryStore };
|
|
597
|
+
/**
|
|
598
|
+
* Configuration helpers and utilities for rate limiting setup
|
|
599
|
+
*
|
|
600
|
+
* ## Best Practices for Production
|
|
601
|
+
*
|
|
602
|
+
* ### 1. Store Selection
|
|
603
|
+
* - **Development**: Use default `MemoryStore` (built-in)
|
|
604
|
+
* - **Production Single Instance**: Use `MemoryStore` with cleanup
|
|
605
|
+
* - **Production Multi-Instance**: Use Redis-based store
|
|
606
|
+
* - **Serverless**: Use external store (Redis/DynamoDB) for state persistence
|
|
607
|
+
*
|
|
608
|
+
* ### 2. Key Generation Strategy
|
|
609
|
+
* ```typescript
|
|
610
|
+
* // Bad: Too generic, easy to abuse
|
|
611
|
+
* keyGenerator: () => 'global'
|
|
612
|
+
*
|
|
613
|
+
* // Good: Multi-dimensional keys
|
|
614
|
+
* keyGenerator: (context) => {
|
|
615
|
+
* const user = context.user?.id;
|
|
616
|
+
* const endpoint = context.req.path?.split('/')[2]; // /api/users -> users
|
|
617
|
+
* const method = context.req.method;
|
|
618
|
+
* return user ? `${user}:${endpoint}:${method}` : `${context.req.ip}:${endpoint}`;
|
|
619
|
+
* }
|
|
620
|
+
* ```
|
|
621
|
+
*
|
|
622
|
+
* ### 3. Dynamic Limits Best Practices
|
|
623
|
+
* ```typescript
|
|
624
|
+
* // Order matchers from most specific to least specific
|
|
625
|
+
* dynamicLimits: {
|
|
626
|
+
* admin: { maxRequests: 10000, matcher: (ctx) => ctx.user?.role === 'admin' },
|
|
627
|
+
* enterprise: { maxRequests: 5000, matcher: (ctx) => ctx.user?.plan === 'enterprise' },
|
|
628
|
+
* premium: { maxRequests: 1000, matcher: (ctx) => ctx.user?.plan === 'premium' },
|
|
629
|
+
* authenticated: { maxRequests: 500, matcher: (ctx) => !!ctx.user },
|
|
630
|
+
* // Default fallback handled by maxRequests
|
|
631
|
+
* }
|
|
632
|
+
* ```
|
|
633
|
+
*
|
|
634
|
+
* ### 4. Error Handling and Fallback
|
|
635
|
+
* ```typescript
|
|
636
|
+
* const resilientRateLimit = rateLimiting({
|
|
637
|
+
* maxRequests: 100,
|
|
638
|
+
* windowMs: 60000,
|
|
639
|
+
*
|
|
640
|
+
* // Custom store with fallback
|
|
641
|
+
* store: new ResilientStore({
|
|
642
|
+
* primary: redisStore,
|
|
643
|
+
* fallback: new MemoryStore(),
|
|
644
|
+
* timeout: 500 // ms
|
|
645
|
+
* }),
|
|
646
|
+
*
|
|
647
|
+
* // Graceful degradation on errors
|
|
648
|
+
* onError: (error, context) => {
|
|
649
|
+
* logger.warn('Rate limiting error, allowing request', { error, ip: context.req.ip });
|
|
650
|
+
* return false; // Don't block request on store errors
|
|
651
|
+
* }
|
|
652
|
+
* });
|
|
653
|
+
* ```
|
|
654
|
+
*
|
|
655
|
+
* ### 5. Monitoring and Alerting
|
|
656
|
+
* ```typescript
|
|
657
|
+
* // Monitor rate limit effectiveness
|
|
658
|
+
* const monitoredRateLimit = rateLimiting({
|
|
659
|
+
* maxRequests: 100,
|
|
660
|
+
* windowMs: 60000,
|
|
661
|
+
*
|
|
662
|
+
* onRateLimit: (context, info) => {
|
|
663
|
+
* // Alert on high rate limit hits
|
|
664
|
+
* metrics.increment('rate_limit.exceeded', {
|
|
665
|
+
* endpoint: context.req.path,
|
|
666
|
+
* user: context.user?.id || 'anonymous'
|
|
667
|
+
* });
|
|
668
|
+
*
|
|
669
|
+
* // Log suspicious patterns
|
|
670
|
+
* if (info.current > info.limit * 2) {
|
|
671
|
+
* logger.warn('Potential abuse detected', {
|
|
672
|
+
* ip: context.req.ip,
|
|
673
|
+
* userAgent: context.req.headers?.['user-agent'],
|
|
674
|
+
* attempts: info.current
|
|
675
|
+
* });
|
|
676
|
+
* }
|
|
677
|
+
* }
|
|
678
|
+
* });
|
|
679
|
+
* ```
|
|
680
|
+
*
|
|
681
|
+
* ### 6. Testing Rate Limits
|
|
682
|
+
* ```typescript
|
|
683
|
+
* // Test helper for rate limit validation
|
|
684
|
+
* export const testRateLimit = async (
|
|
685
|
+
* handler: Handler,
|
|
686
|
+
* requests: number,
|
|
687
|
+
* shouldSucceed: number
|
|
688
|
+
* ) => {
|
|
689
|
+
* const results = await Promise.all(
|
|
690
|
+
* Array(requests).fill(0).map(() => handler.execute(mockRequest, mockResponse))
|
|
691
|
+
* );
|
|
692
|
+
*
|
|
693
|
+
* const successful = results.filter(r => r.statusCode !== 429).length;
|
|
694
|
+
* expect(successful).toBe(shouldSucceed);
|
|
695
|
+
* };
|
|
696
|
+
* ```
|
|
697
|
+
*
|
|
698
|
+
* ## Troubleshooting Common Issues
|
|
699
|
+
*
|
|
700
|
+
* ### Issue: Rate limits not working
|
|
701
|
+
* **Solution**: Check key generation and store connection
|
|
702
|
+
* ```typescript
|
|
703
|
+
* // Debug key generation
|
|
704
|
+
* keyGenerator: (context) => {
|
|
705
|
+
* const key = generateKey(context);
|
|
706
|
+
* console.log('Rate limit key:', key); // Remove in production
|
|
707
|
+
* return key;
|
|
708
|
+
* }
|
|
709
|
+
* ```
|
|
710
|
+
*
|
|
711
|
+
* ### Issue: Too many false positives
|
|
712
|
+
* **Solution**: Refine dynamic limits and key generation
|
|
713
|
+
* ```typescript
|
|
714
|
+
* // More granular limits
|
|
715
|
+
* dynamicLimits: {
|
|
716
|
+
* read: { maxRequests: 1000, matcher: (ctx) => ctx.req.method === 'GET' },
|
|
717
|
+
* write: { maxRequests: 100, matcher: (ctx) => ctx.req.method !== 'GET' }
|
|
718
|
+
* }
|
|
719
|
+
* ```
|
|
720
|
+
*
|
|
721
|
+
* ### Issue: Memory leaks in MemoryStore
|
|
722
|
+
* **Solution**: Ensure proper cleanup interval and limits
|
|
723
|
+
* ```typescript
|
|
724
|
+
* // Monitor store size
|
|
725
|
+
* setInterval(() => {
|
|
726
|
+
* const storeSize = memoryStore.size();
|
|
727
|
+
* if (storeSize > 10000) {
|
|
728
|
+
* logger.warn('Rate limit store size growing', { size: storeSize });
|
|
729
|
+
* }
|
|
730
|
+
* }, 60000);
|
|
731
|
+
* ```
|
|
732
|
+
*
|
|
733
|
+
* ### Issue: Rate limits too restrictive
|
|
734
|
+
* **Solution**: Implement gradual enforcement
|
|
735
|
+
* ```typescript
|
|
736
|
+
* const gradualLimit = rateLimiting({
|
|
737
|
+
* maxRequests: 100,
|
|
738
|
+
* windowMs: 60000,
|
|
739
|
+
*
|
|
740
|
+
* // Warn before blocking
|
|
741
|
+
* onApproachingLimit: (context, info) => {
|
|
742
|
+
* if (info.remaining < 10) {
|
|
743
|
+
* context.res.header('X-Rate-Limit-Warning', 'Approaching limit');
|
|
744
|
+
* }
|
|
745
|
+
* }
|
|
746
|
+
* });
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
261
749
|
//# sourceMappingURL=rateLimitingMiddleware.d.ts.map
|