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
This commit is contained in:
2025-12-19 14:28:40 +01:00
parent cbb28b389c
commit e2c9bef8f4
4 changed files with 77 additions and 34 deletions

View File

@@ -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<AnalysisResponse & { search_string?: string }> {
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.',

View File

@@ -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<AnalysisResponse> {
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);

View File

@@ -10,9 +10,15 @@ import { AnalysisResponse, BottleMetadata } from '@/types/whisky';
export async function magicScan(base64Image: string, provider: 'gemini' | 'nebius' = 'gemini', locale: string = 'de'): Promise<AnalysisResponse & { wb_id?: string }> {
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;

View File

@@ -18,41 +18,51 @@ export interface Tag {
* Fetch tags by category
*/
export async function getTagsByCategory(category: TagCategory): Promise<Tag[]> {
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<Tag[]> {
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 || [];
}
/**