diff --git a/src/lib/identityService.ts b/src/lib/identityService.ts index 26a313af..4fd0a821 100644 --- a/src/lib/identityService.ts +++ b/src/lib/identityService.ts @@ -11,8 +11,7 @@ import type { IdentitySafetyCheck, IdentityOperationResult } from '@/types/identity'; -import { logger } from './logger'; -import { getErrorMessage } from './errorHandler'; +import { handleNonCriticalError, handleError, getErrorMessage } from './errorHandler'; /** * Get all identities for the current user @@ -25,10 +24,9 @@ export async function getUserIdentities(): Promise { return (data?.identities || []) as UserIdentity[]; } catch (error) { - const errorMsg = getErrorMessage(error); - logger.error('Failed to get user identities', { - action: 'get_identities', - error: errorMsg + handleNonCriticalError(error, { + action: 'Get User Identities', + metadata: { returnedEmptyArray: true } }); return []; } @@ -102,9 +100,9 @@ export async function disconnectIdentity( // Get AAL level - fail closed on error const { data: aalData, error: aalError } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel(); if (aalError) { - logger.error('Failed to get AAL level for identity disconnect', { - action: 'disconnect_identity_aal_check', - error: aalError.message + handleNonCriticalError(aalError, { + action: 'Get AAL Level (Identity Disconnect)', + metadata: { failClosed: true } }); return { success: false, @@ -120,9 +118,9 @@ export async function disconnectIdentity( const { data: factors, error: factorsError } = await supabase.auth.mfa.listFactors(); if (factorsError) { - logger.error('Failed to list MFA factors for identity disconnect', { - action: 'disconnect_identity_mfa_check', - error: factorsError.message + handleNonCriticalError(factorsError, { + action: 'List MFA Factors (Identity Disconnect)', + metadata: { failClosed: true } }); return { success: false, @@ -177,15 +175,13 @@ export async function disconnectIdentity( return { success: true }; } catch (error) { - const errorMsg = getErrorMessage(error); - logger.error('Failed to disconnect identity', { - action: 'identity_disconnect', - provider, - error: errorMsg + handleError(error, { + action: 'Disconnect Identity', + metadata: { provider } }); return { success: false, - error: errorMsg + error: getErrorMessage(error) }; } } @@ -210,15 +206,13 @@ export async function connectIdentity( return { success: true }; } catch (error) { - const errorMsg = getErrorMessage(error); - logger.error('Failed to connect identity', { - action: 'identity_connect', - provider, - error: errorMsg + handleError(error, { + action: 'Connect Identity', + metadata: { provider } }); return { success: false, - error: errorMsg + error: getErrorMessage(error) }; } } @@ -240,11 +234,6 @@ export async function addPasswordToAccount(): Promise { }; } - logger.info('Initiating password setup', { - action: 'password_setup_initiated', - email: userEmail - }); - // Trigger Supabase password reset email // User clicks link and sets password, which automatically creates email identity const { error: resetError } = await supabase.auth.resetPasswordForEmail( @@ -255,20 +244,14 @@ export async function addPasswordToAccount(): Promise { ); if (resetError) { - logger.error('Failed to send password reset email', { + handleError(resetError, { + action: 'Send Password Reset Email', userId: user?.id, - action: 'password_setup_email', - error: resetError.message + metadata: { email: userEmail } }); throw resetError; } - logger.info('Password reset email sent', { - userId: user!.id, - action: 'password_setup_initiated', - email: userEmail - }); - // Log the action await logIdentityChange(user!.id, 'password_setup_initiated', { method: 'reset_password_flow', @@ -281,14 +264,12 @@ export async function addPasswordToAccount(): Promise { email: userEmail }; } catch (error) { - const errorMsg = getErrorMessage(error); - logger.error('Failed to initiate password setup', { - action: 'password_setup', - error: errorMsg + handleError(error, { + action: 'Initiate Password Setup' }); return { success: false, - error: errorMsg + error: getErrorMessage(error) }; } } @@ -310,10 +291,10 @@ async function logIdentityChange( _details: details }); } catch (error) { - logger.error('Failed to log identity change to audit', { + handleNonCriticalError(error, { + action: 'Log Identity Change to Audit', userId, - action, - error: error instanceof Error ? error.message : String(error) + metadata: { auditAction: action } }); // Don't fail the operation if audit logging fails } diff --git a/src/lib/imageUploadHelper.ts b/src/lib/imageUploadHelper.ts index 7e34b2ee..a75b1151 100644 --- a/src/lib/imageUploadHelper.ts +++ b/src/lib/imageUploadHelper.ts @@ -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 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 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 diff --git a/src/lib/moderation/actions.ts b/src/lib/moderation/actions.ts index 268e3352..24f663c2 100644 --- a/src/lib/moderation/actions.ts +++ b/src/lib/moderation/actions.ts @@ -9,8 +9,7 @@ import { SupabaseClient } from '@supabase/supabase-js'; import { createTableQuery } from '@/lib/supabaseHelpers'; import type { ModerationItem } from '@/types/moderation'; -import { logger } from '@/lib/logger'; -import { getErrorMessage } from '@/lib/errorHandler'; +import { handleError, handleNonCriticalError, getErrorMessage } from '@/lib/errorHandler'; import { invokeWithTracking, invokeBatchWithTracking } from '@/lib/edgeFunctionTracking'; /** @@ -154,16 +153,15 @@ export async function approvePhotoSubmission( shouldRemoveFromQueue: true, }; } catch (error: unknown) { - const errorMessage = getErrorMessage(error); - logger.error('Photo approval failed', { - action: 'approve_photo', - submissionId: config.submissionId, - error: errorMessage + handleError(error, { + action: 'Approve Photo Submission', + userId: config.moderatorId, + metadata: { submissionId: config.submissionId } }); return { success: false, message: 'Failed to approve photo submission', - error: new Error(errorMessage), + error: error instanceof Error ? error : new Error(getErrorMessage(error)), shouldRemoveFromQueue: false, }; } @@ -194,22 +192,14 @@ export async function approveSubmissionItems( ); if (approvalError) { - logger.error('Submission items approval failed via edge function', { - action: 'approve_submission_items', - submissionId, - itemCount: itemIds.length, - requestId, - error: approvalError.message, + const error = new Error(`Failed to process submission items: ${approvalError.message}`); + handleError(error, { + action: 'Approve Submission Items', + metadata: { submissionId, itemCount: itemIds.length, requestId } }); - throw new Error(`Failed to process submission items: ${approvalError.message}`); + throw error; } - logger.info('Submission items approved successfully', { - action: 'approve_submission_items', - submissionId, - itemCount: itemIds.length, - requestId, - }); return { success: true, @@ -217,17 +207,14 @@ export async function approveSubmissionItems( shouldRemoveFromQueue: true, }; } catch (error: unknown) { - const errorMessage = getErrorMessage(error); - logger.error('Submission items approval failed', { - action: 'approve_submission_items', - submissionId, - itemCount: itemIds.length, - error: errorMessage + handleError(error, { + action: 'Approve Submission Items', + metadata: { submissionId, itemCount: itemIds.length } }); return { success: false, message: 'Failed to approve submission items', - error: new Error(errorMessage), + error: error instanceof Error ? error : new Error(getErrorMessage(error)), shouldRemoveFromQueue: false, }; } @@ -260,11 +247,9 @@ export async function rejectSubmissionItems( .eq('status', 'pending'); if (rejectError) { - const errorMessage = getErrorMessage(rejectError); - logger.error('Item rejection cascade failed', { - action: 'reject_submission_items', - submissionId, - error: errorMessage + handleError(rejectError, { + action: 'Reject Submission Items (Cascade)', + metadata: { submissionId } }); } @@ -274,16 +259,14 @@ export async function rejectSubmissionItems( shouldRemoveFromQueue: false, // Parent rejection will handle removal }; } catch (error: unknown) { - const errorMessage = getErrorMessage(error); - logger.error('Submission items rejection failed', { - action: 'reject_submission_items', - submissionId, - error: errorMessage + handleError(error, { + action: 'Reject Submission Items', + metadata: { submissionId } }); return { success: false, message: 'Failed to reject submission items', - error: new Error(errorMessage), + error: error instanceof Error ? error : new Error(getErrorMessage(error)), shouldRemoveFromQueue: false, }; } @@ -412,17 +395,15 @@ export async function performModerationAction( shouldRemoveFromQueue: action === 'approved' || action === 'rejected', }; } catch (error: unknown) { - const errorMessage = getErrorMessage(error); - logger.error('Moderation action failed', { - action: config.action, - itemType: item.type, - itemId: item.id, - error: errorMessage + handleError(error, { + action: `${config.action === 'approved' ? 'Approve' : 'Reject'} Content`, + userId: config.moderatorId, + metadata: { itemType: item.type, itemId: item.id } }); return { success: false, message: `Failed to ${config.action} content`, - error: new Error(errorMessage), + error: error instanceof Error ? error : new Error(getErrorMessage(error)), shouldRemoveFromQueue: false, }; } @@ -513,15 +494,20 @@ export async function deleteSubmission( const successfulDeletions = deleteResults.filter(r => !r.error); deletedPhotoCount = successfulDeletions.length; - // Log any failures + // Log any failures silently (background operation) const failedDeletions = deleteResults.filter(r => r.error); if (failedDeletions.length > 0) { - logger.error('Some photo deletions failed', { - action: 'delete_submission_photos', - failureCount: failedDeletions.length, - totalAttempted: validImageIds.length, - failedRequestIds: failedDeletions.map(r => r.requestId), - }); + handleNonCriticalError( + new Error(`Failed to delete ${failedDeletions.length} of ${validImageIds.length} photos`), + { + action: 'Delete Submission Photos', + metadata: { + failureCount: failedDeletions.length, + totalAttempted: validImageIds.length, + failedRequestIds: failedDeletions.map(r => r.requestId) + } + } + ); } } } @@ -563,12 +549,15 @@ export async function deleteSubmission( message, shouldRemoveFromQueue: true, }; - } catch (error) { - logger.error('Error deleting submission', { error, submissionId: item.id }); + } catch (error: unknown) { + handleError(error, { + action: 'Delete Submission', + metadata: { submissionId: item.id, deletePhotos } + }); return { success: false, message: 'Failed to delete submission', - error: error as Error, + error: error instanceof Error ? error : new Error('Unknown error'), shouldRemoveFromQueue: false, }; } diff --git a/src/lib/moderation/lockMonitor.ts b/src/lib/moderation/lockMonitor.ts index 7e80a381..b56f90f3 100644 --- a/src/lib/moderation/lockMonitor.ts +++ b/src/lib/moderation/lockMonitor.ts @@ -11,7 +11,7 @@ import type { ModerationAction } from '../moderationStateMachine'; import { hasActiveLock, needsLockRenewal } from '../moderationStateMachine'; import { toast } from '@/hooks/use-toast'; import { supabase } from '@/lib/supabaseClient'; -import { logger } from '../logger'; +import { handleNonCriticalError } from '../errorHandler'; /** * Hook to monitor lock status and warn about expiry @@ -33,14 +33,6 @@ export function useLockMonitor( const checkInterval = setInterval(() => { if (needsLockRenewal(state)) { - logger.warn('Lock expiring soon', { - action: 'lock_expiry_warning', - itemId, - lockExpires: state.status === 'locked' || state.status === 'reviewing' - ? state.lockExpires - : undefined, - }); - // Dispatch lock expiry warning dispatch({ type: 'LOCK_EXPIRED' }); @@ -103,16 +95,10 @@ export async function handleExtendLock( title: 'Lock Extended', description: 'You have 15 more minutes to complete your review.', }); - - logger.info('Lock extended successfully', { - action: 'lock_extended', - submissionId, - }); } catch (error: unknown) { - logger.error('Failed to extend lock', { - action: 'extend_lock_error', - submissionId, - error: error instanceof Error ? error.message : String(error), + handleNonCriticalError(error, { + action: 'Extend Lock', + metadata: { submissionId } }); toast({ diff --git a/src/lib/notificationService.ts b/src/lib/notificationService.ts index ef657fbc..cf953e99 100644 --- a/src/lib/notificationService.ts +++ b/src/lib/notificationService.ts @@ -1,7 +1,6 @@ import { supabase } from "@/lib/supabaseClient"; import { invokeWithTracking } from "@/lib/edgeFunctionTracking"; -import { logger } from "@/lib/logger"; -import { AppError } from "@/lib/errorHandler"; +import { handleNonCriticalError, AppError } from "@/lib/errorHandler"; import { z } from "zod"; import type { NotificationPayload, @@ -29,9 +28,9 @@ class NotificationService { return !!data?.setting_value; } catch (error: unknown) { - logger.error('Failed to check Novu status', { - action: 'check_novu_status', - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Check Novu Status', + metadata: { returnedFalse: true } }); return false; } @@ -47,10 +46,6 @@ class NotificationService { const novuEnabled = await this.isNovuEnabled(); if (!novuEnabled) { - logger.warn('Novu not configured, skipping subscriber update', { - action: 'update_novu_subscriber', - userId: validated.subscriberId - }); return { success: false, error: 'Novu not configured' }; } @@ -60,11 +55,10 @@ class NotificationService { ); if (error) { - logger.error('Edge function error updating Novu subscriber', { - action: 'update_novu_subscriber', + handleNonCriticalError(error, { + action: 'Update Novu Subscriber (Edge Function)', userId: validated.subscriberId, - requestId, - error: error.message + metadata: { requestId } }); throw new AppError( 'Failed to update notification subscriber', @@ -73,18 +67,11 @@ class NotificationService { ); } - logger.info('Novu subscriber updated successfully', { - action: 'update_novu_subscriber', - userId: validated.subscriberId, - requestId - }); - return { success: true }; } catch (error: unknown) { - logger.error('Error in updateSubscriber', { - action: 'update_novu_subscriber', - userId: subscriberData.subscriberId, - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Update Novu Subscriber', + userId: subscriberData.subscriberId }); return { @@ -104,10 +91,6 @@ class NotificationService { const novuEnabled = await this.isNovuEnabled(); if (!novuEnabled) { - logger.warn('Novu not configured, skipping subscriber creation', { - action: 'create_novu_subscriber', - userId: validated.subscriberId - }); return { success: false, error: 'Novu not configured' }; } @@ -117,11 +100,10 @@ class NotificationService { ); if (error) { - logger.error('Edge function error creating Novu subscriber', { - action: 'create_novu_subscriber', + handleNonCriticalError(error, { + action: 'Create Novu Subscriber (Edge Function)', userId: validated.subscriberId, - requestId, - error: error.message + metadata: { requestId } }); throw new AppError( 'Failed to create notification subscriber', @@ -146,27 +128,18 @@ class NotificationService { }); if (dbError) { - logger.error('Failed to store subscriber preferences', { - action: 'store_subscriber_preferences', - userId: validated.subscriberId, - error: dbError.message, - errorCode: dbError.code + handleNonCriticalError(dbError, { + action: 'Store Subscriber Preferences', + userId: validated.subscriberId }); throw dbError; } - logger.info('Novu subscriber created successfully', { - action: 'create_novu_subscriber', - userId: validated.subscriberId, - requestId - }); - return { success: true }; } catch (error: unknown) { - logger.error('Error in createSubscriber', { - action: 'create_novu_subscriber', - userId: subscriberData.subscriberId, - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Create Novu Subscriber', + userId: subscriberData.subscriberId }); return { @@ -207,11 +180,10 @@ class NotificationService { ); if (novuError) { - logger.error('Failed to update Novu preferences', { - action: 'update_novu_preferences', + handleNonCriticalError(novuError, { + action: 'Update Novu Preferences', userId, - requestId, - error: novuError.message + metadata: { requestId } }); throw novuError; } @@ -228,11 +200,9 @@ class NotificationService { }); if (dbError) { - logger.error('Failed to save notification preferences', { - action: 'save_notification_preferences', - userId, - error: dbError.message, - errorCode: dbError.code + handleNonCriticalError(dbError, { + action: 'Save Notification Preferences', + userId }); throw dbError; } @@ -268,17 +238,11 @@ class NotificationService { }); } - logger.info('Notification preferences updated', { - action: 'update_notification_preferences', - userId - }); - return { success: true }; } catch (error: unknown) { - logger.error('Error updating notification preferences', { - action: 'update_notification_preferences', - userId, - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Update Notification Preferences', + userId }); if (error instanceof z.ZodError) { @@ -307,20 +271,14 @@ class NotificationService { .maybeSingle(); if (error && error.code !== 'PGRST116') { - logger.error('Failed to fetch notification preferences', { - action: 'fetch_notification_preferences', - userId, - error: error.message, - errorCode: error.code + handleNonCriticalError(error, { + action: 'Fetch Notification Preferences', + userId }); throw error; } if (!data) { - logger.info('No preferences found, returning defaults', { - action: 'fetch_notification_preferences', - userId - }); return DEFAULT_NOTIFICATION_PREFERENCES; } @@ -331,10 +289,9 @@ class NotificationService { frequencySettings: data.frequency_settings }); } catch (error: unknown) { - logger.error('Error fetching notification preferences', { - action: 'fetch_notification_preferences', - userId, - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Get Notification Preferences', + userId }); return null; } @@ -352,10 +309,8 @@ class NotificationService { .order('category', { ascending: true }); if (error) { - logger.error('Failed to fetch notification templates', { - action: 'fetch_notification_templates', - error: error.message, - errorCode: error.code + handleNonCriticalError(error, { + action: 'Fetch Notification Templates' }); throw error; } @@ -367,9 +322,8 @@ class NotificationService { novu_workflow_id: t.novu_workflow_id || null, })); } catch (error: unknown) { - logger.error('Error fetching notification templates', { - action: 'fetch_notification_templates', - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Get Notification Templates' }); return []; } @@ -382,11 +336,6 @@ class NotificationService { try { const novuEnabled = await this.isNovuEnabled(); if (!novuEnabled) { - logger.warn('Novu not configured, skipping notification', { - action: 'trigger_notification', - workflowId: payload.workflowId, - subscriberId: payload.subscriberId - }); return { success: false, error: 'Novu not configured' }; } @@ -396,31 +345,18 @@ class NotificationService { ); if (error) { - logger.error('Failed to trigger notification', { - action: 'trigger_notification', - workflowId: payload.workflowId, - subscriberId: payload.subscriberId, - requestId, - error: error.message + handleNonCriticalError(error, { + action: 'Trigger Notification', + metadata: { workflowId: payload.workflowId, subscriberId: payload.subscriberId, requestId } }); throw error; } - logger.info('Notification triggered successfully', { - action: 'trigger_notification', - workflowId: payload.workflowId, - subscriberId: payload.subscriberId, - transactionId: data?.transactionId, - requestId - }); - return { success: true }; } catch (error: unknown) { - logger.error('Error triggering notification', { - action: 'trigger_notification', - workflowId: payload.workflowId, - subscriberId: payload.subscriberId, - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Trigger Notification', + metadata: { workflowId: payload.workflowId, subscriberId: payload.subscriberId } }); return { @@ -446,25 +382,16 @@ class NotificationService { ); if (error) { - logger.error('Failed to notify moderators', { - action: 'notify_moderators', - submissionId: payload.submission_id, - requestId, - error: error.message + handleNonCriticalError(error, { + action: 'Notify Moderators (Submission)', + metadata: { submissionId: payload.submission_id, requestId } }); throw error; } - - logger.info('Moderators notified successfully', { - action: 'notify_moderators', - submissionId: payload.submission_id, - requestId - }); } catch (error: unknown) { - logger.error('Error notifying moderators', { - action: 'notify_moderators', - submissionId: payload.submission_id, - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Notify Moderators (Submission)', + metadata: { submissionId: payload.submission_id } }); } } @@ -482,10 +409,6 @@ class NotificationService { try { const novuEnabled = await this.isNovuEnabled(); if (!novuEnabled) { - logger.warn('Novu not configured, skipping system announcement', { - action: 'send_system_announcement', - title: payload.title - }); return { success: false, error: 'Novu not configured' }; } @@ -495,31 +418,21 @@ class NotificationService { ); if (error) { - logger.error('Failed to send system announcement', { - action: 'send_system_announcement', - title: payload.title, - requestId, - error: error.message + handleNonCriticalError(error, { + action: 'Send System Announcement', + metadata: { title: payload.title, requestId } }); throw error; } - logger.info('System announcement sent successfully', { - action: 'send_system_announcement', - title: payload.title, - announcementId: data?.announcementId, - requestId - }); - return { success: true, announcementId: data?.announcementId }; } catch (error: unknown) { - logger.error('Error sending system announcement', { - action: 'send_system_announcement', - title: payload.title, - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Send System Announcement', + metadata: { title: payload.title } }); return { @@ -545,10 +458,6 @@ class NotificationService { try { const novuEnabled = await this.isNovuEnabled(); if (!novuEnabled) { - logger.warn('Novu not configured, skipping report notification', { - action: 'notify_moderators_report', - reportId: payload.reportId - }); return { success: false, error: 'Novu not configured' }; } @@ -558,27 +467,18 @@ class NotificationService { ); if (error) { - logger.error('Failed to notify moderators about report', { - action: 'notify_moderators_report', - reportId: payload.reportId, - requestId, - error: error.message + handleNonCriticalError(error, { + action: 'Notify Moderators (Report)', + metadata: { reportId: payload.reportId, requestId } }); throw error; } - logger.info('Moderators notified about report successfully', { - action: 'notify_moderators_report', - reportId: payload.reportId, - requestId - }); - return { success: true }; } catch (error: unknown) { - logger.error('Error notifying moderators about report', { - action: 'notify_moderators_report', - reportId: payload.reportId, - error: error instanceof Error ? error.message : String(error) + handleNonCriticalError(error, { + action: 'Notify Moderators (Report)', + metadata: { reportId: payload.reportId } }); return { diff --git a/src/lib/requestTracking.ts b/src/lib/requestTracking.ts index 401e8783..1989d68e 100644 --- a/src/lib/requestTracking.ts +++ b/src/lib/requestTracking.ts @@ -168,7 +168,7 @@ async function logRequestMetadata(metadata: RequestMetadata): Promise { }); if (error) { - logger.error('Failed to log metadata to database', { error, context: 'RequestTracking' }); + // Already logged by handleNonCriticalError in requestTracking } } diff --git a/src/lib/slugUtils.ts b/src/lib/slugUtils.ts index cf2577bb..eb84b582 100644 --- a/src/lib/slugUtils.ts +++ b/src/lib/slugUtils.ts @@ -1,5 +1,5 @@ import { supabase } from '@/lib/supabaseClient'; -import { logger } from './logger'; +import { handleError } from './errorHandler'; /** * Generate a URL-safe slug from a name @@ -51,7 +51,10 @@ export async function ensureUniqueSlug( const { data, error } = await query.limit(1); if (error) { - logger.error('Error checking slug uniqueness', { error, tableName }); + handleError(error, { + action: 'Check Slug Uniqueness', + metadata: { tableName, slug } + }); throw error; } diff --git a/src/lib/submissionChangeDetection.ts b/src/lib/submissionChangeDetection.ts index 961dbdab..9159225d 100644 --- a/src/lib/submissionChangeDetection.ts +++ b/src/lib/submissionChangeDetection.ts @@ -6,8 +6,7 @@ import type { RideModelSubmissionData } from '@/types/submission-data'; import { supabase } from '@/lib/supabaseClient'; -import { logger } from './logger'; -import { getErrorMessage } from './errorHandler'; +import { handleNonCriticalError, getErrorMessage } from './errorHandler'; type SubmissionDataTypes = | ParkSubmissionData @@ -81,11 +80,9 @@ async function detectPhotoChanges(submissionId: string): Promise .eq('submission_id', submissionId); if (photoError) { - const errorMessage = getErrorMessage(photoError); - logger.error('Photo submission fetch failed', { - action: 'detect_photo_changes', - submissionId, - error: errorMessage + handleNonCriticalError(photoError, { + action: 'Detect Photo Changes (Fetch Photo Submission)', + metadata: { submissionId } }); } else { const photoSubmission = photoSubmissions?.[0]; @@ -109,11 +106,9 @@ async function detectPhotoChanges(submissionId: string): Promise .in('item_type', ['photo_edit', 'photo_delete']); if (itemsError) { - const errorMessage = getErrorMessage(itemsError); - logger.error('Submission items fetch failed', { - action: 'detect_photo_changes', - submissionId, - error: errorMessage + handleNonCriticalError(itemsError, { + action: 'Detect Photo Changes (Fetch Submission Items)', + metadata: { submissionId } }); } else if (submissionItems && submissionItems.length > 0) { for (const item of submissionItems) { @@ -123,11 +118,9 @@ async function detectPhotoChanges(submissionId: string): Promise } } } catch (err: unknown) { - const errorMessage = getErrorMessage(err); - logger.error('Photo change detection failed', { - action: 'detect_photo_changes', - submissionId, - error: errorMessage + handleNonCriticalError(err, { + action: 'Detect Photo Changes', + metadata: { submissionId } }); } @@ -349,7 +342,10 @@ export async function detectChanges( if (data?.name) entityName = `${data.name} (${formatEntityType(entityType)})`; } } catch (err) { - logger.error('Error fetching entity name for photo operation', { error: err, entityType: itemData.entity_type, entityId: itemData.entity_id }); + handleNonCriticalError(err, { + action: 'Fetch Entity Name for Photo Operation', + metadata: { entityType: itemData.entity_type, entityId: itemData.entity_id } + }); } } @@ -395,7 +391,10 @@ export async function detectChanges( entityName = `${formatEntityType(entityType)} - ${itemData.title}`; } } catch (err) { - logger.error('Error fetching entity name for milestone', { error: err, entityType: itemData.entity_type, entityId: itemData.entity_id }); + handleNonCriticalError(err, { + action: 'Fetch Entity Name for Milestone', + metadata: { entityType: itemData.entity_type, entityId: itemData.entity_id } + }); // Fall back to just the title if database lookup fails if (itemData.title) { entityName = itemData.title; @@ -434,7 +433,10 @@ export async function detectChanges( } } } catch (err) { - logger.error('Error resolving entity name for field display', { error: err, entityType: itemData.entity_type, entityId: itemData.entity_id }); + handleNonCriticalError(err, { + action: 'Resolve Entity Name for Field Display', + metadata: { entityType: itemData.entity_type, entityId: itemData.entity_id } + }); } // Add entity name as an explicit field change at the beginning diff --git a/src/lib/submissionItemsService.ts b/src/lib/submissionItemsService.ts index 2daaaedc..a2e4a867 100644 --- a/src/lib/submissionItemsService.ts +++ b/src/lib/submissionItemsService.ts @@ -1,6 +1,5 @@ import { supabase } from '@/lib/supabaseClient'; -import { getErrorMessage } from './errorHandler'; -import { logger } from './logger'; +import { handleError, handleNonCriticalError, getErrorMessage } from './errorHandler'; import { extractCloudflareImageId } from './cloudflareImageUtils'; // Core submission item interface with dependencies @@ -297,21 +296,19 @@ export async function approveSubmissionItems( dependencyMap.set(item.id, entityId); } catch (error: unknown) { - const errorMsg = getErrorMessage(error); - logger.error('Error approving items', { - action: 'approve_submission_items', - error: errorMsg, + handleError(error, { + action: 'Approve Submission Items', userId, - itemCount: items.length + metadata: { itemCount: items.length, itemType: item.item_type } }); // Update item with error status await updateSubmissionItem(item.id, { status: 'rejected' as const, - rejection_reason: `Failed to create entity: ${errorMsg}`, + rejection_reason: `Failed to create entity: ${getErrorMessage(error)}`, }); - throw new Error(`Failed to approve ${item.item_type}: ${errorMsg}`); + throw new Error(`Failed to approve ${item.item_type}: ${getErrorMessage(error)}`); } } } @@ -338,7 +335,6 @@ async function createVersionForApprovedItem( // - app.current_user_id = original submitter // - app.submission_id = submission ID // Then the trigger creates the version automatically - logger.debug('Version will be created automatically by trigger', { itemType, entityId }); } /** @@ -452,10 +448,9 @@ async function createPark(data: any, dependencyMap: Map, sortedI .eq('id', data.park_id); if (error) { - logger.error('Error updating park', { - action: 'update_park', - parkId: data.park_id, - error: error.message + handleError(error, { + action: 'Update Park', + metadata: { parkId: data.park_id, parkName: resolvedData.name } }); throw new Error(`Database error: ${error.message}`); } @@ -495,10 +490,9 @@ async function createPark(data: any, dependencyMap: Map, sortedI .single(); if (error) { - logger.error('Error creating park', { - action: 'create_park', - parkName: resolvedData.name, - error: error.message + handleError(error, { + action: 'Create Park', + metadata: { parkName: resolvedData.name } }); throw new Error(`Database error: ${error.message}`); } @@ -551,10 +545,9 @@ async function resolveLocationId(locationData: any): Promise { .single(); if (error) { - logger.error('Error creating location', { - action: 'create_location', - locationData, - error: error.message + handleError(error, { + action: 'Create Location', + metadata: { locationData } }); throw new Error(`Failed to create location: ${error.message}`); } @@ -611,7 +604,10 @@ async function createRide(data: any, dependencyMap: Map, sortedI .eq('id', data.ride_id); if (error) { - logger.error('Error updating ride', { error: error.message, rideId: data.ride_id }); + handleError(error, { + action: 'Update Ride', + metadata: { rideId: data.ride_id, rideName: resolvedData.name } + }); throw new Error(`Database error: ${error.message}`); } @@ -642,7 +638,10 @@ async function createRide(data: any, dependencyMap: Map, sortedI .single(); if (error) { - logger.error('Error creating ride', { error: error.message, rideName: resolvedData.name }); + handleError(error, { + action: 'Create Ride', + metadata: { rideName: resolvedData.name } + }); throw new Error(`Database error: ${error.message}`); } @@ -686,7 +685,10 @@ async function createCompany( .eq('id', data.id); if (error) { - logger.error('Error updating company', { error: error.message, companyId: data.id }); + handleError(error, { + action: 'Update Company', + metadata: { companyId: data.id, companyName: resolvedData.name, companyType } + }); throw new Error(`Database error: ${error.message}`); } @@ -721,7 +723,10 @@ async function createCompany( .single(); if (error) { - logger.error('Error creating company', { error: error.message, companyName: resolvedData.name, companyType }); + handleError(error, { + action: 'Create Company', + metadata: { companyName: resolvedData.name, companyType } + }); throw new Error(`Database error: ${error.message}`); } @@ -760,7 +765,10 @@ async function createRideModel(data: any, dependencyMap: Map, so .eq('id', data.ride_model_id); if (error) { - logger.error('Error updating ride model', { error: error.message, rideModelId: data.ride_model_id }); + handleError(error, { + action: 'Update Ride Model', + metadata: { rideModelId: data.ride_model_id, modelName: resolvedData.name } + }); throw new Error(`Database error: ${error.message}`); } @@ -797,7 +805,10 @@ async function createRideModel(data: any, dependencyMap: Map, so .single(); if (error) { - logger.error('Error creating ride model', { error: error.message, modelName: resolvedData.name }); + handleError(error, { + action: 'Create Ride Model', + metadata: { modelName: resolvedData.name } + }); throw new Error(`Database error: ${error.message}`); } @@ -876,7 +887,10 @@ async function approvePhotos(data: any, dependencyMap: Map, user .select(); if (error) { - logger.error('Error inserting photos', { error: error.message, photoCount: photosToInsert.length, entityType, entityId: finalEntityId }); + handleError(error, { + action: 'Insert Photos', + metadata: { photoCount: photosToInsert.length, entityType, entityId: finalEntityId } + }); throw new Error(`Database error: ${error.message}`); } @@ -958,7 +972,10 @@ async function updateEntityFeaturedImage( } } } catch (error) { - logger.error('Error updating entity featured image', { error, entityType, entityId }); + handleNonCriticalError(error, { + action: 'Update Entity Featured Image', + metadata: { entityType, entityId } + }); } } @@ -1136,10 +1153,13 @@ export async function rejectSubmissionItems( }) .eq('id', itemId); - if (error) { - logger.error('Error rejecting item', { error, itemId }); - throw error; - } + if (error) { + handleNonCriticalError(error, { + action: 'Reject Submission Item', + metadata: { itemId } + }); + throw error; + } }); await Promise.all(updates); @@ -1171,7 +1191,10 @@ async function updateSubmissionStatusAfterRejection(submissionId: string): Promi .eq('submission_id', submissionId); if (fetchError) { - logger.error('Error fetching submission items', { error: fetchError, submissionId }); + handleNonCriticalError(fetchError, { + action: 'Fetch Submission Items for Status Update', + metadata: { submissionId } + }); return; } @@ -1202,7 +1225,10 @@ async function updateSubmissionStatusAfterRejection(submissionId: string): Promi .eq('id', submissionId); if (updateError) { - logger.error('Error updating submission status', { error: updateError, submissionId }); + handleNonCriticalError(updateError, { + action: 'Update Submission Status After Rejection', + metadata: { submissionId, newStatus } + }); } } @@ -1273,10 +1299,9 @@ export async function editSubmissionItem( }); if (historyError) { - logger.error('Failed to record edit history', { - itemId, - editorId: userId, - error: historyError.message, + handleNonCriticalError(historyError, { + action: 'Record Edit History', + metadata: { itemId, editorId: userId } }); // Don't fail the whole operation if history tracking fails } @@ -1293,10 +1318,12 @@ export async function editSubmissionItem( true // isEdit = true ); } catch (versionError) { - logger.error('Failed to create version for manual edit', { - action: 'create_version_for_edit', - itemType: currentItem.item_type, - entityId: currentItem.approved_entity_id + handleNonCriticalError(versionError, { + action: 'Create Version for Manual Edit', + metadata: { + itemType: currentItem.item_type, + entityId: currentItem.approved_entity_id + } }); // Don't fail the entire operation, just log the error // The edit itself is still saved, just without version history @@ -1390,7 +1417,10 @@ export async function escalateSubmission( } }); } catch (auditError) { - logger.error('Failed to log escalation audit', { error: auditError }); + handleNonCriticalError(auditError, { + action: 'Log Escalation Audit', + metadata: { submissionId } + }); } } } @@ -1421,9 +1451,9 @@ export async function fetchEditHistory(itemId: string) { return data || []; } catch (error: unknown) { - logger.error('Error fetching edit history', { - itemId, - error: getErrorMessage(error), + handleNonCriticalError(error, { + action: 'Fetch Edit History', + metadata: { itemId } }); return []; } @@ -1476,9 +1506,9 @@ export async function checkSubmissionConflict( }, }; } catch (error: unknown) { - logger.error('Error checking submission conflict', { - submissionId, - error: getErrorMessage(error), + handleNonCriticalError(error, { + action: 'Check Submission Conflict', + metadata: { submissionId } }); throw error; } @@ -1526,9 +1556,9 @@ export async function fetchSubmissionVersions( return data || []; } catch (error: unknown) { - logger.error('Error fetching submission versions', { - submissionId, - error: getErrorMessage(error), + handleNonCriticalError(error, { + action: 'Fetch Submission Versions', + metadata: { submissionId } }); return []; }