Files
rezept_finder/README.md
T
Winz 2337b7588d
Deploy repository / deploy (push) Successful in 17s
new run
2026-06-11 17:35:25 +02:00

8.5 KiB

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, rechts Ja
  • 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 gewinnt
    • Finalrunde 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

  1. Host erstellt eine Session.
  2. Gäste treten per Code oder Einladungslink bei.
  3. Host stellt optional Modus und Like-Schwelle ein.
  4. Host startet die Session.
  5. Alle Teilnehmer bewerten Rezepte per Swipe oder Button.
  6. 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:

  1. Alle bewerten die aktuellen 8 Rezepte.
  2. Rezepte mit genug Likes werden Favoriten.
  3. Gibt es weniger als 3 Favoriten, lädt die App weitere 8 Rezepte nach.
  4. Sobald mindestens 3 Favoriten da sind, startet Runde 2.
  5. In Runde 2 werden nur die Favoriten erneut bewertet.
  6. 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.ico
  • favicon-32.png
  • favicon-512.png
  • apple-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