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

@@ -16,18 +16,19 @@ export interface CloudflareUploadResponse {
*/
export async function uploadPendingImages(images: UploadedImage[]): Promise<UploadedImage[]> {
const uploadedImages: UploadedImage[] = [];
const newlyUploadedIds: string[] = []; // Track newly uploaded IDs for cleanup on error
let currentImageIndex = 0;
for (const image of images) {
if (image.isLocal && image.file) {
try {
try {
for (const image of images) {
if (image.isLocal && image.file) {
// Step 1: Get upload URL from our Supabase Edge Function
const { data: uploadUrlData, error: urlError } = await supabase.functions.invoke('upload-image', {
body: { action: 'get-upload-url' }
});
if (urlError || !uploadUrlData?.uploadURL) {
console.error('Error getting upload URL:', urlError);
throw new Error('Failed to get upload URL from Cloudflare');
throw new Error(`Failed to get upload URL: ${urlError?.message || 'Unknown error'}`);
}
// Step 2: Upload file directly to Cloudflare
@@ -40,7 +41,8 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
});
if (!uploadResponse.ok) {
throw new Error(`Upload failed with status: ${uploadResponse.status}`);
const errorText = await uploadResponse.text();
throw new Error(`Upload failed (status ${uploadResponse.status}): ${errorText}`);
}
const result: CloudflareUploadResponse = await uploadResponse.json();
@@ -49,6 +51,9 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
throw new Error('Cloudflare upload returned unsuccessful response');
}
// Track this newly uploaded image
newlyUploadedIds.push(result.result.id);
// Step 3: Return uploaded image metadata
uploadedImages.push({
url: result.result.variants[0], // Use first variant (usually the original)
@@ -59,20 +64,37 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
// Clean up object URL
URL.revokeObjectURL(image.url);
} catch (error) {
console.error('Error uploading image:', error);
throw new Error(`Failed to upload image: ${error instanceof Error ? error.message : 'Unknown error'}`);
} else {
// Already uploaded, keep as is
uploadedImages.push({
url: image.url,
cloudflare_id: image.cloudflare_id,
caption: image.caption,
isLocal: false,
});
}
} else {
// Already uploaded, keep as is
uploadedImages.push({
url: image.url,
cloudflare_id: image.cloudflare_id,
caption: image.caption,
isLocal: false,
});
currentImageIndex++;
}
}
return uploadedImages;
return uploadedImages;
} catch (error) {
// Cleanup: Attempt to delete newly uploaded images on error
if (newlyUploadedIds.length > 0) {
console.error(`Upload failed at image ${currentImageIndex + 1}. Cleaning up ${newlyUploadedIds.length} uploaded images...`);
// Attempt cleanup but don't throw if it fails
for (const imageId of newlyUploadedIds) {
try {
await supabase.functions.invoke('upload-image', {
body: { action: 'delete', imageId }
});
} catch (cleanupError) {
console.error(`Failed to cleanup image ${imageId}:`, cleanupError);
}
}
}
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new Error(`Failed to upload image ${currentImageIndex + 1} of ${images.length}: ${errorMessage}`);
}
}