Google maps integration
This commit is contained in:
119
backend/middleware/rateLimiter.js
Normal file
119
backend/middleware/rateLimiter.js
Normal file
@@ -0,0 +1,119 @@
|
||||
const rateLimit = require("express-rate-limit");
|
||||
|
||||
// General rate limiter for Maps API endpoints
|
||||
const createMapsRateLimiter = (windowMs, max, message) => {
|
||||
return rateLimit({
|
||||
windowMs, // time window in milliseconds
|
||||
max, // limit each IP/user to max requests per windowMs
|
||||
message: {
|
||||
error: message,
|
||||
retryAfter: Math.ceil(windowMs / 1000), // seconds
|
||||
},
|
||||
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
||||
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
||||
// Use user ID if available, otherwise fall back to IPv6-safe IP handling
|
||||
keyGenerator: (req) => {
|
||||
if (req.user?.id) {
|
||||
return `user:${req.user.id}`;
|
||||
}
|
||||
// Use the built-in IP key generator which properly handles IPv6
|
||||
return rateLimit.defaultKeyGenerator(req);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Specific rate limiters for different endpoints
|
||||
const rateLimiters = {
|
||||
// Places Autocomplete - allow more requests since users type frequently
|
||||
placesAutocomplete: createMapsRateLimiter(
|
||||
60 * 1000, // 1 minute window
|
||||
30, // 30 requests per minute per user/IP
|
||||
"Too many autocomplete requests. Please slow down."
|
||||
),
|
||||
|
||||
// Place Details - moderate limit since each selection triggers this
|
||||
placeDetails: createMapsRateLimiter(
|
||||
60 * 1000, // 1 minute window
|
||||
20, // 20 requests per minute per user/IP
|
||||
"Too many place detail requests. Please slow down."
|
||||
),
|
||||
|
||||
// Geocoding - lower limit since this is typically used less frequently
|
||||
geocoding: createMapsRateLimiter(
|
||||
60 * 1000, // 1 minute window
|
||||
10, // 10 requests per minute per user/IP
|
||||
"Too many geocoding requests. Please slow down."
|
||||
),
|
||||
};
|
||||
|
||||
// Enhanced rate limiter with user-specific limits
|
||||
const createUserBasedRateLimiter = (windowMs, max, message) => {
|
||||
const store = new Map(); // Simple in-memory store (use Redis in production)
|
||||
|
||||
return (req, res, next) => {
|
||||
const key = req.user?.id
|
||||
? `user:${req.user.id}`
|
||||
: rateLimit.defaultKeyGenerator(req);
|
||||
const now = Date.now();
|
||||
const windowStart = now - windowMs;
|
||||
|
||||
// Clean up old entries
|
||||
for (const [k, data] of store.entries()) {
|
||||
if (data.windowStart < windowStart) {
|
||||
store.delete(k);
|
||||
}
|
||||
}
|
||||
|
||||
// Get or create user's request data
|
||||
let userData = store.get(key);
|
||||
if (!userData || userData.windowStart < windowStart) {
|
||||
userData = {
|
||||
count: 0,
|
||||
windowStart: now,
|
||||
resetTime: now + windowMs,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if limit exceeded
|
||||
if (userData.count >= max) {
|
||||
return res.status(429).json({
|
||||
error: message,
|
||||
retryAfter: Math.ceil((userData.resetTime - now) / 1000),
|
||||
});
|
||||
}
|
||||
|
||||
// Increment counter and store
|
||||
userData.count++;
|
||||
store.set(key, userData);
|
||||
|
||||
// Add headers
|
||||
res.set({
|
||||
"RateLimit-Limit": max,
|
||||
"RateLimit-Remaining": Math.max(0, max - userData.count),
|
||||
"RateLimit-Reset": new Date(userData.resetTime).toISOString(),
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
// Burst protection for expensive operations
|
||||
const burstProtection = createUserBasedRateLimiter(
|
||||
10 * 1000, // 10 seconds
|
||||
5, // 5 requests per 10 seconds
|
||||
"Too many requests in a short period. Please slow down."
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
// Individual rate limiters
|
||||
placesAutocomplete: rateLimiters.placesAutocomplete,
|
||||
placeDetails: rateLimiters.placeDetails,
|
||||
geocoding: rateLimiters.geocoding,
|
||||
|
||||
// Burst protection
|
||||
burstProtection,
|
||||
|
||||
// Utility functions
|
||||
createMapsRateLimiter,
|
||||
createUserBasedRateLimiter,
|
||||
};
|
||||
Reference in New Issue
Block a user