UNPKG

@canmertinyo/rate-limiter-core

Version:

A simple rate-limiting middleware for Express.js with support for in-memory, Redis, and MongoDB storage

100 lines (99 loc) 4.25 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.rateLimiter = rateLimiter; const in_memory_storage_1 = require("./storages/in-memory.storage"); const utils_1 = require("./utils"); const inMemoryStorage = new in_memory_storage_1.InMemoryStorage(); let storageInstance; function rateLimiter(options) { var _a; var storage = (_a = options.storage) !== null && _a !== void 0 ? _a : inMemoryStorage; return (req, res, next) => __awaiter(this, void 0, void 0, function* () { var _a, _b; if (!storageInstance) { try { storageInstance = yield storage.initialize(); } catch (error) { return handleError(req, res, error, options); } } try { const rateLimitPropName = (_a = options.requestPropertyName) !== null && _a !== void 0 ? _a : "rateLimit"; let key; if (options.keyGenerator && typeof options.keyGenerator === "function") { key = options.keyGenerator(req, res); } else { key = (0, utils_1.getIp)(req); } var record = yield storageInstance.getRateLimitRecord(key); if (options.skip && typeof options.skip === "function") { const skip = yield options.skip(req, res); if (skip) { req[rateLimitPropName] = record; return next(); } } let maxRequest; if (typeof options.maxRequest === "function") { maxRequest = yield options.maxRequest(req, res); } else { maxRequest = options.maxRequest; } let message; if (typeof options.message === "function") { message = options.message(req, res); } else { message = options.message; } const currentTime = (0, utils_1.getTimestamp)(); const startTime = currentTime - options.ms; if (!record) { record = yield storageInstance.createRateLimitRecord({ key, count: 0, timestamp: currentTime, }); req[rateLimitPropName] = record; } if (startTime > record.timestamp) { record = yield storageInstance.updateRateLimitRecord(key, currentTime, 1); req[rateLimitPropName] = record; next(); } else if (record.count < maxRequest) { record = yield storageInstance.increment(record.key); req[rateLimitPropName] = record; next(); } else { if (options.handler && typeof options.handler === "function") { return yield options.handler(req, res, next, options); } res.status((_b = options.statusCode) !== null && _b !== void 0 ? _b : 429).send(message); } } catch (error) { handleError(req, res, error, options); } }); } function handleError(req, res, error, options) { const message = error instanceof Error ? error.message : JSON.stringify(error); if (options.errorHandler && typeof options.errorHandler === "function") { return options.errorHandler(req, res); } return res.status(500).json({ statusCode: 500, message }); }