UNPKG

marketing-post-generator-mcp

Version:

A powerful MCP server for AI-powered marketing blog post generation with Claude integration

141 lines 6.13 kB
import { rateLimitConfigs, } from '../../services/rateLimit/RateLimitService'; import { RateLimitError } from '../errors/BaseError'; import { getRequestId } from './RequestIdMiddleware'; export class RateLimitMiddleware { rateLimitService; config; constructor(rateLimitService, config) { this.rateLimitService = rateLimitService; this.config = { identifierExtractor: (req) => this.getClientIdentifier(req), standardHeaders: true, legacyHeaders: false, message: 'Too many requests, please try again later.', ...config, }; } createMiddleware() { return async (req, res, next) => { try { const identifier = this.config.identifierExtractor(req); const result = this.rateLimitService.checkRateLimit(identifier, this.config); // Set rate limit headers this.setRateLimitHeaders(res, result.info); if (!result.allowed) { // Record the failed request this.rateLimitService.recordRequest(identifier, this.config, false); // Call custom handler if provided if (this.config.onLimitReached) { this.config.onLimitReached(req, res, result.info); } // Create rate limit error const rateLimitError = new RateLimitError(this.config.message || 'Too many requests, please try again later.', { identifier, requestId: getRequestId(req), rateLimitInfo: result.info, }); // Set retry-after header if (result.info.retryAfter) { res.set('Retry-After', result.info.retryAfter.toString()); } return res.status(429).json({ error: rateLimitError.toJSON(), retryAfter: result.info.retryAfter, }); } // Record successful request check this.rateLimitService.recordRequest(identifier, this.config, true); next(); } catch (error) { next(error); } }; } getClientIdentifier(req) { // Try to get IP address from various headers const ip = req.get('X-Forwarded-For')?.split(',')[0] || req.get('X-Real-IP') || req.connection.remoteAddress || req.socket.remoteAddress || 'unknown'; // You might want to include user ID if available const userId = req.user?.id; return userId ? `user:${userId}` : `ip:${ip}`; } setRateLimitHeaders(res, info) { if (this.config.standardHeaders) { // Standard rate limit headers (draft RFC) res.set('RateLimit-Limit', this.config.maxRequests.toString()); res.set('RateLimit-Remaining', info.remainingRequests.toString()); res.set('RateLimit-Reset', new Date(info.resetTime).toISOString()); } if (this.config.legacyHeaders) { // Legacy headers (commonly used) res.set('X-RateLimit-Limit', this.config.maxRequests.toString()); res.set('X-RateLimit-Remaining', info.remainingRequests.toString()); res.set('X-RateLimit-Reset', Math.ceil(info.resetTime / 1000).toString()); } } } // Helper functions to create common rate limit middleware export function createRateLimitMiddleware(rateLimitService, config) { const middleware = new RateLimitMiddleware(rateLimitService, config || rateLimitConfigs.standard); return middleware.createMiddleware(); } export function createStrictRateLimitMiddleware(rateLimitService) { return createRateLimitMiddleware(rateLimitService, { ...rateLimitConfigs.strict, message: 'Rate limit exceeded for sensitive operation.', }); } export function createGenerousRateLimitMiddleware(rateLimitService) { return createRateLimitMiddleware(rateLimitService, { ...rateLimitConfigs.generous, message: 'Rate limit exceeded for bulk operation.', }); } // Path-specific rate limiting export function createPathSpecificRateLimit(rateLimitService, pathConfigs) { return (req, res, next) => { const path = req.path; // Find matching path configuration const matchingConfig = Object.entries(pathConfigs).find(([pattern, config]) => { if (pattern.includes('*')) { // Simple wildcard matching const regexPattern = pattern.replace(/\*/g, '.*'); return new RegExp(`^${regexPattern}$`).test(path); } return path.startsWith(pattern); }); if (matchingConfig) { const [, config] = matchingConfig; const middleware = new RateLimitMiddleware(rateLimitService, config); return middleware.createMiddleware()(req, res, next); } // No specific config found, use default const defaultMiddleware = createRateLimitMiddleware(rateLimitService); return defaultMiddleware(req, res, next); }; } // User-specific rate limiting export function createUserRateLimit(rateLimitService, config = rateLimitConfigs.standard) { return createRateLimitMiddleware(rateLimitService, { ...config, identifierExtractor: (req) => { const userId = req.user?.id; return userId ? `user:${userId}` : `ip:${req.ip}`; }, }); } // API key-specific rate limiting export function createApiKeyRateLimit(rateLimitService, config = rateLimitConfigs.standard) { return createRateLimitMiddleware(rateLimitService, { ...config, identifierExtractor: (req) => { const apiKey = req.get('X-API-Key') || req.get('Authorization'); return apiKey ? `api:${apiKey}` : `ip:${req.ip}`; }, }); } //# sourceMappingURL=RateLimitMiddleware.js.map