From e2c9bef8f41e9ff0610bb220998f3fa036a63fb8 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 19 Dec 2025 14:28:40 +0100 Subject: [PATCH] fix: improve server action resilience and error logging - Moved Supabase client initialization inside try-catch in Server Actions - Added safety checks for null Supabase client in magicScan - Added detailed server-side logging for debugging deployment issues - Ensures all failure paths return a structured error response instead of 500 --- src/services/analyze-bottle-nebius.ts | 26 ++++++++++++-- src/services/analyze-bottle.ts | 27 ++++++++------ src/services/magic-scan.ts | 6 ++++ src/services/tags.ts | 52 ++++++++++++++++----------- 4 files changed, 77 insertions(+), 34 deletions(-) diff --git a/src/services/analyze-bottle-nebius.ts b/src/services/analyze-bottle-nebius.ts index 454e68e..d73304b 100644 --- a/src/services/analyze-bottle-nebius.ts +++ b/src/services/analyze-bottle-nebius.ts @@ -10,13 +10,14 @@ import { trackApiUsage } from './track-api-usage'; import { checkCreditBalance, deductCredits } from './credit-service'; export async function analyzeBottleNebius(base64Image: string, tags?: string[], locale: string = 'de'): Promise { - const supabase = createServerActionClient({ cookies }); - if (!process.env.NEBIUS_API_KEY) { return { success: false, error: 'NEBIUS_API_KEY is not configured.' }; } + let supabase; try { + supabase = createServerActionClient({ cookies }); + console.log('[analyzeBottleNebius] Initialized Supabase client'); const { data: { session } } = await supabase.auth.getSession(); if (!session || !session.user) { return { success: false, error: 'Nicht autorisiert oder Session abgelaufen.' }; @@ -52,6 +53,8 @@ export async function analyzeBottleNebius(base64Image: string, tags?: string[], .replace('{AVAILABLE_TAGS}', tags ? tags.join(', ') : 'No tags available') .replace('{LANGUAGE}', locale === 'en' ? 'English' : 'German') + "\nAdditionally, generate a 'search_string' field for Whiskybase in this format: 'site:whiskybase.com [Distillery] [Name] [Vintage]'. Include this field in the JSON object."; + console.log(`[analyzeBottleNebius] Instruction prepared for AI: ${instruction.substring(0, 100)}...`); + const response = await aiClient.chat.completions.create({ model: "Qwen/Qwen2.5-VL-72B-Instruct", @@ -116,6 +119,25 @@ export async function analyzeBottleNebius(base64Image: string, tags?: string[], } catch (error) { console.error('Nebius Analysis Error:', error); + + // Track failed API call + try { + if (supabase) { + const { data: { session } } = await supabase.auth.getSession(); + if (session?.user) { + await trackApiUsage({ + userId: session.user.id, + apiType: 'gemini_ai', + endpoint: 'nebius/qwen2.5-vl', + success: false, + errorMessage: error instanceof Error ? error.message : 'Unknown error' + }); + } + } + } catch (trackError) { + console.error('Failed to track error:', trackError); + } + return { success: false, error: error instanceof Error ? error.message : 'Nebius AI analysis failed.', diff --git a/src/services/analyze-bottle.ts b/src/services/analyze-bottle.ts index fe298fa..36c21b6 100644 --- a/src/services/analyze-bottle.ts +++ b/src/services/analyze-bottle.ts @@ -9,13 +9,16 @@ import { trackApiUsage } from './track-api-usage'; import { checkCreditBalance, deductCredits } from './credit-service'; export async function analyzeBottle(base64Image: string, tags?: string[], locale: string = 'de'): Promise { - const supabase = createServerActionClient({ cookies }); - if (!process.env.GEMINI_API_KEY) { return { success: false, error: 'GEMINI_API_KEY is not configured.' }; } + let supabase; // Declare supabase outside try block for error tracking access try { + // Initialize Supabase client inside the try block + supabase = createServerActionClient({ cookies }); + console.log('[analyzeBottle] Initialized Supabase client'); + // ... (auth and credit check remain same) ... const { data: { session } } = await supabase.auth.getSession(); if (!session || !session.user) { @@ -110,15 +113,17 @@ export async function analyzeBottle(base64Image: string, tags?: string[], locale // Track failed API call try { - const { data: { session } } = await supabase.auth.getSession(); - if (session?.user) { - await trackApiUsage({ - userId: session.user.id, - apiType: 'gemini_ai', - endpoint: 'generateContent', - success: false, - errorMessage: error instanceof Error ? error.message : 'Unknown error' - }); + if (supabase) { + const { data: { session } } = await supabase.auth.getSession(); + if (session?.user) { + await trackApiUsage({ + userId: session.user.id, + apiType: 'gemini_ai', + endpoint: 'generateContent', + success: false, + errorMessage: error instanceof Error ? error.message : 'Unknown error' + }); + } } } catch (trackError) { console.error('Failed to track error:', trackError); diff --git a/src/services/magic-scan.ts b/src/services/magic-scan.ts index 9997837..cb79b72 100644 --- a/src/services/magic-scan.ts +++ b/src/services/magic-scan.ts @@ -10,9 +10,15 @@ import { AnalysisResponse, BottleMetadata } from '@/types/whisky'; export async function magicScan(base64Image: string, provider: 'gemini' | 'nebius' = 'gemini', locale: string = 'de'): Promise { try { + console.log('[magicScan] Starting scan process...'); + if (!supabase) { + throw new Error('Supabase client is not initialized. Check environment variables.'); + } + // 0. Fetch available tags for constrained generation const systemTags = await getAllSystemTags(); const tagNames = systemTags.map(t => t.name); + console.log(`[magicScan] Fetched ${tagNames.length} system tags`); // 1. AI Analysis let aiResponse: any; diff --git a/src/services/tags.ts b/src/services/tags.ts index 9a74f90..0ad8564 100644 --- a/src/services/tags.ts +++ b/src/services/tags.ts @@ -18,41 +18,51 @@ export interface Tag { * Fetch tags by category */ export async function getTagsByCategory(category: TagCategory): Promise { - const supabase = createServerActionClient({ cookies }); + try { + const supabase = createServerActionClient({ cookies }); - const { data, error } = await supabase - .from('tags') - .select('*') - .eq('category', category) - .order('popularity_score', { ascending: false }) - .order('name'); + const { data, error } = await supabase + .from('tags') + .select('*') + .eq('category', category) + .order('popularity_score', { ascending: false }) + .order('name'); - if (error) { - console.error(`Error fetching tags for ${category}:`, error); + if (error) { + console.error(`Error fetching tags for ${category}:`, error); + return []; + } + + return data || []; + } catch (err) { + console.error(`Exception in getTagsByCategory for ${category}:`, err); return []; } - - return data || []; } /** * Fetch all system default tags */ export async function getAllSystemTags(): Promise { - const supabase = createServerActionClient({ cookies }); + try { + const supabase = createServerActionClient({ cookies }); - const { data, error } = await supabase - .from('tags') - .select('*') - .eq('is_system_default', true) - .order('name'); + const { data, error } = await supabase + .from('tags') + .select('*') + .eq('is_system_default', true) + .order('name'); - if (error) { - console.error('Error fetching all system tags:', error); + if (error) { + console.error('Error fetching all system tags:', error); + return []; + } + + return data || []; + } catch (err) { + console.error('Exception in getAllSystemTags:', err); return []; } - - return data || []; } /**