wulf-pulse/lib/services/rate-limiter.ts
root 6eee14f8af Add comprehensive admin features and multi-system integration
- Add admin dashboard with sync controls and data browser
- Implement RMM, Auvik, and Addigy organization mappings
- Add chunked ticket sync with progress tracking
- Implement entity sync service with rate limiting
- Add analytics engine and performance optimizer
- Create data browser for all PSA entities
- Add navigation components and UI improvements
- Implement background processing and sync services
- Add comprehensive documentation and migration scripts
- Update configuration items with multi-system support
- Enhance contact management and purchase history
- Add issue type assignment and LLM analyzer
- Improve error handling and logging utilities
2025-11-19 14:18:16 -05:00

116 lines
2.8 KiB
TypeScript

/**
* 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<T>(fn: () => Promise<T>): Promise<T> {
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<void> {
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<void> {
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<void> {
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;