From 97e6adefadf630695848333dc30baaa8e0cdee2d Mon Sep 17 00:00:00 2001 From: nadja Date: Wed, 26 Aug 2026 16:17:54 +0200 Subject: [PATCH] fix Analyse --- backend/analytics-db.js | 7 ++++++- backend/db.js | 7 ++++++- backend/routes/recipes.js | 14 +++++++++++++ backend/services/ai.js | 1 + frontend/src/api.js | 36 +++++++++++++++----------------- frontend/src/pages/AddRecipe.jsx | 8 ++++++- frontend/src/pages/Analytics.jsx | 4 +++- 7 files changed, 54 insertions(+), 23 deletions(-) diff --git a/backend/analytics-db.js b/backend/analytics-db.js index d8a36df..a22a3a9 100644 --- a/backend/analytics-db.js +++ b/backend/analytics-db.js @@ -7,7 +7,12 @@ const DB_FILE = process.env.ANALYTICS_PATH || join(__dirname, 'analytics.json'); function loadDB() { if (existsSync(DB_FILE)) { - return JSON.parse(readFileSync(DB_FILE, 'utf-8')); + try { + return JSON.parse(readFileSync(DB_FILE, 'utf-8')); + } catch (e) { + console.error('Analytics Datei korrupt, Backup erstellt:', e.message); + try { writeFileSync(DB_FILE + '.bak', readFileSync(DB_FILE, 'utf-8')); } catch {} + } } return { events: [], _counter: 0 }; } diff --git a/backend/db.js b/backend/db.js index 31d0c33..d348008 100644 --- a/backend/db.js +++ b/backend/db.js @@ -7,7 +7,12 @@ const DB_FILE = process.env.DB_PATH || join(__dirname, 'data.json'); function loadDB() { if (existsSync(DB_FILE)) { - return JSON.parse(readFileSync(DB_FILE, 'utf-8')); + try { + return JSON.parse(readFileSync(DB_FILE, 'utf-8')); + } catch (e) { + console.error('DB Datei korrupt, Backup erstellt:', e.message); + try { writeFileSync(DB_FILE + '.bak', readFileSync(DB_FILE, 'utf-8')); } catch {} + } } return { users: [], recipes: [], invite_tokens: [], _counters: { users: 0, recipes: 0, invite_tokens: 0 } }; } diff --git a/backend/routes/recipes.js b/backend/routes/recipes.js index 3b82db5..23c84a6 100644 --- a/backend/routes/recipes.js +++ b/backend/routes/recipes.js @@ -83,6 +83,7 @@ router.post('/from-tiktok', async (req, res) => { let extracted = null; let aiStatus = ollamaAvailable ? 'available' : 'unavailable'; + let aiMessage = ''; if (ollamaAvailable && tiktokData.title) { try { extracted = await extractRecipe(tiktokData.title); @@ -90,13 +91,19 @@ router.post('/from-tiktok', async (req, res) => { } catch (e) { console.error('Ollama Extraktion fehlgeschlagen:', e.message); aiStatus = 'error'; + aiMessage = e.message; } + } else if (!ollamaAvailable) { + aiMessage = 'Ollama-Server nicht erreichbar'; + } else if (!tiktokData.title) { + aiMessage = 'Kein Titel im Video gefunden'; } res.json({ tiktok: tiktokData, ollama: ollamaAvailable, aiStatus, + aiMessage, extracted, }); } catch (error) { @@ -115,6 +122,7 @@ router.post('/from-instagram', async (req, res) => { let extracted = null; let aiStatus = ollamaAvailable ? 'available' : 'unavailable'; + let aiMessage = ''; if (ollamaAvailable && instagramData.title) { try { extracted = await extractRecipe(instagramData.title); @@ -122,13 +130,19 @@ router.post('/from-instagram', async (req, res) => { } catch (e) { console.error('Ollama Extraktion fehlgeschlagen:', e.message); aiStatus = 'error'; + aiMessage = e.message; } + } else if (!ollamaAvailable) { + aiMessage = 'Ollama-Server nicht erreichbar'; + } else if (!instagramData.title) { + aiMessage = 'Kein Titel im Video gefunden'; } res.json({ instagram: instagramData, ollama: ollamaAvailable, aiStatus, + aiMessage, extracted, }); } catch (error) { diff --git a/backend/services/ai.js b/backend/services/ai.js index 0116960..95695fb 100644 --- a/backend/services/ai.js +++ b/backend/services/ai.js @@ -22,6 +22,7 @@ export async function extractRecipe(description) { prompt: `Extrahiere ein Rezept aus dieser TikTok-Videobeschreibung:\n\n${description}`, stream: false, }), + signal: AbortSignal.timeout(120000), }); if (!response.ok) { diff --git a/frontend/src/api.js b/frontend/src/api.js index e244311..0356a5c 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -72,42 +72,40 @@ export async function checkAiStatus() { return apiFetch(`${API_BASE}/ai-status`); } -export async function fetchAnalyticsOverview(token) { - const res = await fetch('/api/analytics/overview', { +async function analyticsFetch(url, token) { + const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` }, }); - return res.json(); + const text = await res.text(); + if (!res.ok) { + let msg = `Server-Fehler (${res.status})`; + try { msg = JSON.parse(text).error || msg; } catch {} + throw new Error(msg); + } + return JSON.parse(text); +} + +export async function fetchAnalyticsOverview(token) { + return analyticsFetch('/api/analytics/overview', token); } export async function fetchAnalyticsPageviews(token, from, to) { const params = new URLSearchParams(); if (from) params.set('from', from); if (to) params.set('to', to); - const res = await fetch(`/api/analytics/pageviews?${params}`, { - headers: { Authorization: `Bearer ${token}` }, - }); - return res.json(); + return analyticsFetch(`/api/analytics/pageviews?${params}`, token); } export async function fetchAnalyticsUsers(token) { - const res = await fetch('/api/analytics/users', { - headers: { Authorization: `Bearer ${token}` }, - }); - return res.json(); + return analyticsFetch('/api/analytics/users', token); } export async function fetchAnalyticsRecipes(token) { - const res = await fetch('/api/analytics/recipes', { - headers: { Authorization: `Bearer ${token}` }, - }); - return res.json(); + return analyticsFetch('/api/analytics/recipes', token); } export async function fetchAnalyticsActivity(token, userId) { const params = new URLSearchParams(); if (userId) params.set('user_id', userId); - const res = await fetch(`/api/analytics/activity?${params}`, { - headers: { Authorization: `Bearer ${token}` }, - }); - return res.json(); + return analyticsFetch(`/api/analytics/activity?${params}`, token); } diff --git a/frontend/src/pages/AddRecipe.jsx b/frontend/src/pages/AddRecipe.jsx index e191e85..b7fc067 100644 --- a/frontend/src/pages/AddRecipe.jsx +++ b/frontend/src/pages/AddRecipe.jsx @@ -31,6 +31,7 @@ export default function AddRecipe() { const [source, setSource] = useState(''); const [hasData, setHasData] = useState(false); const [aiStatus, setAiStatus] = useState(null); + const [aiMessage, setAiMessage] = useState(''); const handleExtract = async () => { if (!url.trim()) return; @@ -43,6 +44,7 @@ export default function AddRecipe() { setLoading(true); setError(''); setAiStatus(null); + setAiMessage(''); try { let data; if (detected === 'instagram') { @@ -69,6 +71,7 @@ export default function AddRecipe() { setName(detected === 'instagram' ? (data.instagram?.title || '') : (data.tiktok?.title || '')); } setAiStatus(data.aiStatus || (data.extracted ? 'success' : 'unavailable')); + setAiMessage(data.aiMessage || ''); setHasData(true); } catch (e) { setError('Fehler beim Abrufen der Video-Daten. Überprüfe die URL.'); @@ -152,7 +155,10 @@ export default function AddRecipe() { - KI konnte nicht extrahieren — füll die Felder manuell aus. + + KI-Extraktion fehlgeschlagen{aiMessage && : {aiMessage}} + — füll die Felder manuell aus. + )} {aiStatus === 'unavailable' && ( diff --git a/frontend/src/pages/Analytics.jsx b/frontend/src/pages/Analytics.jsx index bf70523..5cb1572 100644 --- a/frontend/src/pages/Analytics.jsx +++ b/frontend/src/pages/Analytics.jsx @@ -68,6 +68,7 @@ export default function Analytics() { const [users, setUsers] = useState([]); const [activity, setActivity] = useState([]); const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); useEffect(() => { load(); @@ -88,12 +89,13 @@ export default function Analytics() { setActivity(act); } catch (e) { console.error('Analytics Fehler:', e); + setError(e.message); } setLoading(false); }; if (loading) return
Laden...
; - if (!overview) return
Fehler beim Laden der Analytics-Daten.
; + if (!overview) return
Fehler beim Laden der Analytics-Daten. {error && {error}}
; return (