'use server'; import { trackApiUsage, checkDailyLimit } from './track-api-usage'; import { checkCreditBalance, deductCredits } from './credit-service'; import { createClient } from '@/lib/supabase/server'; import { DiscoveryDataSchema, DiscoveryData } from '@/types/whisky'; /** * Service to discover a Whiskybase ID for a given bottle. * Uses Google Custom Search JSON API to search Google and extracts the ID from the first result. */ export async function discoverWhiskybaseId(rawBottle: DiscoveryData) { // Validate input const bottle = DiscoveryDataSchema.parse(rawBottle); // Both Gemini and Custom Search often use the same API key if created via AI Studio const apiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY; const cx = '37e905eb03fd14e0f'; // Provided by user if (!apiKey) { return { success: false, error: 'GEMINI_API_KEY oder GOOGLE_API_KEY ist nicht konfiguriert.' }; } // Get current user for tracking const supabase = await createClient(); const { data: { user } } = await supabase.auth.getUser(); if (!user) { return { success: false, error: 'Benutzer nicht authentifiziert.' }; } // Check daily limit before making API call const limitCheck = await checkDailyLimit('google_search'); if (!limitCheck.allowed) { return { success: false, error: `Tageslimit für Whiskybase-Suchen erreicht (${limitCheck.limit} pro Tag). Versuche es morgen erneut.`, limitReached: true }; } // Check credit balance before making API call const creditCheck = await checkCreditBalance(user.id, 'google_search'); if (!creditCheck.allowed) { return { success: false, error: `Nicht genügend Credits. Du benötigst ${creditCheck.cost} Credits, hast aber nur ${creditCheck.balance}.`, insufficientCredits: true }; } try { // Construct targeted search query const queryParts = [ bottle.distillery ? `${bottle.distillery}` : '', // Removed quotes for more fuzzy matching bottle.name ? `${bottle.name}` : '', bottle.abv ? `${bottle.abv}%` : '', bottle.age ? `${bottle.age} year old` : '', bottle.batch_info ? `"${bottle.batch_info}"` : '', bottle.distilled_at ? `distilled ${bottle.distilled_at}` : '', bottle.bottled_at ? `bottled ${bottle.bottled_at}` : '' ].filter(Boolean); const q = queryParts.join(' '); console.log('Whiskybase Search Query:', q); const url = `https://www.googleapis.com/customsearch/v1?key=${apiKey}&cx=${cx}&q=${encodeURIComponent(q)}`; const response = await fetch(url); const data = await response.json(); if (data.error) { console.error('Google API Error Response:', data.error); // Track failed API call await trackApiUsage({ userId: user.id, apiType: 'google_search', endpoint: 'customsearch/v1', success: false, errorMessage: data.error.message }); throw new Error(data.error.message || 'Google API Error'); } // Track successful API call await trackApiUsage({ userId: user.id, apiType: 'google_search', endpoint: 'customsearch/v1', success: true }); // Deduct credits after successful API call const creditDeduction = await deductCredits(user.id, 'google_search', 'Whiskybase search'); if (!creditDeduction.success) { console.error('Failed to deduct credits:', creditDeduction.error); // Don't fail the search if credit deduction fails } if (!data.items || data.items.length === 0) { return { success: false, error: `Keine Treffer auf Whiskybase gefunden. (Query: ${q})` }; } // Try to find the first result that looks like a valid product page // Pattern matches: https://www.whiskybase.com/whiskies/whisky/12345/name const wbRegex = /\/whisky\/(\d+)\//; for (const item of data.items) { const link = item.link; const match = link.match(wbRegex); if (match && match[1]) { return { success: true, id: match[1], url: link, title: item.title }; } } return { success: false, error: 'Konnte keine gültige Whiskybase-ID im Suchergebnis finden.' }; } catch (error) { console.error('Whiskybase Discovery Error:', error); // Track failed attempt (if not already tracked) if (user) { await trackApiUsage({ userId: user.id, apiType: 'google_search', endpoint: 'customsearch/v1', success: false, errorMessage: error instanceof Error ? error.message : 'Unknown error' }); } return { success: false, error: error instanceof Error ? error.message : 'Fehler bei der Suche auf Whiskybase.' }; } }