feat: Implement final error coverage

This commit is contained in:
gpt-engineer-app[bot]
2025-11-04 19:50:06 +00:00
parent a9334c7a3a
commit 0df047d56b
9 changed files with 291 additions and 403 deletions

View File

@@ -1,7 +1,7 @@
import { supabase } from '@/lib/supabaseClient';
import { invokeWithTracking } from './edgeFunctionTracking';
import type { UploadedImage } from '@/components/upload/EntityMultiImageUploader';
import { logger } from './logger';
import { handleError, handleNonCriticalError } from './errorHandler';
export interface CloudflareUploadResponse {
result: {
@@ -34,20 +34,14 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
);
if (urlError || !uploadUrlData?.uploadURL) {
logger.error('Failed to get upload URL', {
action: 'upload_pending_images',
fileName,
requestId,
error: urlError?.message || 'Unknown error',
const error = new Error(`Failed to get upload URL for "${fileName}": ${urlError?.message || 'Unknown error'}`);
handleError(error, {
action: 'Get Upload URL',
metadata: { fileName, requestId }
});
throw new Error(`Failed to get upload URL for "${fileName}": ${urlError?.message || 'Unknown error'}`);
throw error;
}
logger.info('Got upload URL', {
action: 'upload_pending_images',
fileName,
requestId,
});
// Step 2: Upload file directly to Cloudflare
const formData = new FormData();
@@ -60,30 +54,25 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
if (!uploadResponse.ok) {
const errorText = await uploadResponse.text();
logger.error('Cloudflare upload failed', {
action: 'upload_pending_images',
fileName,
status: uploadResponse.status,
error: errorText,
const error = new Error(`Upload failed for "${fileName}" (status ${uploadResponse.status}): ${errorText}`);
handleError(error, {
action: 'Cloudflare Upload',
metadata: { fileName, status: uploadResponse.status }
});
throw new Error(`Upload failed for "${fileName}" (status ${uploadResponse.status}): ${errorText}`);
throw error;
}
const result: CloudflareUploadResponse = await uploadResponse.json();
if (!result.success || !result.result) {
logger.error('Cloudflare upload unsuccessful', {
action: 'upload_pending_images',
fileName,
const error = new Error(`Cloudflare upload returned unsuccessful response for "${fileName}"`);
handleError(error, {
action: 'Cloudflare Upload',
metadata: { fileName }
});
throw new Error(`Cloudflare upload returned unsuccessful response for "${fileName}"`);
throw error;
}
logger.info('Image uploaded successfully', {
action: 'upload_pending_images',
fileName,
imageId: result.result.id,
});
// Clean up object URL
URL.revokeObjectURL(image.url);
@@ -132,10 +121,13 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
// If any uploads failed, clean up ONLY newly uploaded images and throw error
if (errors.length > 0) {
if (newlyUploadedImageIds.length > 0) {
logger.error('Some uploads failed, cleaning up', {
action: 'upload_pending_images',
newlyUploadedCount: newlyUploadedImageIds.length,
failureCount: errors.length,
const cleanupError = new Error(`Some uploads failed, cleaning up ${newlyUploadedImageIds.length} newly uploaded images`);
handleError(cleanupError, {
action: 'Upload Cleanup',
metadata: {
newlyUploadedCount: newlyUploadedImageIds.length,
failureCount: errors.length
}
});
// Attempt cleanup in parallel with detailed error tracking
@@ -148,24 +140,29 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
)
);
// Track cleanup failures for better debugging
// Track cleanup failures silently (non-critical)
const cleanupFailures = cleanupResults.filter(r => r.status === 'rejected');
if (cleanupFailures.length > 0) {
logger.error('Failed to cleanup images', {
action: 'upload_pending_images_cleanup',
cleanupFailures: cleanupFailures.length,
totalCleanup: newlyUploadedImageIds.length,
orphanedImages: newlyUploadedImageIds.filter((_, i) => cleanupResults[i].status === 'rejected'),
});
} else {
logger.info('Successfully cleaned up images', {
action: 'upload_pending_images_cleanup',
cleanedCount: newlyUploadedImageIds.length,
});
handleNonCriticalError(
new Error(`Failed to cleanup ${cleanupFailures.length} of ${newlyUploadedImageIds.length} images`),
{
action: 'Image Cleanup',
metadata: {
cleanupFailures: cleanupFailures.length,
totalCleanup: newlyUploadedImageIds.length,
orphanedImages: newlyUploadedImageIds.filter((_, i) => cleanupResults[i].status === 'rejected')
}
}
);
}
}
throw new Error(`Failed to upload ${errors.length} of ${images.length} images: ${errors.join('; ')}`);
const finalError = new Error(`Failed to upload ${errors.length} of ${images.length} images: ${errors.join('; ')}`);
handleError(finalError, {
action: 'Image Upload',
metadata: { failureCount: errors.length, totalCount: images.length }
});
throw finalError;
}
// Remove the wasNewlyUploaded flag before returning