UNPKG

hikma-engine

Version:

Code Knowledge Graph Indexer - A sophisticated TypeScript-based indexer that transforms Git repositories into multi-dimensional knowledge stores for AI agents

165 lines (164 loc) 6.5 kB
"use strict"; /** * @file Rate limiting middleware for API requests. * Implements configurable rate limiting per IP address with different limits for different endpoints. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RateLimiters = exports.developmentRateLimit = exports.healthCheckRateLimit = exports.heavySearchRateLimit = exports.searchRateLimit = exports.globalRateLimit = void 0; exports.getEnvironmentRateLimit = getEnvironmentRateLimit; exports.addRateLimitHeaders = addRateLimitHeaders; const express_rate_limit_1 = __importDefault(require("express-rate-limit")); const logger_1 = require("../../utils/logger"); const logger = (0, logger_1.getLogger)('RateLimitMiddleware'); /** * Custom rate limit exceeded response handler. */ function rateLimitExceededHandler(req, res) { const requestId = req.headers['x-request-id'] || 'unknown'; logger.warn('Rate limit exceeded', { requestId, ip: req.ip, method: req.method, url: req.url, userAgent: req.get('User-Agent'), }); res.status(429).json({ success: false, error: { code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests from this IP address. Please try again later.', retryAfter: Math.ceil(req.rateLimit?.resetTime ? (req.rateLimit.resetTime.getTime() - Date.now()) / 1000 : 60), }, meta: { timestamp: new Date().toISOString(), requestId, rateLimit: { limit: req.rateLimit?.limit, current: req.rateLimit?.limit ? (req.rateLimit.limit - (req.rateLimit.remaining || 0)) : 0, remaining: req.rateLimit?.remaining, resetTime: req.rateLimit?.resetTime?.toISOString(), }, }, }); } /** * Skip function for rate limiting - allows bypassing rate limits for certain conditions. */ function skipLimitFunction(req) { // Skip rate limiting for health check endpoints if (req.path.startsWith('/health')) { return true; } // Skip rate limiting for root endpoint if (req.path === '/') { return true; } return false; } /** * Creates a rate limiting middleware with specified configuration. */ function createRateLimiter(config) { return (0, express_rate_limit_1.default)({ windowMs: config.windowMs, max: config.max, message: config.message, standardHeaders: config.standardHeaders ?? true, legacyHeaders: config.legacyHeaders ?? false, skipSuccessfulRequests: config.skipSuccessfulRequests ?? false, skipFailedRequests: config.skipFailedRequests ?? false, // Use default keyGenerator for IPv6 compatibility skip: skipLimitFunction, handler: rateLimitExceededHandler, // Note: onLimitReached is not available in newer versions of express-rate-limit // The rate limit exceeded logging is handled in the handler function }); } /** * Global rate limiter - applies to all API endpoints. * Default: 100 requests per 15 minutes per IP. */ exports.globalRateLimit = createRateLimiter({ windowMs: 15 * 60 * 1000, // 15 minutes max: parseInt(process.env.GLOBAL_RATE_LIMIT || '100', 10), message: 'Too many requests from this IP address. Please try again later.', }); /** * Search endpoint rate limiter - more restrictive for search operations. * Default: 50 requests per 10 minutes per IP. */ exports.searchRateLimit = createRateLimiter({ windowMs: 10 * 60 * 1000, // 10 minutes max: parseInt(process.env.SEARCH_RATE_LIMIT || '50', 10), message: 'Too many search requests from this IP address. Please try again later.', skipSuccessfulRequests: false, skipFailedRequests: true, // Don't count failed requests (validation errors, etc.) }); /** * Heavy search operations rate limiter - very restrictive for resource-intensive operations. * Default: 20 requests per 10 minutes per IP. */ exports.heavySearchRateLimit = createRateLimiter({ windowMs: 10 * 60 * 1000, // 10 minutes max: parseInt(process.env.HEAVY_SEARCH_RATE_LIMIT || '20', 10), message: 'Too many heavy search requests from this IP address. Please try again later.', skipSuccessfulRequests: false, skipFailedRequests: true, }); /** * Health check rate limiter - very permissive for monitoring. * Default: 200 requests per 5 minutes per IP. */ exports.healthCheckRateLimit = createRateLimiter({ windowMs: 5 * 60 * 1000, // 5 minutes max: parseInt(process.env.HEALTH_RATE_LIMIT || '200', 10), message: 'Too many health check requests from this IP address.', }); /** * Development rate limiter - more permissive for development environments. * Default: 500 requests per 15 minutes per IP. */ exports.developmentRateLimit = createRateLimiter({ windowMs: 15 * 60 * 1000, // 15 minutes max: parseInt(process.env.DEV_RATE_LIMIT || '500', 10), message: 'Too many requests from this IP address (development mode).', }); /** * Returns appropriate rate limiter based on environment. */ function getEnvironmentRateLimit() { const env = process.env.NODE_ENV || 'development'; if (env === 'development' || env === 'test') { logger.info('Using development rate limits'); return exports.developmentRateLimit; } logger.info('Using production rate limits'); return exports.globalRateLimit; } /** * Rate limiting configuration for different endpoint types. */ exports.RateLimiters = { global: exports.globalRateLimit, search: exports.searchRateLimit, heavySearch: exports.heavySearchRateLimit, healthCheck: exports.healthCheckRateLimit, development: exports.developmentRateLimit, environment: getEnvironmentRateLimit(), }; /** * Middleware to add rate limit information to response headers. */ function addRateLimitHeaders(req, res, next) { // Add custom rate limit headers if available if (req.rateLimit) { res.setHeader('X-RateLimit-Limit', req.rateLimit.limit.toString()); res.setHeader('X-RateLimit-Remaining', req.rateLimit.remaining.toString()); res.setHeader('X-RateLimit-Reset', req.rateLimit.resetTime?.toISOString() || ''); res.setHeader('X-RateLimit-Used', (req.rateLimit.limit - req.rateLimit.remaining).toString()); } next(); }