// Netlify serverless function — proxies photo analysis to Anthropic Vision export default async (req, context) => { if (req.method !== 'POST') { return new Response(JSON.stringify({ error: 'Method not allowed' }), { status: 405, headers: { 'Content-Type': 'application/json' } }); } const apiKey = Netlify.env.get('ANTHROPIC_API_KEY'); if (!apiKey) { return new Response(JSON.stringify({ error: 'API key not configured on server' }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } let body; try { body = await req.json(); } catch { return new Response(JSON.stringify({ error: 'Invalid JSON' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } const imageData = body.imageData; const mediaType = body.mediaType || 'image/jpeg'; if (!imageData) { return new Response(JSON.stringify({ error: 'Missing imageData' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } // Cap at ~5MB base64 to prevent abuse if (imageData.length > 7_000_000) { return new Response(JSON.stringify({ error: 'Image too large' }), { status: 413, headers: { 'Content-Type': 'application/json' } }); } try { const upstream = await fetch('https://api.anthropic.com/v1/messages', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' }, body: JSON.stringify({ model: 'claude-sonnet-4-20250514', max_tokens: 400, system: 'You are an expert nutritionist. Look at this food photo. Identify all visible items, estimate realistic portion sizes in grams or ml, and calculate total macros. Return ONLY valid JSON with no markdown: {"name":"brief meal name","qty":number,"unit":"g or ml","calories":number,"protein":number,"carbs":number,"fat":number,"breakdown":"comma-separated list with estimated weights e.g. chicken 180g, rice 120g"}. qty is the total estimated weight of everything on the plate. Macros in grams, calories as kcal integers.', messages: [{ role: 'user', content: [ { type: 'image', source: { type: 'base64', media_type: mediaType, data: imageData } }, { type: 'text', text: 'What is on this plate? Estimate total calories and macros.' } ] }] }) }); const data = await upstream.json(); return new Response(JSON.stringify(data), { status: upstream.status, headers: { 'Content-Type': 'application/json' } }); } catch (e) { return new Response(JSON.stringify({ error: 'Upstream request failed' }), { status: 502, headers: { 'Content-Type': 'application/json' } }); } }; export const config = { path: '/api/photo' };