Introcuced a Free LLM for easier food input
KalorienTracker
Ein selbst gehosteter Kalorien-Tracker als Webapp: Barcode scannen, Nährwerte automatisch von Open Food Facts ziehen, bei unbekannten Produkten manuell nacherfassen, Tagesübersicht mit Kalorien/Makros.
Architektur
KalorienTracker/
├── backend/ FastAPI + SQLModel (SQLite), JWT-Auth
├── frontend/ React (Vite) + Tailwind, mobile-first
└── docker-compose.yml
- Backend (
backend/): FastAPI-API mit JWT-Login, Produktsuche (lokale DB → Fallback auf Open Food Facts, Ergebnis wird lokal gecacht), Logging von Mahlzeiten inkl. Tagesübersicht, Historie (Tage/Wochen/Monate) und selbst gesetzten Tageszielen. Optional eine KI-Nährwertschätzung aus Freitext über ein OpenAI-kompatibles LLM-Gateway. Mehrbenutzerfähig: jeder registrierte Nutzer sieht nur seine eigenen Tagebuch-Einträge, Ziele und privaten Rezepte, während alle Lebensmittel (Barcode-Produkte) in einer gemeinsamen Datenbank für alle Nutzer verfügbar sind. Jeder Endpunkt außer Registrierung/Login verlangt ein gültiges JWT. - Frontend (
frontend/): React-SPA mit Kamera-Barcode-Scanner (html5-qrcode), Dashboard mit Fortschrittsbalken für Kalorien/Protein/Carbs/Fett, Formular für manuell angelegte Produkte, Historie-Seite mit Trend-Grafiken (Recharts), Kalenderwochen- und Monatsdurchschnitt, Settings-Seite für eigene Tagesziele, umschaltbarer Dark Mode (folgt standardmäßig der Systemeinstellung, per Klick auf das Sonne/Mond-Icon umschaltbar, Wahl bleibt gespeichert).
Setup
Variante 1: Docker (empfohlen)
Voraussetzung: Docker + Docker Compose.
-
Backend-Konfiguration anlegen:
cp backend/.env.example backend/.envTrage in
backend/.enveinen eigenen, zufälligenSECRET_KEYein (z. B.python3 -c "import secrets; print(secrets.token_hex(32))"). -
Bauen und starten:
docker compose up --build -d -
Öffnen: http://localhost:8080
Die App läuft komplett hinter einem nginx-Reverse-Proxy: das Frontend wird als statisches Build ausgeliefert, /api/*-Requests werden intern an das Backend weitergeleitet. Es ist also nur ein Port (8080) nach außen nötig — kein CORS-Setup notwendig, auch nicht vom Handy im selben Netzwerk.
Die SQLite-Datenbank wird persistent unter ./data/kalorientracker.db auf dem Host gespeichert (Docker-Volume), bleibt also auch nach docker compose down erhalten.
Container stoppen: docker compose down.
Wichtiger Hinweis: Kamera-Zugriff & HTTPS
Browser erlauben Kamera-Zugriff (getUserMedia, nötig für den Barcode-Scanner) nur in einem "secure context" — also https:// oder localhost. Ruft man die App über http://<lan-ip>:8080 vom Handy aus auf, wird der Browser die Kamera blockieren. Für produktiven Einsatz übers Heimnetz braucht es daher zusätzlich HTTPS, z. B. über:
- einen Reverse Proxy mit automatischem HTTPS (z. B. Caddy) vor dem
frontend-Container, - ein selbstsigniertes Zertifikat (z. B. mit mkcert) im Heimnetz, oder
- einen Tunnel-Dienst (z. B. Tailscale Funnel, Cloudflare Tunnel), der HTTPS terminiert.
Ohne HTTPS funktioniert die App abgesehen vom Scannen ganz normal (manuelles Anlegen per Formular geht immer).
Variante 2: Lokale Entwicklung ohne Docker
Backend:
cd backend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # SECRET_KEY setzen, siehe oben
uvicorn app.main:app --reload --port 8000
API läuft auf http://localhost:8000, Swagger-UI unter http://localhost:8000/docs.
Frontend:
cd frontend
npm install
npm run dev -- --port 5173
Läuft auf http://localhost:5173 und spricht per CORS direkt mit dem Backend auf Port 8000 (siehe frontend/.env, VITE_API_URL).
Nutzung
- Registrieren / einloggen.
- Auf „Barcode scannen“ tippen, Kamera auf den Barcode halten. Falls die Live-Erkennung hakt (z. B. schlechtes Licht oder Fokus), per „Stattdessen Foto aufnehmen“ ein einzelnes scharfes Foto aufnehmen — das Bild wird einmalig ausgewertet und danach verworfen, nie gespeichert oder hochgeladen.
- Produkt gefunden → Mahlzeit (Frühstück/Mittagessen/Abendessen/Snacks, per Uhrzeit vorausgewählt) + Menge in Gramm eingeben → „Hinzufügen“. Angezeigt werden Kalorien, Carbs, Protein, Fett sowie Zucker, Ballaststoffe, gesättigte Fettsäuren und Salz pro 100g.
- Produkt nicht gefunden (404) → Name + Nährwerte pro 100g manuell eintragen (die vier Zusatz-Nährwerte sind optional) → wird angelegt und direkt geloggt. Alternativ oben im Formular kurz beschreiben, was es war (z. B. „Currywurst mit Pommes“) und auf „Schätzen“ tippen — dann füllt eine KI alle Nährwerte und die übliche Portionsgröße als Vorschlag aus, den man vor dem Speichern prüfen und korrigieren kann.
- Alternativ „Mahlzeit hinzufügen“ tippen, um ohne Scan nach einem Produkt zu suchen (z. B. „Döner“ vom Essen gehen) oder ein neues anzulegen. Die Suche schlägt zuerst die zuletzt selbst verwendeten Produkte vor (inkl. der zuletzt genutzten Menge) — auch wenn sie ursprünglich per Barcode gescannt wurden.
- Dashboard zeigt die Tagessumme aus Kalorien, Protein, Carbs und Fett als Fortschrittsbalken gegen die eigenen Ziele, plus eine Zeile mit den restlichen Nährwerten (Zucker/Ballaststoffe/ges. Fett/Salz, ohne eigenes Ziel), sowie die Einträge gruppiert nach Frühstück/Mittagessen/Abendessen/Snacks. Jeder Eintrag lässt sich über die Stift-/Papierkorb-Icons nachträglich in Menge/Mahlzeit bearbeiten oder löschen.
- Unter „Ziele“ lassen sich die eigenen Tagesziele für Kalorien, Protein, Carbs und Fett festlegen (Default: 2000 kcal / 100 g Protein / 250 g Carbs / 70 g Fett).
- Unter „Historie“ gibt es einen Kalorien-Trend und einen Makro-Trend (Carbs/Protein/Fett) als Grafik über die letzten 30 Tage, eine Wochenübersicht mit echten Kalenderwochen-Durchschnitten (Montag–Sonntag) sowie den Monatsdurchschnitt (jeweils nur über Tage mit Einträgen gemittelt) und eine Liste der letzten 30 Tage — ein Tag antippen zeigt die geloggten Einträge dieses Tages, ebenfalls nach Mahlzeit gruppiert.
- Unter „Kochbuch“ lassen sich eigene Gerichte aus bereits bekannten Produkten zusammenstellen (Name, Zubereitung, Zutaten mit Menge, bei Bedarf direkt per Scan oder manuellem Formular neu angelegt) — die Nährwerte pro 100g werden automatisch aus den Zutaten berechnet. Ein Gericht taucht danach wie ein normales Produkt in der Suche auf (mit „Gericht“-Badge) und lässt sich genauso mengenbasiert loggen, bearbeiten oder löschen — praktisch für Rezepte, die man öfter kocht, oder als Inspiration. Rezepte sind privat und nur für den Ersteller sichtbar, lassen sich aber über „Teilen“ auf der Detailseite gezielt mit anderen registrierten Nutzern teilen (diese können das Rezept dann ansehen und loggen, aber nicht bearbeiten oder löschen).
KI-Nährwertschätzung (optional)
Die Schätzfunktion spricht ein OpenAI-kompatibles Chat-Gateway an und wird rein über
backend/.env konfiguriert:
LLM_API_URL=https://ki-toolbox.scc.kit.edu/api/v1
LLM_API_KEY=dein-key
LLM_MODEL=kit.mistral-small-4-119b-a8b
Ohne gesetzten LLM_API_KEY ist das Feature einfach deaktiviert (der Endpunkt antwortet mit
503) — die restliche App funktioniert unverändert, Nährwerte lassen sich weiterhin manuell
eintragen. Weil die Konfiguration rein OpenAI-kompatibel ist, funktioniert genauso jedes andere
Gateway (OpenAI selbst, ein lokaler Ollama-Server mit OpenAI-API, …) durch bloßes Ändern von
LLM_API_URL/LLM_MODEL.
Als Default ist kit.mistral-small-4-119b-a8b gesetzt: In einem Vergleich der frei verfügbaren
kit.*-Modelle lieferte es als einziges durchgängig valides JSON und war mit ~4 s pro
Anfrage etwa drei- bis viermal schneller als die Alternativen (kit.gpt-oss-120b und
kit.gemma4-31b-it waren langsamer bzw. unzuverlässiger, kit.qwen3.5-397b-A17b lieferte
gar keinen verwertbaren Inhalt).
Tech-Stack
- Backend: FastAPI, SQLModel, SQLite, JWT (python-jose), passlib/bcrypt, httpx, openai (LLM-Client)
- Frontend: React, Vite, TailwindCSS, react-router-dom, axios, html5-qrcode, recharts
- Deployment: Docker, nginx (Reverse Proxy + Static Hosting)