Rezept Finder
Gemeinsame Food-Swipe-Webapp, mit der mehrere Personen in einer Session Rezepte bewerten und am Ende ein gemeinsames Essen finden.
Die App lädt dynamisch Rezeptvorschläge von Chefkoch, synchronisiert Teilnehmer per Socket.IO und unterstützt zwei Match-Modi: ein schnelles erstes Match oder eine zweite Finalrunde aus gemeinsamen Favoriten.
Features
- Gemeinsame Sessions mit kurzem Session-Code
- Einladungslink für Gäste
- Host-/Gast-Flow mit eigener Namenseingabe
- Swipe-Bedienung wie bei Dating-Apps: links
Nein, rechtsJa - Buttons als Alternative zum Swipen
- Dynamische Chefkoch-Rezepte aus „Was koche ich heute?“
- Automatisches Nachladen weiterer 8 Rezepte
- Asynchrones Voting: alle Teilnehmer können in eigener Geschwindigkeit voten
- Einstellbare Like-Schwelle ab mindestens 2 Likes
- Zwei Match-Modi:
Erstes Match gewinntFinalrunde aus Favoriten
- Session-Neustart durch den Host mit frischen Rezepten
- Same-Domain-Setup über
/api, vorbereitet für Nginx
Tech Stack
- Frontend: React + Vite
- Realtime: Socket.IO
- Backend: Flask + Flask-SocketIO
- Datenbank: PostgreSQL
- ORM: Flask-SQLAlchemy
- Container: Docker Compose
Projektstruktur
.
├── backend/
│ ├── app/
│ │ ├── models/
│ │ ├── routes/
│ │ ├── services/
│ │ └── socket/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── run.py
├── frontend/
│ ├── public/
│ ├── src/
│ ├── Dockerfile
│ ├── index.html
│ ├── package.json
│ └── vite.config.js
├── docker-compose.yml
├── .env.example
└── README.md
Schnellstart
cp .env.example .env
docker compose up --build
Danach ist die App lokal erreichbar unter:
http://localhost:5173
Die API läuft im Dev-Setup über denselben Origin:
http://localhost:5173/api
Vite proxyt /api intern an den Backend-Container.
Environment
Alle zentralen Konfigurationswerte liegen in .env. Die Datei ist absichtlich nicht versioniert. Als Vorlage dient .env.example.
Wichtige Variablen:
| Variable | Bedeutung |
|---|---|
APP_DOMAIN |
Öffentliche Domain der App, z. B. https://example.com |
FRONTEND_ORIGIN |
Erlaubter Browser-Origin für CORS und Socket.IO |
DATABASE_URL |
SQLAlchemy-Verbindung zum Backend-Postgres |
POSTGRES_DB |
Datenbankname für den lokalen Postgres-Container |
POSTGRES_USER |
Datenbanknutzer für den lokalen Postgres-Container |
POSTGRES_PASSWORD |
Datenbankpasswort für den lokalen Postgres-Container |
SECRET_KEY |
Flask Secret Key |
SOCKETIO_PATH |
Backend-Socket.IO-Pfad, standardmäßig /api/socket.io |
VITE_API_BASE |
Frontend-API-Basis, standardmäßig /api |
VITE_SOCKET_PATH |
Frontend-Socket.IO-Pfad, standardmäßig /api/socket.io |
VITE_DEV_API_TARGET |
Ziel für den Vite-Dev-Proxy im Docker-Netzwerk |
Beispiel für lokale Entwicklung:
APP_DOMAIN=http://localhost:5173
FRONTEND_ORIGIN=http://localhost:5173
POSTGRES_DB=rezept_finder
POSTGRES_USER=rezept_finder
POSTGRES_PASSWORD=rezept_finder
DATABASE_URL=postgresql+psycopg2://rezept_finder:rezept_finder@postgres:5432/rezept_finder
SECRET_KEY=dev-secret
SOCKETIO_PATH=/api/socket.io
VITE_API_BASE=/api
VITE_SOCKET_PATH=/api/socket.io
VITE_DEV_API_TARGET=http://backend:5000
Same-Domain-Routing
Das Frontend spricht bewusst keine harte Backend-URL mehr an. Stattdessen laufen alle Backend-Zugriffe unter /api.
Aktuelle Pfade:
| Pfad | Ziel |
|---|---|
/ |
Frontend |
/api/sessions |
Flask-API |
/api/sessions/<code>/join |
Flask-API |
/api/socket.io |
Socket.IO / WebSocket |
Das macht die App bereit für ein Nginx-Setup mit einer einzigen öffentlichen Domain.
Beispiel: Nginx
Eine mögliche Nginx-Skizze:
server {
server_name example.com;
location / {
proxy_pass http://frontend:5173;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/socket.io {
proxy_pass http://backend:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/ {
proxy_pass http://backend:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Je nach Deployment laufen frontend und backend natürlich unter anderen Upstream-Namen oder Ports.
Docker-Kommandos
Alle Services starten:
docker compose up --build
Detached starten:
docker compose up -d --build
Logs ansehen:
docker compose logs -f
Nur Backend-Logs:
docker compose logs -f backend
Services stoppen:
docker compose down
Datenbankvolume ebenfalls löschen:
docker compose down -v
Entwicklung Ohne Docker
Backend
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python run.py
Das Backend erwartet eine gültige DATABASE_URL.
Frontend
cd frontend
npm install
npm run dev
Für lokale Entwicklung ohne Docker muss VITE_DEV_API_TARGET passend auf dein Backend zeigen, z. B.:
VITE_DEV_API_TARGET=http://localhost:5000
App-Ablauf
- Host erstellt eine Session.
- Gäste treten per Code oder Einladungslink bei.
- Host stellt optional Modus und Like-Schwelle ein.
- Host startet die Session.
- Alle Teilnehmer bewerten Rezepte per Swipe oder Button.
- Je nach Modus gewinnt:
- das erste Rezept mit genug Likes
- oder ein Rezept aus einer zweiten Finalrunde
Match-Modi
Erstes Match Gewinnt
Ein Rezept gewinnt sofort, sobald es die eingestellte Like-Schwelle erreicht.
Beispiel:
- 4 Teilnehmer
- Like-Schwelle: 2
- Sobald 2 Personen dasselbe Rezept liken, ist es das Match.
Finalrunde Aus Favoriten
In diesem Modus gewinnt nicht sofort das erste passende Rezept.
Ablauf:
- Alle bewerten die aktuellen 8 Rezepte.
- Rezepte mit genug Likes werden Favoriten.
- Gibt es weniger als 3 Favoriten, lädt die App weitere 8 Rezepte nach.
- Sobald mindestens 3 Favoriten da sind, startet Runde 2.
- In Runde 2 werden nur die Favoriten erneut bewertet.
- In Runde 2 gewinnt ein Rezept, sobald es die Like-Schwelle erreicht.
Die Reihenfolge im zweiten Modus wird pro Teilnehmer lokal zufällig gemischt.
Rezeptquelle
Die App lädt Vorschläge von:
https://www.chefkoch.de/rezepte/was-koche-ich-heute/
Falls Chefkoch nicht erreichbar ist oder das Parsen fehlschlägt, nutzt das Backend Fallback-Rezepte, damit die App weiter bedienbar bleibt.
Favicon Und Assets
Das Favicon liegt unter:
frontend/public/
Dateien:
favicon.icofavicon-32.pngfavicon-512.pngapple-touch-icon.png
Hinweise Zur Datenbank
Beim App-Start wird das Schema automatisch vorbereitet. Zusätzlich gibt es eine kleine idempotente Aktualisierung für neue Settings-Spalten in session_settings.
Für produktive Deployments sollte später eine echte Migration-Strategie verwendet werden, z. B. Flask-Migrate/Alembic.
Troubleshooting
Frontend Kann Backend Nicht Erreichen
Prüfe:
docker compose ps
docker compose logs backend
docker compose logs frontend
Im Dev-Setup sollte dieser Request funktionieren:
curl -i http://localhost:5173/api/socket.io/?EIO=4\&transport=polling
Und eine Session-Erstellung:
curl -i -X POST http://localhost:5173/api/sessions \
-H 'Content-Type: application/json' \
-d '{"name":"Test"}'
Socket.IO Verbindet Nicht Hinter Nginx
Stelle sicher, dass /api/socket.io WebSocket-Upgrades erlaubt:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
SELinux / Permission Denied Bei Volumes
Die Docker-Volumes nutzen in docker-compose.yml :Z, damit Bind-Mounts auf SELinux-Systemen lesbar sind.
Alte Daten Stören
Lokale Datenbank komplett zurücksetzen:
docker compose down -v
docker compose up --build