From 65bfd931cc924f92c6a8584e866a9984c13b1341 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Sat, 18 Jul 2026 12:23:46 +0200 Subject: [PATCH] =?UTF-8?q?Schritt-f=C3=BCr-Schritt-Deployment-Anleitung?= =?UTF-8?q?=20f=C3=BCr=20neuen=20Server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neues DEPLOYMENT.md: Voraussetzungen, Repository holen, .env.local konfigurieren, docker compose up --build, Admin-Zugangsdaten aus ZUGANGSDATEN.md abholen, Reverse-Proxy/HTTPS (Caddy-Beispiel), Updates, Backup der Docker-Volumes. Aus README.md verlinkt. .env.example: LOCAL_PG_PASSWORD war bisher nirgends dokumentiert, obwohl docker-compose.yml die DATABASE_URL des App-Containers daraus zusammenbaut (die dortige DATABASE_URL-Zeile wird beim Docker-Compose-Betrieb ignoriert) – beim Schreiben der Anleitung aufgefallen und ergänzt. README.md: RSS-Feed und Vorschläge-Badge in der Funktionstabelle ergänzt. --- .env.example | 11 +++- DEPLOYMENT.md | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 9 ++- 3 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 DEPLOYMENT.md diff --git a/.env.example b/.env.example index a8b1b0a..50cdb9e 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,16 @@ # Vorlage für .env.local (Entwicklung) bzw. die Umgebungsvariablen # beim Hoster (Live-Betrieb). Echte Werte niemals committen! -# Verbindung zur (selbst gehosteten) PostgreSQL-Datenbank. +# Nur für Docker Compose relevant: Passwort für den mitgelieferten +# PostgreSQL-Container (Dienst "db" in docker-compose.yml). Daraus baut +# docker-compose.yml die DATABASE_URL für die App-Container selbst +# zusammen – die Zeile unten wird beim Betrieb per Docker Compose +# NICHT verwendet, nur bei manuellem "npm run dev"/"npm run start" +# gegen eine selbst betriebene PostgreSQL-Instanz. +LOCAL_PG_PASSWORD= + +# Verbindung zur PostgreSQL-Datenbank (nur für den Betrieb OHNE Docker +# Compose relevant, s. o.). DATABASE_URL=postgres://postgres:@localhost:5432/canteen_menu # Öffentliche URL, unter der die App erreichbar ist (Schema + Domain). diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..ab90368 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,165 @@ +# Deployment auf einem neuen Server + +Schritt-für-Schritt-Anleitung, um Canteen Menu auf einem frischen +(Linux-)Server produktiv zu betreiben. Die App läuft vollständig selbst +gehostet (PostgreSQL + eigene Auth), einzige optionale externe +Abhängigkeit ist DeepL für automatische Übersetzungen. + +## Voraussetzungen + +- Ein Server (VM oder Root-Server) mit Docker und Docker Compose v2 + (`docker compose version` sollte funktionieren; auf den meisten + aktuellen Distributionen per `apt install docker.io + docker-compose-v2` bzw. dem [offiziellen Docker-Install-Skript](https://docs.docker.com/engine/install/) + installierbar). +- Eine Domain, deren DNS-A/AAAA-Eintrag auf die Server-IP zeigt (z. B. + `menu.example.de`). Ohne eigene Domain funktioniert die App auch + unter der reinen Server-IP, dann aber ohne gültiges HTTPS-Zertifikat + und ohne funktionierende Passkeys (siehe unten). +- Mindestens 1 GB RAM frei (App + PostgreSQL sind genügsam, für ~250 + Mitarbeiter reicht die kleinste übliche VM-Größe). + +## 1. Repository auf den Server holen + +```bash +git clone https://github.com//canteen-menu.git +cd canteen-menu +``` + +(Oder per `git pull`/`scp`, falls kein direkter Git-Zugriff vom Server +aus gewünscht ist.) + +## 2. Konfiguration anlegen + +```bash +cp .env.example .env.local +``` + +`.env.local` mit einem Editor öffnen und ausfüllen: + +| Variable | Was rein muss | +| --- | --- | +| `LOCAL_PG_PASSWORD` | Ein neues, zufälliges Passwort (z. B. `openssl rand -base64 24`) – wird beim ersten Start zum Datenbank-Passwort des mitgelieferten PostgreSQL-Containers | +| `APP_URL` | Die öffentliche URL mit Schema, z. B. `https://menu.example.de` (auch wenn HTTPS erst in Schritt 5 eingerichtet wird – schon jetzt korrekt eintragen, da daraus u. a. die Passkey-Domain abgeleitet wird) | +| `SITE_TITLE` | Optional, Titel im Browser-Tab/Überschrift ohne Logo | +| `ADMIN_EMAIL` | Optional, E-Mail des automatisch angelegten Admin-Erstnutzers. Ohne Angabe: `admin@` | +| `DEEPL_API_KEY` | Optional, nur für automatische Übersetzung | + +`DATABASE_URL` in `.env.local` **nicht** anpassen – die wird nur für +den Betrieb ohne Docker gebraucht (siehe README) und beim +Docker-Compose-Start ignoriert. + +Zusätzlich eine leere Datei für die automatisch generierten +Admin-Zugangsdaten anlegen, da sie per Bind-Mount in den Container +eingebunden wird: + +```bash +touch ZUGANGSDATEN.md +``` + +## 3. App starten + +```bash +docker compose --env-file .env.local up -d --build +``` + +Das baut das Docker-Image, startet PostgreSQL und die App, wendet beim +ersten Start automatisch das Datenbankschema an und legt – da die +Datenbank leer ist – einen einzigen Admin-Benutzer mit zufällig +generiertem Passwort an. + +Kurz prüfen, ob beides läuft: + +```bash +docker compose ps +docker compose logs app --tail 30 +``` + +In den Logs sollte u. a. `bootstrap-admin – Admin angelegt: ...` +stehen. + +## 4. Admin-Zugangsdaten abholen + +```bash +cat ZUGANGSDATEN.md +``` + +Enthält E-Mail-Adresse und Passwort des automatisch angelegten +Admin-Kontos. Direkt nach dem ersten Login unter „Mein Konto“ das +Passwort ändern und optional einen Passkey registrieren. + +Die App ist jetzt unter `http://:3000` erreichbar – für den +produktiven Betrieb fehlt noch HTTPS/die Domain (nächster Schritt). + +## 5. Reverse-Proxy + HTTPS einrichten + +Die App selbst spricht nur HTTP auf Port 3000; TLS/HTTPS übernimmt ein +Reverse-Proxy davor. Am einfachsten mit [Caddy](https://caddyserver.com/) +(automatisches Let’s-Encrypt-Zertifikat, keine manuelle Zertifikats- +verwaltung nötig): + +```bash +sudo apt install caddy # oder: siehe caddyserver.com/docs/install +``` + +`/etc/caddy/Caddyfile`: + +``` +menu.example.de { + reverse_proxy localhost:3000 +} +``` + +```bash +sudo systemctl reload caddy +``` + +Caddy holt beim ersten Aufruf automatisch ein Zertifikat und leitet +danach HTTP→HTTPS um. Alternative: nginx + certbot, falls bereits ein +nginx-Setup existiert (Standard-`proxy_pass`-Konfiguration auf +`http://127.0.0.1:3000`, Zertifikat separat per `certbot --nginx`). + +Danach in `.env.local` sicherstellen, dass `APP_URL=https://menu.example.de` +gesetzt ist (Schema `https`, keine Portangabe), und die App neu +starten, falls diese Variable nach Schritt 2 noch geändert wurde: + +```bash +docker compose --env-file .env.local up -d +``` + +## 6. Danach + +- Passkeys funktionieren erst mit korrekt gesetzter `APP_URL` (RP-Domain + wird direkt daraus abgeleitet) und über HTTPS (Passkeys verlangen eine + sichere Origin, `localhost` ausgenommen). +- Weitere Kantinenmitarbeiter/Admins unter „Benutzer“ (nur als Admin + sichtbar) anlegen. +- Essenszeiten, Gerichte, Allergene/Zusatzstoffe sind zu Beginn leer und + werden im Kantinen-Bereich gepflegt. +- Logo/Favicon/Farben unter „Design“ (nur Admin) anpassen. + +## Updates einspielen + +```bash +git pull +docker compose --env-file .env.local up -d --build +``` + +Neue Datenbank-Migrationen (`db/migrations/*.sql`) werden beim Start +automatisch und idempotent angewendet, bestehende Daten bleiben +erhalten. + +## Backup + +Zwei Docker-Volumes enthalten alle persistenten Daten: + +- `canteen-menu_pgdata` – die komplette PostgreSQL-Datenbank +- `canteen-menu_branding_data` – hochgeladenes Logo/Favicon + +```bash +docker run --rm -v canteen-menu_pgdata:/data -v "$PWD":/backup alpine \ + tar czf /backup/pgdata-backup.tar.gz -C /data . +``` + +Für ein reines Datenbank-Backup eignet sich auch `docker compose exec db +pg_dump -U postgres canteen_menu > backup.sql`. diff --git a/README.md b/README.md index dd96518..69d4095 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,16 @@ DeepL als einzige externe API (optional). Läuft vollständig selbst gehostet, keine Cloud-Abhängigkeit außer optional DeepL. Änderungen je Version stehen in [CHANGELOG.md](CHANGELOG.md), Git-Tags -markieren die jeweiligen Versionsstände (`git tag`). +markieren die jeweiligen Versionsstände (`git tag`). Schritt-für-Schritt- +Anleitung für die Inbetriebnahme auf einem neuen Server: +[DEPLOYMENT.md](DEPLOYMENT.md). ## Funktionen | Bereich | Funktionen | | --- | --- | -| Öffentlich (ohne Login) | Wochenplan Mo–Fr mit frei konfigurierbaren Essenszeiten; Navigation vorherige/nächste Woche/Heute; auf schmalen Bildschirmen umschaltbare Tages-/Wochenansicht (Tag ist Standard); Logo zentriert über der Karte; Allergen-/Zusatzstoff-Kürzel je Gericht, Legende unterhalb der Karte; Sprachumschalter Deutsch/Englisch/Polnisch; Rezeptvorschläge einreichen | -| Kantinen-Team (`/intern`) | Essenszeiten anlegen/bearbeiten/löschen/neu anordnen; Gerichte anlegen/bearbeiten/löschen (inkl. Live-Suche, Mehrfachauswahl zum Batch-Löschen, englischem/polnischem Namen mit automatischer DeepL-Übersetzung) mit Allergenen/Zusatzstoffen je Gericht; Gerichte Tagen/Essenszeiten zuweisen (mehrere pro Zelle) und wieder entfernen; globale Allergen-/Zusatzstofflisten pflegen (ebenfalls mehrsprachig); eingereichte Rezeptvorschläge sichten, übernehmen oder ablehnen; eigenes Konto verwalten (Name, E-Mail, Passwort, Passkeys, Passwort-Anmeldung an/aus) | +| Öffentlich (ohne Login) | Wochenplan Mo–Fr mit frei konfigurierbaren Essenszeiten; Navigation vorherige/nächste Woche/Heute; auf schmalen Bildschirmen umschaltbare Tages-/Wochenansicht (Tag ist Standard); Logo zentriert über der Karte; Allergen-/Zusatzstoff-Kürzel je Gericht, Legende unterhalb der Karte; Sprachumschalter Deutsch/Englisch/Polnisch; Rezeptvorschläge einreichen; RSS-Feed (`/feed.xml`, Icon im Kopfbereich) mit einem Eintrag pro Tag zum Abonnieren | +| Kantinen-Team (`/intern`) | Essenszeiten anlegen/bearbeiten/löschen/neu anordnen; Gerichte anlegen/bearbeiten/löschen (inkl. Live-Suche, Mehrfachauswahl zum Batch-Löschen, englischem/polnischem Namen mit automatischer DeepL-Übersetzung) mit Allergenen/Zusatzstoffen je Gericht; Gerichte per Suche Tagen/Essenszeiten zuweisen (mehrere pro Zelle) und wieder entfernen; globale Allergen-/Zusatzstofflisten pflegen (ebenfalls mehrsprachig); eingereichte Rezeptvorschläge sichten, übernehmen oder ablehnen (Anzahl offener Vorschläge als Badge in der Navigation); eigenes Konto verwalten (Name, E-Mail, Passwort, Passkeys, Passwort-Anmeldung an/aus) | | Admin (`/admin`) | Kantinenmitarbeiter anlegen/bearbeiten/löschen (inkl. Passwort-Reset mit Wiederholung und Rollenvergabe); Farbgebung der Speisekarte anpassen; Logo und Favicon hochladen/entfernen | Anmeldung mit E-Mail + Passwort oder alternativ per **Passkey** @@ -37,6 +39,7 @@ mit optionaler automatischer Übersetzung über DeepL (siehe ``` app/ Next.js-Routen page.tsx Öffentliche Speisekarte + feed.xml/ RSS-Feed des Speiseplans (öffentlich) login/ Login (Passwort + Passkey) vorschlag/ Öffentliches Rezeptvorschlag-Formular intern/ Kantinen-Bereich (Rolle: staff oder admin)