90 lines
2.9 KiB
JavaScript
90 lines
2.9 KiB
JavaScript
const rateLimit = require('express-rate-limit');
|
|
|
|
// In-memory store for per-recipient email tracking
|
|
const emailTracker = new Map();
|
|
|
|
// Helper function to clean up expired entries
|
|
function cleanupExpiredEntries() {
|
|
const now = Date.now();
|
|
for (const [key, timestamp] of emailTracker.entries()) {
|
|
if (now - timestamp > 5 * 60 * 1000) { // 5 minutes
|
|
emailTracker.delete(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean up expired entries every minute
|
|
setInterval(cleanupExpiredEntries, 60 * 1000);
|
|
|
|
// General API rate limiter
|
|
const general = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 100, // limit each IP to 100 requests per windowMs
|
|
message: {
|
|
error: 'Too many requests from this IP, please try again later.',
|
|
retryAfter: 15 * 60 // 15 minutes in seconds
|
|
},
|
|
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
|
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
});
|
|
|
|
// Email sending rate limiter (general - keeps existing behavior)
|
|
const email = rateLimit({
|
|
windowMs: 60 * 60 * 1000, // 1 hour
|
|
max: 10, // limit each IP to 10 emails per hour
|
|
message: {
|
|
error: 'Too many emails sent from this IP, please try again later.',
|
|
retryAfter: 60 * 60 // 1 hour in seconds
|
|
},
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
skipSuccessfulRequests: false, // Don't skip counting successful requests
|
|
});
|
|
|
|
// Custom middleware for per-recipient email rate limiting
|
|
const perRecipientEmailLimiter = (req, res, next) => {
|
|
const clientIp = req.ip || req.connection.remoteAddress;
|
|
const recipientEmail = req.body.recipientEmail;
|
|
|
|
if (!recipientEmail) {
|
|
return next(); // Let validation middleware handle missing recipient
|
|
}
|
|
|
|
const trackingKey = `${clientIp}:${recipientEmail}`;
|
|
const now = Date.now();
|
|
const lastSent = emailTracker.get(trackingKey);
|
|
|
|
if (lastSent && (now - lastSent) < 5 * 60 * 1000) { // 5 minutes
|
|
const timeRemaining = Math.ceil((5 * 60 * 1000 - (now - lastSent)) / 1000);
|
|
return res.status(429).json({
|
|
success: false,
|
|
error: 'Rate limit exceeded',
|
|
message: `You can only send one email per representative every 5 minutes. Please wait ${Math.ceil(timeRemaining / 60)} more minutes before sending another email to this representative.`,
|
|
retryAfter: timeRemaining,
|
|
rateLimitType: 'per-recipient'
|
|
});
|
|
}
|
|
|
|
// Store the current timestamp for this IP-recipient combination
|
|
emailTracker.set(trackingKey, now);
|
|
next();
|
|
};
|
|
|
|
// Represent API rate limiter (more restrictive)
|
|
const representAPI = rateLimit({
|
|
windowMs: 60 * 1000, // 1 minute
|
|
max: 60, // match the Represent API limit of 60 requests per minute
|
|
message: {
|
|
error: 'Represent API rate limit exceeded, please try again later.',
|
|
retryAfter: 60 // 1 minute in seconds
|
|
},
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
});
|
|
|
|
module.exports = {
|
|
general,
|
|
email,
|
|
perRecipientEmailLimiter,
|
|
representAPI
|
|
}; |