UNPKG

nodejs-cloud-taskmq

Version:

Node.js TypeScript library for integrating Google Cloud Tasks with MongoDB/Redis/Memory/Custom for a BullMQ-like queue system. Compatible with NestJS but framework-agnostic.

129 lines (128 loc) 4.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RateLimiterService = void 0; /** * Rate limiter service */ class RateLimiterService { constructor(storageAdapter) { this.storageAdapter = storageAdapter; } /** * Check and increment rate limit */ async checkRateLimit(key, options) { const { maxRequests, windowMs } = options; // Handle zero max requests case if (maxRequests <= 0) { return { allowed: false, count: 0, limit: maxRequests, resetTime: windowMs, remaining: 0, }; } // Use atomic increment operation const result = await this.storageAdapter.incrementRateLimit(key, windowMs, maxRequests); const now = Date.now(); return { allowed: result.allowed, count: result.count, limit: maxRequests, resetTime: result.resetTime.getTime() - now, remaining: Math.max(0, maxRequests - result.count), }; } /** * Get current rate limit status without incrementing */ async getRateLimitStatus(key, options) { const { maxRequests } = options; const currentLimit = await this.storageAdapter.getRateLimit(key); const now = Date.now(); if (!currentLimit || currentLimit.resetTime.getTime() <= now) { return null; // No active rate limit } return { allowed: currentLimit.count < maxRequests, count: currentLimit.count, limit: maxRequests, resetTime: currentLimit.resetTime.getTime() - now, remaining: Math.max(0, maxRequests - currentLimit.count), }; } /** * Reset rate limit for a key */ async resetRateLimit(key) { if (this.storageAdapter.deleteRateLimit) { await this.storageAdapter.deleteRateLimit(key); } else { // Fallback for adapters that don't support deleteRateLimit console.warn('Rate limit reset not directly supported - limit will expire naturally'); } } /** * Check if a request would be allowed without incrementing the counter */ async wouldAllow(key, options) { const status = await this.getRateLimitStatus(key, options); return status ? status.allowed : true; } /** * Get remaining requests for a key */ async getRemaining(key, options) { const status = await this.getRateLimitStatus(key, options); return status ? status.remaining : options.maxRequests; } /** * Get time until reset for a key */ async getTimeUntilReset(key) { const currentLimit = await this.storageAdapter.getRateLimit(key); if (!currentLimit) { return 0; } const now = Date.now(); const resetTime = currentLimit.resetTime.getTime(); return Math.max(0, resetTime - now); } /** * Create a rate limit key based on various parameters */ static createKey(prefix, ...parts) { return [prefix, ...parts].join(':'); } /** * Create a rate limit key for IP address */ static createIpKey(ip, endpoint) { return endpoint ? RateLimiterService.createKey('ip', ip, endpoint) : RateLimiterService.createKey('ip', ip); } /** * Create a rate limit key for user */ static createUserKey(userId, endpoint) { return endpoint ? RateLimiterService.createKey('user', userId, endpoint) : RateLimiterService.createKey('user', userId); } /** * Create a rate limit key for queue */ static createQueueKey(queueName) { return RateLimiterService.createKey('queue', queueName); } /** * Create a rate limit key for processor */ static createProcessorKey(queueName, processorName) { return RateLimiterService.createKey('processor', queueName, processorName); } } exports.RateLimiterService = RateLimiterService;