Files
einkaufsapp/README.md

288 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Einkaufsliste
Selbst gehostete Einkaufslisten-App für Haushalt, WG oder Verein. Läuft als
Progressive Web App auf dem Telefon, funktioniert ohne Verbindung weiter und
gleicht ab, sobald wieder Netz da ist.
Keine Werbung, kein Tracking, keine externen Schriften oder Skripte. Alle Daten
bleiben auf dem eigenen Server.
## Funktionen
- **Listen teilen** per Einladung mit Rollen (lesen, bearbeiten, verwalten)
oder über einen zeitlich begrenzten öffentlichen Link
- **Offline nutzbar** Änderungen werden lokal festgehalten und später
nachgereicht, ohne dass etwas verlorengeht
- **Nach Märkten und Warengruppen sortiert** die Reihenfolge lässt sich an
den Weg durch den Laden anpassen
- **Strichcodes scannen** eigener EAN-Decoder, Produktdaten von Open Food
Facts (Abfrage über den Server, nicht vom Gerät)
- **Preise erfassen und vergleichen** je Markt, ohne Personenbezug
- **Push-Benachrichtigungen** bei Änderungen, gedrosselt auf höchstens eine
Meldung je Liste alle zwei Stunden
- **Druckansicht** auf A4, nach Märkten gegliedert
- **Benutzerverwaltung** mit einstellbaren Fristen für Bereinigung und Archiv
## Technik
| Schicht | Verwendet |
|---|---|
| Backend | Python 3.12, FastAPI, SQLAlchemy 2, Alembic |
| Datenbank | MariaDB 11.4 |
| Frontend | ES-Module, kein Framework, kein Bauschritt |
| Auslieferung | nginx |
| Betrieb | Docker Compose, drei Container |
## Voraussetzungen
- Docker mit Compose-Plugin
- Ein SMTP-Zugang für Bestätigungs- und Einladungsmails
- Für den öffentlichen Betrieb: ein Reverse Proxy mit TLS (siehe unten)
## Installation
```bash
git clone <repository-url> einkaufsapp
cd einkaufsapp
cp .env.example .env
```
In der `.env` mindestens setzen:
```ini
DB_PASSWORD=… # eigenes Passwort
DB_ROOT_PASSWORD=… # anderes Passwort
ADMIN_EMAIL=du@example.de # das erste Konto
ADMIN_INITIAL_PASSWORD=… # mind. 12 Zeichen, danach ändern
PUBLIC_BASE_URL=https://einkauf.example.de
COOKIE_SECURE=true # false nur beim Testen ohne TLS
SMTP_HOST=mail.example.de
SMTP_PORT=587
SMTP_SECURITY=starttls
SMTP_USER=
SMTP_PASSWORD=
SMTP_FROM=einkauf@example.de
```
Prüfen und starten:
```bash
bash tools/check-env.sh .env
docker compose up -d
docker compose logs -f api
```
Beim ersten Start legt die Anwendung das Administratorkonto an und führt die
Datenbankmigrationen aus. Danach ist sie unter `http://<host>:8080/` erreichbar.
**Nach der ersten Anmeldung:** Passwort ändern und `ADMIN_INITIAL_PASSWORD` in
der `.env` leeren der Wert wird danach ignoriert, stünde aber weiter im
Klartext in der Umgebung.
### Push-Benachrichtigungen (freiwillig)
```bash
python3 tools/vapid-keys.py
```
Die drei ausgegebenen Zeilen in die `.env` übernehmen **vorhandene Zeilen
ersetzen, nicht ergänzen.** Bei doppelten Einträgen nimmt Docker Compose den
letzten, und das ist oft die leere Vorlagenzeile.
```bash
docker compose up -d
```
## TLS und Reverse Proxy
> **Die Anwendung fordert keine Zertifikate an und verwaltet sie nicht.**
> Sie liefert ausschließlich HTTP auf dem konfigurierten Port aus. Für TLS ist
> ein vorgeschalteter Reverse Proxy zuständig.
Ohne HTTPS fehlen wesentliche Funktionen: Service Worker, Kamerazugriff zum
Scannen und Push-Benachrichtigungen sind an einen sicheren Kontext gebunden.
Für lokale Tests gilt `localhost` als sicher, jede andere Adresse nicht.
### Variante A: nginx davor
```nginx
server {
listen 443 ssl http2;
server_name einkauf.example.de;
ssl_certificate /etc/letsencrypt/live/einkauf.example.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/einkauf.example.de/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
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;
# Server-Sent Events: ohne diese beiden Zeilen bekommt die App
# keine Änderungen mit, die andere gemacht haben.
proxy_buffering off;
proxy_read_timeout 3600s;
}
}
```
**Wichtig:** Setze am vorgelagerten Proxy **keine eigene
Content-Security-Policy.** Der mitgelieferte nginx bringt bereits eine mit.
Zwei Richtlinien werden vom Browser als Schnittmenge angewandt die Anwendung
hört dann ohne erkennbaren Fehler auf zu funktionieren.
Die übrigen Sicherheitskopfzeilen (HSTS, `X-Frame-Options`,
`Referrer-Policy`) doppelt zu setzen ist unschädlich.
### Variante B: Traefik
`docker-compose.override.yml` im Projektverzeichnis:
```yaml
services:
web:
ports: [] # kein direkter Port mehr nötig
networks: [einkaufsapp, traefik]
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.einkauf.rule=Host(`einkauf.example.de`)"
- "traefik.http.routers.einkauf.entrypoints=websecure"
- "traefik.http.routers.einkauf.tls.certresolver=le"
- "traefik.http.services.einkauf.loadbalancer.server.port=80"
# SSE: Antworten nicht puffern
- "traefik.http.services.einkauf.loadbalancer.responseforwarding.flushinterval=100ms"
networks:
traefik:
external: true
```
Traefik setzt `X-Forwarded-*` von sich aus. Auch hier gilt: keine zusätzliche
CSP über eine Middleware.
### Nach dem Einrichten prüfen
```bash
curl -sI https://einkauf.example.de | grep -i content-security-policy
```
Es darf **genau eine** Zeile erscheinen.
## Betrieb
### Aktualisieren
```bash
cd einkaufsapp
git pull
bash tools/check-all.sh # alle Prüfungen
docker compose up -d --build
docker compose logs --tail=30 api
```
Migrationen laufen beim Start automatisch. **Vorher sichern** Migrationen
lassen sich nicht immer zurücknehmen.
### Sichern
```bash
docker compose exec -T db mariadb-dump -u root -p"$DB_ROOT_PASSWORD" \
--single-transaction --quick --routines --events \
--default-character-set=utf8mb4 einkaufsapp | gzip > einkaufsapp-$(date +%F).sql.gz
```
Sichere den **Datenbankauszug**, nicht das Volume: Ein Dateisystemabbild einer
laufenden Datenbank ist nicht zuverlässig wiederherstellbar. `--single-transaction`
liefert einen konsistenten Stand, ohne die Anwendung anzuhalten.
Die `.env` gehört ebenfalls gesichert aber getrennt und verschlüsselt. Sie
enthält Datenbankpasswort, SMTP-Zugangsdaten und den privaten VAPID-Schlüssel.
Ausführlich: [`docs/betrieb.md`](docs/betrieb.md)
### Prüfskripte
```bash
bash tools/check-all.sh
```
Prüft Konfiguration, Berechtigungen aller Endpunkte, Datenbankschema gegen die
Modelle, nginx-Kopfzeilen, JavaScript-Module, Fristen und den Strichcode-Decoder.
Rückgabewert 1 bei Problemen als Git-Hook oder in einer CI verwendbar.
### Funktionstest gegen eine laufende Instanz
```bash
bash tools/smoke-test.sh http://localhost:8080 admin@example.de deinpasswort
```
Legt eine Liste an, fügt Einträge hinzu, hakt ab, räumt auf und löscht sich
selbst wieder. Nützlich nach einem Update.
### Entwicklungsmodus
```bash
cp docker-compose.override.yml.example docker-compose.override.yml
docker compose up -d
```
Bindet den Quelltext ein und startet uvicorn bei Änderungen neu. **Die Datei
gehört nicht auf den Produktivserver** dort liefe sonst Code vom Host statt
aus dem gebauten Abbild.
## Rechtliches
`web/html/impressum.html` und `web/html/datenschutz.html` sind **Entwürfe** mit
Platzhaltern. Vor der Veröffentlichung ausfüllen und prüfen lassen die
Hinweiskästen im Dokument nennen, worauf zu achten ist.
Beide sind aus jeder Ansicht über die Fußzeile erreichbar.
## Dokumentation
| Datei | Inhalt |
|---|---|
| [`doku/`](doku/) | Ausführliche Erklärung des Codes, 17 Notizen mit Diagrammen |
| [`docs/betrieb.md`](docs/betrieb.md) | Sicherung, Wiederherstellung, Fehlersuche |
| [`docs/sicherheit.md`](docs/sicherheit.md) | Prüfergebnisse und bekannte Grenzen |
| [`docs/reverse-proxy.md`](docs/reverse-proxy.md) | Zusammenspiel mit einem vorgelagerten Proxy |
| [`docs/entwicklung.md`](docs/entwicklung.md) | Entstehung der Anwendung, Phase für Phase |
| [`db/schema.sql`](db/schema.sql) | Vollständiges Datenbankschema |
## Struktur
```
backend/ FastAPI, SQLAlchemy, Alembic-Migrationen
web/ nginx-Konfiguration und das Frontend
db/schema.sql Schema als ein Stück SQL
tools/ Prüfskripte
docs/ Betriebs- und Sicherheitsdokumentation
doku/ Code-Dokumentation zum Nachlesen
```
## Entstehung
Diese Anwendung ist in Zusammenarbeit mit **Claude (Anthropic)** entstanden.
Konzept, Anforderungen, fachliche Entscheidungen und das Testen im echten
Betrieb kamen von mir; der Code wurde im Dialog erarbeitet einschließlich der
Fehlersuche, wenn etwas nicht funktionierte.
Ich halte diese Angabe für angebracht: Wer den Code liest, soll wissen, wie er
entstanden ist. Die Verantwortung für das, was hier läuft, liegt trotzdem bei
mir geprüft, angepasst und im Alltag erprobt habe ich es selbst. Die Nutzung
erfolgt ohne jegliche Garantie und auf eigene Verantwortung des jeweiligens
Anwenders.
Es wird keine Haftung für Datenverlust oder andere Schäden übernommen.
## Lizenz
GPL v3 (General Public Licence Version 3)
Produktdaten zu Strichcodes stammen von [Open Food Facts](https://openfoodfacts.org)
und stehen unter der Open Database License (ODbL).