import rateLimit from 'express-rate-limit' import { getRateLimit } from '../config/env' /** * Skip rate limiting for successful requests from localhost (development) */ const skipLocalhost = (req: any): boolean => { return req.socket?.remoteAddress?.includes('127.0.0.1') || req.socket?.remoteAddress?.includes('::1') || req.ip?.includes('127.0.0.1') || req.ip?.includes('::1') } // Get rate limit values from env vars (with defaults) const WEBHOOK_LIMIT = getRateLimit('RATE_LIMIT_WEBHOOK', 10) const HEALTH_LIMIT = getRateLimit('RATE_LIMIT_HEALTH', 30) const DEFAULT_LIMIT = getRateLimit('RATE_LIMIT_DEFAULT', 60) /** * Webhook limiter: 10 requests per minute per IP (configurable via RATE_LIMIT_WEBHOOK) * RevenueCat retries are spaced minutes apart, so this is generous. * Successful webhooks (HTTP 200) DO count toward the limit — a processed * webhook should not give an attacker unlimited free requests. */ export const webhookLimiter = rateLimit({ windowMs: 60 * 1000, // 1 minute max: WEBHOOK_LIMIT, standardHeaders: true, legacyHeaders: false, keyGenerator: (req) => req.ip || req.socket?.remoteAddress || 'unknown', skip: (req) => skipLocalhost(req), }) /** * Health limiter: 30 requests per minute per IP (configurable via RATE_LIMIT_HEALTH) * Allows frequent health checks without abuse. */ export const healthLimiter = rateLimit({ windowMs: 60 * 1000, // 1 minute max: HEALTH_LIMIT, standardHeaders: true, legacyHeaders: false, keyGenerator: (req) => req.ip || req.socket?.remoteAddress || 'unknown', skip: (req) => skipLocalhost(req), }) /** * Default/API limiter: 60 requests per minute per IP (configurable via RATE_LIMIT_DEFAULT) * For future authenticated routes. */ export const defaultLimiter = rateLimit({ windowMs: 60 * 1000, // 1 minute max: DEFAULT_LIMIT, standardHeaders: true, legacyHeaders: false, keyGenerator: (req) => req.ip || req.socket?.remoteAddress || 'unknown', skip: (req) => skipLocalhost(req), })