Improve error handling and display for searches and uploads

Enhance user feedback by displaying search errors, refine photo submission fetching, add rate limiting cleanup logic, improve image upload cleanup, and strengthen moderator permission checks.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 2741d09b-80fb-4f0a-bfd6-ababb2ac4bfc
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
This commit is contained in:
pac7
2025-10-08 19:55:55 +00:00
parent 7101632977
commit 13a4d8f64c
6 changed files with 90 additions and 33 deletions

View File

@@ -41,36 +41,38 @@ 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, remove entries based on LRU (oldest resetAt)
if (rateLimitMap.size >= MAX_MAP_SIZE) {
const toDelete = Math.floor(MAX_MAP_SIZE * 0.3); // Remove 30% of entries
const sortedEntries = Array.from(rateLimitMap.entries())
.sort((a, b) => a[1].resetAt - b[1].resetAt);
for (let i = 0; i < toDelete && i < sortedEntries.length; i++) {
rateLimitMap.delete(sortedEntries[i][0]);
}
console.warn(`Rate limit map reached ${MAX_MAP_SIZE} entries. Cleared ${toDelete} oldest entries.`);
}
// Handle existing entries (most common case - early return for performance)
if (existing && now <= existing.resetAt) {
if (existing.count >= MAX_REQUESTS) {
const retryAfter = Math.ceil((existing.resetAt - now) / 1000);
return { allowed: false, retryAfter };
}
// Create new entry or reset expired entry
rateLimitMap.set(ip, { count: 1, resetAt: now + RATE_LIMIT_WINDOW });
existing.count++;
return { allowed: true };
}
if (existing.count >= MAX_REQUESTS) {
const retryAfter = Math.ceil((existing.resetAt - now) / 1000);
return { allowed: false, retryAfter };
// Need to add new entry or reset expired one
// Only perform cleanup if we're at capacity AND adding a new IP
if (!existing && rateLimitMap.size >= MAX_MAP_SIZE) {
// First try cleaning expired entries
cleanupExpiredEntries();
// If still at capacity after cleanup, remove oldest entries (LRU eviction)
if (rateLimitMap.size >= MAX_MAP_SIZE) {
const toDelete = Math.floor(MAX_MAP_SIZE * 0.3); // Remove 30% of entries
const sortedEntries = Array.from(rateLimitMap.entries())
.sort((a, b) => a[1].resetAt - b[1].resetAt);
for (let i = 0; i < toDelete && i < sortedEntries.length; i++) {
rateLimitMap.delete(sortedEntries[i][0]);
}
console.warn(`Rate limit map reached ${MAX_MAP_SIZE} entries. Cleared ${toDelete} oldest entries.`);
}
}
existing.count++;
// Create new entry or reset expired entry
rateLimitMap.set(ip, { count: 1, resetAt: now + RATE_LIMIT_WINDOW });
return { allowed: true };
}