UNPKG

qapinterface

Version:

Comprehensive API utilities for Node.js applications including authentication, security, request processing, and response handling with zero external dependencies

70 lines (61 loc) 2.65 kB
/** * Rate Limiter Middleware Creator * Single Responsibility: Create rate limiting middleware ONLY */ const localVars = require('../../config/localVars'); const { logger } = require('../asyncLogger'); const { createRateLimiter } = require('./limiter-creator'); /** * Creates an Express middleware for rate limiting requests. * @param {object} [options] - Options passed to createRateLimiter. * @returns {function} An Express middleware function. */ function createRateLimiterMiddleware(options = {}) { return (req, res, next) => { try { // Determine rate limit settings based on endpoint and user tier let points = localVars.DEFAULT_RATE_LIMIT_POINTS; let duration = localVars.DEFAULT_RATE_LIMIT_DURATION; // Check for endpoint-specific override const endpoint = req.path; if (localVars.ENDPOINT_RATE_LIMITS[endpoint]) { points = localVars.ENDPOINT_RATE_LIMITS[endpoint].points; duration = localVars.ENDPOINT_RATE_LIMITS[endpoint].duration; } // Check for user-tier override const userTier = req.user?.tier || 'free'; if (localVars.RATE_LIMIT_TIERS[userTier]) { points = localVars.RATE_LIMIT_TIERS[userTier].points; duration = localVars.RATE_LIMIT_TIERS[userTier].duration; } // Create limiter instance with calculated settings const limiter = createRateLimiter({ ...options, points, duration }); // Use user ID if available, else IP address const key = req.user?.id || req.ip; limiter.consume(key) .then((rateLimiterRes) => { logger.debug(`Rate limit consumed for ${key}: ${rateLimiterRes.remainingPoints}/${points} remaining`); next(); }) .catch((rateLimiterRes) => { const retryAfter = Math.ceil(rateLimiterRes.msBeforeNext / 1000); logger.warn(`Rate limit exceeded for ${key}: ${points} req/${duration}s - Retry after ${retryAfter}s`); res.setHeader('Retry-After', String(retryAfter)); res.writeHead(429, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Too Many Requests', message: `Rate limit exceeded. Try again in ${retryAfter} seconds.`, limit: points, remaining: rateLimiterRes.remainingPoints, reset: new Date(Date.now() + rateLimiterRes.msBeforeNext) })); }); } catch (err) { logger.error(`Rate limiter error: ${err.message}`, { stack: err.stack }); next(); // Fail open to avoid blocking traffic } }; } module.exports = { createRateLimiterMiddleware };