Add rate limiting to location detection and standardize error messages

Introduces rate limiting to the `detect-location` function and refactors error responses in `upload-image` to provide consistent, descriptive messages.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: b3d7d4df-59a9-4a9c-971d-175b92dadbfa
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7cdf4e95-3f41-4180-b8e3-8ef56d032c0e/b3d7d4df-59a9-4a9c-971d-175b92dadbfa/1VcvPNb
This commit is contained in:
pac7
2025-10-08 17:18:00 +00:00
parent 3e2ce53fa7
commit 61285b0261
3 changed files with 150 additions and 26 deletions

View File

@@ -11,6 +11,40 @@ interface IPLocationResponse {
measurementSystem: 'metric' | 'imperial';
}
// Simple in-memory rate limiter
const rateLimitMap = new Map<string, { count: number; resetAt: number }>();
const RATE_LIMIT_WINDOW = 60000; // 1 minute in milliseconds
const MAX_REQUESTS = 10; // 10 requests per minute per IP
function checkRateLimit(ip: string): { allowed: boolean; retryAfter?: number } {
const now = Date.now();
const existing = rateLimitMap.get(ip);
if (!existing || now > existing.resetAt) {
// Create new entry or reset expired entry
rateLimitMap.set(ip, { count: 1, resetAt: now + RATE_LIMIT_WINDOW });
return { allowed: true };
}
if (existing.count >= MAX_REQUESTS) {
const retryAfter = Math.ceil((existing.resetAt - now) / 1000);
return { allowed: false, retryAfter };
}
existing.count++;
return { allowed: true };
}
// Clean up old entries periodically to prevent memory leak
setInterval(() => {
const now = Date.now();
for (const [ip, data] of rateLimitMap.entries()) {
if (now > data.resetAt) {
rateLimitMap.delete(ip);
}
}
}, RATE_LIMIT_WINDOW);
serve(async (req) => {
// Handle CORS preflight requests
if (req.method === 'OPTIONS') {
@@ -23,6 +57,26 @@ serve(async (req) => {
const realIP = req.headers.get('x-real-ip');
const clientIP = forwarded?.split(',')[0] || realIP || '8.8.8.8'; // fallback to Google DNS for testing
// Check rate limit
const rateLimit = checkRateLimit(clientIP);
if (!rateLimit.allowed) {
return new Response(
JSON.stringify({
error: 'Rate limit exceeded',
message: 'Too many requests. Please try again later.',
retryAfter: rateLimit.retryAfter
}),
{
status: 429,
headers: {
...corsHeaders,
'Content-Type': 'application/json',
'Retry-After': String(rateLimit.retryAfter || 60)
}
}
);
}
console.log('Detecting location for IP:', clientIP);
// Use configurable geolocation service with proper error handling