Skip to main content
Glama
fmangot
by fmangot
rate-limit.ts3.31 kB
/** * Simple Rate Limiting Middleware * In-memory rate limiter for protecting against DoS */ import http from 'http'; interface RateLimitEntry { count: number; resetTime: number; } export class RateLimiter { private requests: Map<string, RateLimitEntry>; private cleanupInterval: NodeJS.Timeout | null; constructor( private readonly maxRequests: number = 100, private readonly windowMs: number = 15 * 60 * 1000 // 15 minutes ) { this.requests = new Map(); this.cleanupInterval = null; this.startCleanup(); } private startCleanup(): void { // Clean up expired entries every 5 minutes this.cleanupInterval = setInterval(() => { const now = Date.now(); for (const [key, entry] of this.requests.entries()) { if (now > entry.resetTime) { this.requests.delete(key); } } }, 5 * 60 * 1000); if (this.cleanupInterval.unref) { this.cleanupInterval.unref(); } } /** * Check if request should be rate limited * Returns true if request should be blocked */ public check(identifier: string): boolean { const now = Date.now(); let entry = this.requests.get(identifier); if (!entry || now > entry.resetTime) { // New window entry = { count: 1, resetTime: now + this.windowMs, }; this.requests.set(identifier, entry); return false; } entry.count++; if (entry.count > this.maxRequests) { return true; // Rate limited } return false; } /** * Get remaining requests for an identifier */ public getRemaining(identifier: string): number { const entry = this.requests.get(identifier); if (!entry || Date.now() > entry.resetTime) { return this.maxRequests; } return Math.max(0, this.maxRequests - entry.count); } /** * Get reset time for an identifier */ public getResetTime(identifier: string): number | null { const entry = this.requests.get(identifier); if (!entry || Date.now() > entry.resetTime) { return null; } return entry.resetTime; } public destroy(): void { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } this.requests.clear(); } } /** * Get client identifier from request */ export function getClientIdentifier(req: http.IncomingMessage): string { // Try to get real IP from headers (for proxies) const forwarded = req.headers['x-forwarded-for']; if (forwarded) { const ips = typeof forwarded === 'string' ? forwarded.split(',') : forwarded; return ips[0].trim(); } return req.socket.remoteAddress || 'unknown'; } /** * Send rate limit error response */ export function sendRateLimitError( res: http.ServerResponse, resetTime: number | null ): void { const retryAfter = resetTime ? Math.ceil((resetTime - Date.now()) / 1000) : 900; res.setHeader('Retry-After', retryAfter.toString()); res.setHeader('X-RateLimit-Limit', '100'); res.setHeader('X-RateLimit-Remaining', '0'); res.writeHead(429, { 'Content-Type': 'application/json' }); res.end( JSON.stringify({ error: 'Too many requests', message: 'You have exceeded the rate limit. Please try again later.', retryAfter, }) ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/fmangot/Mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server