UNPKG

@developers-joyride/rate-limiter

Version:

A flexible rate limiting library with TypeScript support, Express middleware, and NestJS guard/interceptor capabilities

129 lines (128 loc) 4.64 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MongoDBCacheProvider = void 0; const mongoose_1 = __importDefault(require("mongoose")); const rate_limit_model_1 = require("../models/rate-limit.model"); class MongoDBCacheProvider { constructor(config) { this.isConnected = false; if (config.type !== "mongodb") { throw new Error('MongoDBCacheProvider requires type to be "mongodb"'); } if (!config.mongoUrl) { throw new Error("MongoDB connection URL is required"); } this.config = { type: "mongodb", mongoUrl: config.mongoUrl, collectionName: config.collectionName || "rateLimitingLogs", redisUrl: "", redisKeyPrefix: "rate_limit:", }; } async initialize() { if (this.isConnected) { return; } try { await mongoose_1.default.connect(this.config.mongoUrl); this.isConnected = true; // Set up TTL index for automatic cleanup const collection = mongoose_1.default.connection.collection(this.config.collectionName); await collection.createIndex({ createdAt: 1 }, { expireAfterSeconds: 86400 } // 24 hours default TTL ); console.log("MongoDB cache provider initialized successfully"); } catch (error) { console.error("Failed to initialize MongoDB cache provider:", error); throw error; } } async checkLimit(key, maxRequests, windowMs) { await this.ensureConnection(); const now = new Date(); const resetTime = new Date(now.getTime() + windowMs * 1000); try { // Find existing rate limit record let rateLimitLog = await rate_limit_model_1.RateLimitLog.findOne({ key }); if (!rateLimitLog) { // Create new rate limit record rateLimitLog = new rate_limit_model_1.RateLimitLog({ key, count: 1, resetTime, createdAt: now, }); await rateLimitLog.save(); } else { // Check if the window has expired if (rateLimitLog.resetTime <= now) { // Reset the counter for new window rateLimitLog.count = 1; rateLimitLog.resetTime = resetTime; rateLimitLog.createdAt = now; } else { // Increment the counter rateLimitLog.count += 1; } await rateLimitLog.save(); } const allowed = rateLimitLog.count <= maxRequests; const remaining = Math.max(0, maxRequests - rateLimitLog.count); return { allowed, current: rateLimitLog.count, limit: maxRequests, windowMs, resetTime: rateLimitLog.resetTime.toISOString(), remaining, }; } catch (error) { console.error("Error checking rate limit in MongoDB:", error); // In case of error, allow the request to prevent blocking return { allowed: true, current: 0, limit: maxRequests, windowMs, resetTime: resetTime.toISOString(), remaining: maxRequests, }; } } async resetLimit(key) { await this.ensureConnection(); await rate_limit_model_1.RateLimitLog.deleteOne({ key }); } async getLimitInfo(key) { await this.ensureConnection(); const rateLimitLog = await rate_limit_model_1.RateLimitLog.findOne({ key }); if (!rateLimitLog) { return null; } return { key: rateLimitLog.key, count: rateLimitLog.count, resetTime: rateLimitLog.resetTime, createdAt: rateLimitLog.createdAt, }; } async ensureConnection() { if (!this.isConnected) { await this.initialize(); } } async close() { if (this.isConnected) { await mongoose_1.default.connection.close(); this.isConnected = false; } } } exports.MongoDBCacheProvider = MongoDBCacheProvider;