UNPKG

marketing-post-generator-mcp

Version:

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

188 lines 5.86 kB
export class RateLimitService { logger; store; cleanupInterval; constructor(logger) { this.logger = logger; this.store = new Map(); // Clean up expired records every 5 minutes this.cleanupInterval = setInterval(() => { this.cleanup(); }, 5 * 60 * 1000); } checkRateLimit(identifier, config) { const key = config.keyGenerator ? config.keyGenerator(identifier) : identifier; const now = Date.now(); // Get or create record let record = this.store.get(key); if (!record || now >= record.resetTime) { // Create new record or reset expired one record = { count: 1, resetTime: now + config.windowMs, firstRequest: now, }; this.store.set(key, record); const info = { totalRequests: 1, remainingRequests: config.maxRequests - 1, resetTime: record.resetTime, }; return { allowed: true, info, }; } // Check if limit exceeded if (record.count >= config.maxRequests) { const info = { totalRequests: record.count, remainingRequests: 0, resetTime: record.resetTime, retryAfter: Math.ceil((record.resetTime - now) / 1000), }; this.logger.warn('Rate limit exceeded', { identifier, count: record.count, maxRequests: config.maxRequests, resetTime: record.resetTime, }); return { allowed: false, info, }; } // Increment count record.count++; const info = { totalRequests: record.count, remainingRequests: config.maxRequests - record.count, resetTime: record.resetTime, }; return { allowed: true, info, }; } recordRequest(identifier, config, success = true) { // Skip recording based on configuration if (success && config.skipSuccessfulRequests) { return; } if (!success && config.skipFailedRequests) { return; } const key = config.keyGenerator ? config.keyGenerator(identifier) : identifier; const now = Date.now(); // Reset on successful request if configured if (success && config.resetOnSuccessfulRequest) { this.store.delete(key); return; } // Update record const record = this.store.get(key); if (record && now < record.resetTime) { // Record is already updated in checkRateLimit // This method is mainly for post-request updates this.logger.debug('Rate limit request recorded', { identifier, count: record.count, success, }); } } getRateLimitInfo(identifier, config) { const key = config.keyGenerator ? config.keyGenerator(identifier) : identifier; const record = this.store.get(key); if (!record) { return null; } const now = Date.now(); if (now >= record.resetTime) { // Record expired return null; } const result = { totalRequests: record.count, remainingRequests: Math.max(0, config.maxRequests - record.count), resetTime: record.resetTime, }; if (record.count >= config.maxRequests) { result.retryAfter = Math.ceil((record.resetTime - now) / 1000); } return result; } resetRateLimit(identifier, config) { const key = config.keyGenerator ? config.keyGenerator(identifier) : identifier; this.store.delete(key); this.logger.info('Rate limit reset', { identifier, key, }); } getStats() { const now = Date.now(); let activeRecords = 0; for (const [, record] of this.store) { if (now < record.resetTime) { activeRecords++; } } return { totalKeys: this.store.size, activeRecords, memoryUsage: process.memoryUsage().heapUsed, }; } cleanup() { const now = Date.now(); let cleanedCount = 0; for (const [key, record] of this.store) { if (now >= record.resetTime) { this.store.delete(key); cleanedCount++; } } if (cleanedCount > 0) { this.logger.debug('Rate limit cleanup completed', { cleanedRecords: cleanedCount, remainingRecords: this.store.size, }); } } destroy() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); } this.store.clear(); } } // Predefined rate limit configurations export const rateLimitConfigs = { // Very strict - for sensitive operations strict: { windowMs: 60 * 1000, // 1 minute maxRequests: 5, }, // Standard - for general API usage standard: { windowMs: 60 * 1000, // 1 minute maxRequests: 60, }, // Generous - for bulk operations generous: { windowMs: 60 * 1000, // 1 minute maxRequests: 300, }, // Hourly limit hourly: { windowMs: 60 * 60 * 1000, // 1 hour maxRequests: 1000, }, // Daily limit daily: { windowMs: 24 * 60 * 60 * 1000, // 24 hours maxRequests: 10000, }, }; //# sourceMappingURL=RateLimitService.js.map