Improve image handling, optimize hooks, and add rate limiting

This commit introduces several improvements:
- Enhances `RideModelCard` by safely accessing and displaying ride count and image data, preventing potential errors.
- Refactors `useEntityVersions` and `useSearch` hooks to use `useCallback` and improve performance and prevent race conditions.
- Introduces a `MAX_MAP_SIZE` and cleanup mechanism for the rate limiting map in `detect-location` Supabase function to prevent memory leaks.
- Adds robust error handling and cleanup for image uploads in `uploadPendingImages`.
- Modifies `ManufacturerModels` to correctly map and display ride counts.
- Includes error handling for topological sort in `process-selective-approval` Supabase function.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 39bb006b-d046-477f-a1f9-b7821836f3a1
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
This commit is contained in:
pac7
2025-10-08 17:55:37 +00:00
parent b17d36de03
commit 0f2219f849
8 changed files with 165 additions and 76 deletions

View File

@@ -15,12 +15,39 @@ interface IPLocationResponse {
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
const MAX_MAP_SIZE = 10000; // Maximum number of IPs to track
function cleanupExpiredEntries() {
const now = Date.now();
for (const [ip, data] of rateLimitMap.entries()) {
if (now > data.resetAt) {
rateLimitMap.delete(ip);
}
}
}
function checkRateLimit(ip: string): { allowed: boolean; retryAfter?: number } {
const now = Date.now();
const existing = rateLimitMap.get(ip);
if (!existing || now > existing.resetAt) {
// If map is too large, clean up expired entries first
if (rateLimitMap.size >= MAX_MAP_SIZE) {
cleanupExpiredEntries();
// If still too large after cleanup, clear oldest entries
if (rateLimitMap.size >= MAX_MAP_SIZE) {
const toDelete = Math.floor(MAX_MAP_SIZE * 0.2); // Remove 20% of entries
let deleted = 0;
for (const key of rateLimitMap.keys()) {
if (deleted >= toDelete) break;
rateLimitMap.delete(key);
deleted++;
}
console.warn(`Rate limit map reached size limit. Cleared ${deleted} entries.`);
}
}
// Create new entry or reset expired entry
rateLimitMap.set(ip, { count: 1, resetAt: now + RATE_LIMIT_WINDOW });
return { allowed: true };
@@ -36,14 +63,7 @@ function checkRateLimit(ip: string): { allowed: boolean; retryAfter?: number } {
}
// 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);
setInterval(cleanupExpiredEntries, RATE_LIMIT_WINDOW);
serve(async (req) => {
// Handle CORS preflight requests