UNPKG

express-brute-guard

Version:

A customizable and production-ready rate-limiting middleware for Node.js.

123 lines (122 loc) 5.88 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BruteGuard = void 0; const memoryStore_1 = __importDefault(require("./stores/memoryStore")); class BruteGuard { // requestCount: number; // firstRequestTime: number; constructor(options = {}) { this.maxRequests = options.maxRequests || 10; // this.requestCount = options.requestCount || 0; // this.firstRequestTime = options.firstRequestTime || 0; this.windowMs = options.windowMs || 5 * 60 * 1000; this.blockDuration = options.blockDuration || 3 * 60 * 1000; this.resetTime = options.resetTime || Date.now() + this.windowMs; this.statusCode = options.statusCode || 429; this.errormessage = options.errormessage || 'Too many Requests'; this.store = options.store || new memoryStore_1.default(); this.headers = options.headers !== false; } isBlocked(res, exist) { if (exist.blockExpiresAt && Date.now() < exist.blockExpiresAt) { this.blockDuration = exist.blockExpiresAt; if (this.headers) { //Enable console.logs for detailed flow while testing // console.log('isBlocked X-RateLimit-Reset', this.resetTime); // console.log('isBlocked Retry-After', this.blockDuration); res.setHeader('Retry-After', this.blockDuration); } res.status(this.statusCode).json({ message: `${this.errormessage}` }); return true; } return false; } incrementRequest(res, exist, ipAddress) { if ((exist === null || exist === void 0 ? void 0 : exist.requestCount) >= this.maxRequests) { if (this.headers) { //Enable console.logs for detailed flow while testing // console.log('incrementRequest X-RateLimit-Reset', this.resetTime); // console.log('incrementRequest Retry-After', this.blockDuration); res.setHeader('X-RateLimit-Reset', this.resetTime); res.setHeader('Retry-After', this.blockDuration); } this.store.updateIp(ipAddress, exist.requestCount, exist.firstRequestTime, exist.windowMs, Date.now() + this.blockDuration); res.status(this.statusCode).json({ message: `${this.errormessage}` }); return true; } exist.requestCount++; //Enable console.logs for detailed flow while testing // console.log('[Increment] requestCount:', exist.requestCount); this.store.updateIp(ipAddress, exist.requestCount, exist.firstRequestTime, exist.windowMs, null); return false; } // If the IP is already present, increment the request count checkIpExist(res, exist, ipAddress) { if (exist && this.isBlocked(res, exist)) return true; if (exist && this.incrementRequest(res, exist, ipAddress)) return true; return false; } createGuard(req, res, next) { const ipAddress = req.ip; const exist = this.store.getIp(ipAddress); // If the IP is already present, increment the request count if (this.checkIpExist(res, exist, ipAddress)) { //Enable console.logs for detailed flow while testing //console.log('[Guard] Blocked IP – no next()'); return; } // If the IP doesn't exist, create a new entry if (!exist) { //Enable console.logs for detailed flow while testing //console.log('[Guard] New IP – calling next()'); this.store.setIp(ipAddress, this.maxRequests, 1, Date.now(), Date.now() + this.windowMs); if (this.headers) { //Enable console.logs for detailed flow while testing // console.log('Setting headers:', this.maxRequests, this.maxRequests - 1); // console.log('X-RateLimit-Limit', this.maxRequests); // console.log('X-RateLimit-Remaining', this.maxRequests - 1); res.setHeader('X-RateLimit-Limit', this.maxRequests); res.setHeader('X-RateLimit-Remaining', this.maxRequests - 1); } //console.log('New IP [Increment] requestCount:', 1); return next(); } // Reset if window expired if (Date.now() > exist.windowMs) { //Enable console.logs for detailed flow while testing //console.log('[Guard] Window expired –reset - calling next()'); this.store.resetIp(ipAddress, 0, Date.now(), this.windowMs, null); return next(); } // If passed all checks if (this.headers) { //Enable console.logs for detailed flow while testing // console.log( // 'Setting headers - final:', // this.maxRequests, // this.maxRequests - (exist?.requestCount || 0) // ); // console.log( // 'If passed all checks' + 'X-RateLimit-Limit', // this.maxRequests // ); // console.log( // 'If passed all checks' + 'X-RateLimit-Remaining', // this.maxRequests - (exist?.requestCount || 0) // ); res.setHeader('X-RateLimit-Limit', this.maxRequests); res.setHeader('X-RateLimit-Remaining', this.maxRequests - ((exist === null || exist === void 0 ? void 0 : exist.requestCount) || 0)); } //Enable console.logs for detailed flow while testing //console.log('[Guard] Final fallback next()'); next(); } } exports.BruteGuard = BruteGuard; const bruteGuard = new BruteGuard(); exports.default = bruteGuard;