mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 08:11:12 -05:00
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:
@@ -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}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user