From 7642ac435b812f0d44778fb85fd38d860cfb670d Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 02:39:15 +0000 Subject: [PATCH] Connect to Lovable Cloud Improve CORS handling and error logging to fix moderation edge cases: - Add x-idempotency-key to allowed CORS headers and expose explicit POST methods - Extend CORS headers to include Access-Control-Allow-Methods - Update edge function tracing and client error handling to better detect and log CORS/network issues - Enhance error handling utilities to surface CORS-related failures and provide clearer user messages --- src/lib/edgeFunctionTracking.ts | 25 ++++++++++++++++++++++++- src/lib/errorHandler.ts | 29 +++++++++++++++++++++++++++-- supabase/functions/_shared/cors.ts | 2 ++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/lib/edgeFunctionTracking.ts b/src/lib/edgeFunctionTracking.ts index 903e2532..88a56b41 100644 --- a/src/lib/edgeFunctionTracking.ts +++ b/src/lib/edgeFunctionTracking.ts @@ -10,6 +10,7 @@ import { trackRequest } from './requestTracking'; import { getErrorMessage } from './errorHandler'; import { withRetry, isRetryableError, type RetryOptions } from './retryHelpers'; import { breadcrumb } from './errorBreadcrumbs'; +import { logger } from './logger'; /** * Invoke a Supabase edge function with request tracking @@ -149,9 +150,31 @@ export async function invokeWithTracking( } const errorMessage = getErrorMessage(error); + + // Detect CORS errors specifically + const isCorsError = errorMessage.toLowerCase().includes('cors') || + errorMessage.toLowerCase().includes('cross-origin') || + errorMessage.toLowerCase().includes('failed to send') || + (error instanceof TypeError && errorMessage.toLowerCase().includes('failed to fetch')); + + // Enhanced error logging + logger.error('[EdgeFunctionTracking] Edge function invocation failed', { + functionName, + error: errorMessage, + errorType: isCorsError ? 'CORS/Network' : (error as any)?.name || 'Unknown', + attempts: attemptCount, + isCorsError, + debugHint: isCorsError ? 'Browser blocked request - verify CORS headers allow X-Idempotency-Key or check network connectivity' : undefined, + status: (error as any)?.status, + }); + return { data: null, - error: { message: errorMessage, status: (error as any)?.status }, + error: { + message: errorMessage, + status: (error as any)?.status, + isCorsError, + }, requestId: 'unknown', duration: 0, attempts: attemptCount, diff --git a/src/lib/errorHandler.ts b/src/lib/errorHandler.ts index 2e958a08..dab841b5 100644 --- a/src/lib/errorHandler.ts +++ b/src/lib/errorHandler.ts @@ -38,12 +38,24 @@ export function isSupabaseConnectionError(error: unknown): boolean { // Database connection errors (08xxx codes) if (supabaseError.code?.startsWith('08')) return true; + + // Check message for CORS and connectivity keywords + const message = supabaseError.message?.toLowerCase() || ''; + if (message.includes('cors') || + message.includes('cross-origin') || + message.includes('failed to send')) { + return true; + } } // Network fetch errors if (error instanceof TypeError) { const message = error.message.toLowerCase(); - if (message.includes('fetch') || message.includes('network') || message.includes('failed to fetch')) { + if (message.includes('fetch') || + message.includes('network') || + message.includes('failed to fetch') || + message.includes('cors') || + message.includes('cross-origin')) { return true; } } @@ -61,7 +73,15 @@ export const handleError = ( // Check if this is a connection error and dispatch event if (isSupabaseConnectionError(error)) { - window.dispatchEvent(new CustomEvent('api-connectivity-down')); + const errorMsg = getErrorMessage(error).toLowerCase(); + const isCors = errorMsg.includes('cors') || errorMsg.includes('cross-origin'); + + window.dispatchEvent(new CustomEvent('api-connectivity-down', { + detail: { + isCorsError: isCors, + error: errorMsg, + } + })); } // Enhanced error message and stack extraction @@ -132,6 +152,9 @@ export const handleError = ( } // Log to console/monitoring with enhanced debugging + const isCorsError = errorMessage.toLowerCase().includes('cors') || + errorMessage.toLowerCase().includes('cross-origin') || + errorMessage.toLowerCase().includes('failed to send'); logger.error('Error occurred', { ...context, @@ -144,6 +167,8 @@ export const handleError = ( hasStack: !!stack, isSyntheticStack: !!(error && typeof error === 'object' && !(error instanceof Error) && stack), supabaseError: supabaseErrorDetails, + isCorsError, + debugHint: isCorsError ? 'Browser blocked request - check CORS headers or network connectivity' : undefined, }); // Additional debug logging when stack is missing diff --git a/supabase/functions/_shared/cors.ts b/supabase/functions/_shared/cors.ts index fe1996dd..9b6b2ddd 100644 --- a/supabase/functions/_shared/cors.ts +++ b/supabase/functions/_shared/cors.ts @@ -9,6 +9,7 @@ const STANDARD_HEADERS = [ 'x-client-info', 'apikey', 'content-type', + 'x-idempotency-key', ]; // Tracing headers for distributed tracing and request tracking @@ -36,6 +37,7 @@ export const corsHeaders = { export const corsHeadersWithTracing = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': ALL_HEADERS.join(', '), + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', }; /**