This commit is contained in:
2026-06-10 11:19:39 +02:00
parent 90e5f61b91
commit aaa9365e81
11 changed files with 392 additions and 20 deletions
+333 -7
View File
@@ -1,28 +1,354 @@
# Rezept Finder
MVP: Gemeinsame Food-Swipe-Webapp.
Gemeinsame Food-Swipe-Webapp, mit der mehrere Personen in einer Session Rezepte bewerten und am Ende ein gemeinsames Essen finden.
## Entwicklung starten
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
```text
.
├── 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
```bash
cp .env.example .env
docker compose up --build
```
Danach ist die App lokal erreichbar unter:
```text
http://localhost:5173
```
Die API läuft im Dev-Setup über denselben Origin:
```text
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](.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:
```env
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:
```nginx
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:
```bash
docker compose up --build
```
Detached starten:
```bash
docker compose up -d --build
```
Logs ansehen:
```bash
docker compose logs -f
```
Nur Backend-Logs:
```bash
docker compose logs -f backend
```
Services stoppen:
```bash
docker compose down
```
Datenbankvolume ebenfalls löschen:
```bash
docker compose down -v
```
## Entwicklung Ohne Docker
### Backend
```bash
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
flask --app app:create_app db upgrade
python run.py
```
Das Backend erwartet eine gültige `DATABASE_URL`.
### Frontend
```bash
cd frontend
npm install
npm run dev
```
## Postgres lokal per Docker
```bash
docker compose up -d postgres
Für lokale Entwicklung ohne Docker muss `VITE_DEV_API_TARGET` passend auf dein Backend zeigen, z. B.:
```env
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:
```text
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:
```text
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:
```bash
docker compose ps
docker compose logs backend
docker compose logs frontend
```
Im Dev-Setup sollte dieser Request funktionieren:
```bash
curl -i http://localhost:5173/api/socket.io/?EIO=4\&transport=polling
```
Und eine Session-Erstellung:
```bash
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:
```nginx
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:
```bash
docker compose down -v
docker compose up --build
```