const rateLimit = require('express-rate-limit'); const slowDown = require('express-slow-down'); const helmet = require('helmet'); // Rate limiting for authentication endpoints const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // limit each IP to 5 requests per windowMs message: 'Too many authentication attempts, please try again later', standardHeaders: true, legacyHeaders: false, skipSuccessfulRequests: true, // Vercel/proxy configuration validate: { trustProxy: false } // Disable validation for serverless environments }); // Rate limiting for general API endpoints const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: 'Too many requests from this IP, please try again later', standardHeaders: true, legacyHeaders: false, // Vercel/proxy configuration validate: { trustProxy: false } // Disable validation for serverless environments }); // Rate limiting for payment endpoints const paymentLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 10, // limit each IP to 10 payment requests per windowMs message: 'Too many payment attempts, please try again later', standardHeaders: true, legacyHeaders: false, // Vercel/proxy configuration validate: { trustProxy: false } // Disable validation for serverless environments }); // Slow down repeated requests const speedLimiter = slowDown({ windowMs: 15 * 60 * 1000, // 15 minutes delayAfter: 50, // allow 50 requests per 15 minutes, then... delayMs: 500 // begin adding 500ms of delay per request above 50 }); // CORS configuration const corsOptions = { origin: function (origin, callback) { // Allow requests with no origin (like mobile apps or curl requests) if (!origin) return callback(null, true); const allowedOrigins = process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',') : ['http://localhost:3000', 'http://localhost:3001']; if (allowedOrigins.indexOf(origin) !== -1) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } }, credentials: true, optionsSuccessStatus: 200 }; // Security headers const securityHeaders = helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'", "https://api.stripe.com"], frameSrc: ["'self'", "https://js.stripe.com", "https://hooks.stripe.com"], objectSrc: ["'none'"], upgradeInsecureRequests: [] } }, hsts: { maxAge: 31536000, includeSubDomains: true, preload: true } }); // Request validation middleware const validateRequest = (schema) => { return (req, res, next) => { const { error } = schema.validate(req.body); if (error) { return res.status(400).json({ message: 'Validation error', errors: error.details.map(detail => detail.message) }); } next(); }; }; // Subscription check middleware const checkSubscription = (requiredPlan = 'free') => { return async (req, res, next) => { try { const user = req.user; if (!user) { return res.status(401).json({ message: 'Authentication required' }); } const planHierarchy = { free: 0, basic: 1, premium: 2 }; const userPlanLevel = planHierarchy[user.subscription] || 0; const requiredPlanLevel = planHierarchy[requiredPlan] || 0; if (userPlanLevel < requiredPlanLevel) { return res.status(403).json({ message: `${requiredPlan} subscription required`, currentPlan: user.subscription, requiredPlan: requiredPlan }); } // Check guides limit for all users if (user.guidesUsed >= user.guidesLimit) { const message = user.subscription === 'free' ? 'You have no guides available. Use a promo code to get free guides or upgrade your subscription.' : 'Monthly guide limit reached. Upgrade your subscription for more guides.'; return res.status(403).json({ message, guidesUsed: user.guidesUsed, guidesLimit: user.guidesLimit, subscription: user.subscription }); } next(); } catch (error) { console.error('Subscription check error:', error); res.status(500).json({ message: 'Subscription validation failed' }); } }; }; // Guide usage tracking middleware const trackGuideUsage = async (req, res, next) => { try { const user = req.user; if (user && user.subscription !== 'free') { // For paid users, we might want to track usage differently next(); } else if (user && user.subscription === 'free') { // For free users, increment usage count await user.increment('guidesUsed'); next(); } else { next(); } } catch (error) { console.error('Guide usage tracking error:', error); // Don't block the request if tracking fails next(); } }; module.exports = { authLimiter, apiLimiter, paymentLimiter, speedLimiter, corsOptions, securityHeaders, validateRequest, checkSubscription, trackGuideUsage };