/** * Rate Limiter Service * Implements token bucket algorithm for API rate limiting * Limits requests to 10 per second for Autotask API */ export class RateLimiter { private maxRequestsPerSecond: number; private requestQueue: Array<() => void> = []; private requestTimes: number[] = []; private processing = false; constructor(maxRequestsPerSecond: number = 10) { this.maxRequestsPerSecond = maxRequestsPerSecond; } /** * Throttle a function call to respect rate limits * @param fn Function to execute with rate limiting * @returns Promise that resolves when function completes */ async throttle(fn: () => Promise): Promise { return new Promise((resolve, reject) => { this.requestQueue.push(async () => { try { const result = await fn(); resolve(result); } catch (error) { reject(error); } }); this.processQueue(); }); } /** * Process the request queue with rate limiting */ private async processQueue(): Promise { if (this.processing || this.requestQueue.length === 0) { return; } this.processing = true; while (this.requestQueue.length > 0) { await this.waitIfNeeded(); const request = this.requestQueue.shift(); if (request) { this.requestTimes.push(Date.now()); await request(); } } this.processing = false; } /** * Wait if we've hit the rate limit */ private async waitIfNeeded(): Promise { const now = Date.now(); const oneSecondAgo = now - 1000; // Remove request times older than 1 second this.requestTimes = this.requestTimes.filter(time => time > oneSecondAgo); // If we've hit the limit, wait until we can make another request if (this.requestTimes.length >= this.maxRequestsPerSecond) { const oldestRequest = this.requestTimes[0]; const waitTime = 1000 - (now - oldestRequest) + 10; // Add 10ms buffer if (waitTime > 0) { await this.sleep(waitTime); } } } /** * Sleep for specified milliseconds */ private sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Get current queue length */ getQueueLength(): number { return this.requestQueue.length; } /** * Get number of requests in the last second */ getCurrentRequestCount(): number { const oneSecondAgo = Date.now() - 1000; return this.requestTimes.filter(time => time > oneSecondAgo).length; } /** * Clear the queue and reset */ reset(): void { this.requestQueue = []; this.requestTimes = []; this.processing = false; } } // Export singleton instance for Autotask API (10 req/sec) export const autotaskRateLimiter = new RateLimiter(10); export default autotaskRateLimiter;