CSV-Import/-Export (Gerichte, Allergene, Zusatzstoffe): neue Export-/ Import-Routen unter app/api/*/export und */import, hand-geschriebener CSV-Parser/-Serializer (lib/csv.ts, keine neue Abhängigkeit), Upsert über Kürzel (Allergene/Zusatzstoffe) bzw. exakten Namen (Gerichte, mit Ambiguitäts-Check bei Namensdopplung). UI-Buttons in DishManager und CodeListManager. RSS-Feed: <content:encoded> zusätzlich zu <description> ausgeben, da mehrere Feed-Reader description als kürzbare Vorschau behandeln und dadurch bei mehreren Essenszeiten pro Tag nur die erste zeigten. Version 1.7.0. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
13 KiB
13 KiB
Changelog
Alle nennenswerten Änderungen an diesem Projekt werden hier dokumentiert. Format angelehnt an Keep a Changelog, Versionierung nach Semantic Versioning.
[1.7.0] – 2026-07-19
Hinzugefügt
- CSV-Import/-Export für Gerichte, Allergene und Zusatzstoffe (Buttons
„Exportieren“/„Importieren“ auf den jeweiligen Verwaltungsseiten).
Export lädt die aktuelle Liste als CSV herunter (Gerichte:
name,name_en,name_pl,price,allergens,additives, Allergene/ Zusatzstoffe:code,name,name_en,name_pl; Kürzel bei Gerichten Semikolon-getrennt in einer Zelle, z. B.A;G). Import liest dieselbe Spaltenstruktur (Reihenfolge egal, Groß-/Kleinschreibung der Kopfzeile wird ignoriert) und aktualisiert bestehende Einträge (Abgleich über Kürzel bei Allergenen/Zusatzstoffen, über den exakten Namen bei Gerichten) bzw. legt neue an. Zeilen mit fehlenden Pflichtfeldern, ungültigem Preis oder mehrdeutigem Gerichtsnamen (mehrere Gerichte mit demselben Namen) werden übersprungen und mit Grund in der Erfolgsmeldung aufgelistet, statt den ganzen Import abzubrechen.
Behoben
- RSS-Feed (
/feed.xml): Mehrere Feed-Reader (u. a. Outlook, Thunderbird) behandeln<description>als kurze Vorschau und kürzen sie eigenmächtig – dadurch war bei Tagen mit mehreren Essenszeiten oft nur die erste sichtbar, obwohl der Server bereits alle Essenszeiten des Tages in der Beschreibung lieferte. Zusätzlich zu<description>wird jetzt auch<content:encoded>(RSS-Content- Modul) mit demselben vollständigen Inhalt ausgeliefert – von den meisten Readern als vollständiger Artikeltext erkannt und ungekürzt angezeigt.
[1.6.3] – 2026-07-18
Behoben
docker-compose.yml: Derapp-Dienst setzteBRANDING_DIRnicht, sodasslib/branding.tsbeim Hochladen von Logo/Favicon auf den Fallback-Pfad${cwd}/data/branding(=/app/data/branding) statt auf das im Dockerfile vorbereitete, gemountete Verzeichnis/data/brandingauswich./appgehört im Containerroot, der App-Prozess läuft aber als nicht-root-Nutzernextjs– das Anlegen des Fallback-Verzeichnisses scheiterte mitEACCES, der Upload endete mit „Upload fehlgeschlagen: 500". Jetzt wirdBRANDING_DIR=/data/brandingexplizit gesetzt, passend zum Volume-Mount und zum inDockerfileperchownfreigegebenen Verzeichnis.
[1.6.2] – 2026-07-18
Behoben
scripts/bootstrap-admin.mjs: KonnteZUGANGSDATEN.mdnicht geschrieben werden (z. B.EACCES, weil die per Bind-Mount eingebundene Host-Datei root gehört und der App-Container als nicht-root läuft), brach das Skript ab, nachdem der Admin-Benutzer bereits in der Datenbank angelegt war – das generierte Passwort war dann nirgends mehr zu finden. Schreibt jetzt bei einem Fehler stattdessen E-Mail/Passwort ins Log und beendet sich weiterhin erfolgreich.- DEPLOYMENT.md: Schritt 2 fehlte
chmod 666 ZUGANGSDATEN.md(der eigentliche Auslöser des Bugs oben). Neue Fehlerbehebung-Einträge fürECONNREFUSEDbeim Start (harmlose Start-Reihenfolge, löst sich durch den automatischen Neustart von selbst) und für diesenEACCES-Fall inkl. Recovery-Anleitung für einen bereits verwaisten Admin-Account.
[1.6.1] – 2026-07-18
Geändert
- PostgreSQL-Container ist nicht mehr über einen Host-Port erreichbar
(
docker-compose.yml:ports: 5433:5432beimdb-Dienst entfernt). App und Dev-Server erreichen die Datenbank weiterhin über das von Docker Compose automatisch angelegte interne Netzwerk (Hostnamedb) – reduziert die Angriffsfläche, ohne den Betrieb zu ändern. Debugging/Backup weiterhin überdocker compose exec db psql/pg_dumpmöglich.
Behoben
- DEPLOYMENT.md: Fehlerbehebung-Abschnitt für
password authentication failed for user "postgres"ergänzt (tritt auf, wenndocker compose upvor dem Setzen vonLOCAL_PG_PASSWORDläuft oder ohne--env-file .env.local– PostgreSQL übernimmt das Passwort nur beim allerersten Start auf leerem Datenverzeichnis)..env.examplewarnt jetzt vor Sonderzeichen inLOCAL_PG_PASSWORD, die beim.env-Parsing zu stillen Fehlern führen können.
[1.6.0] – 2026-07-18
Hinzugefügt
- RSS-Feed des Speiseplans (
/feed.xml, ein Eintrag pro Tag mit den Gerichten je Essenszeit für die aktuelle und die nächste Woche) zum Abonnieren in einem Feedreader. Autodiscovery-Link im Seiten-<head>sowie sichtbares RSS-Icon im Kopfbereich der öffentlichen Speisekarte. - Kantinen-Bereich: „Vorschläge“-Navigationspunkt zeigt jetzt die Anzahl offener Rezeptvorschläge als Zahl im roten Kreis.
- Neues DEPLOYMENT.md: Schritt-für-Schritt-Anleitung für die Inbetriebnahme auf einem neuen Server (Docker Compose, Konfiguration, Reverse-Proxy/HTTPS, Updates, Backup).
Geändert
- Letzte Erwähnungen von Supabase in aktiven Doku-Texten und Code-Kommentaren entfernt/umformuliert (README, Migrationsschema, Passkey-Konfiguration, Branding-Ablage) – die App läuft seit v1.5 vollständig ohne Supabase, das sollte sich jetzt auch durchgängig in der Dokumentation widerspiegeln. Historische CHANGELOG-Einträge bleiben unverändert.
.env.example:LOCAL_PG_PASSWORD(vondocker-compose.ymlverwendet, um die Datenbank-Verbindung der App-Container zusammenzubauen) war bisher nirgends dokumentiert – beim Schreiben der Deployment-Anleitung aufgefallen und ergänzt.
[1.5.0] – 2026-07-18
Hinzugefügt
- Wochenplan: „Mahlzeit hinzufügen" bietet jetzt ein Suchfeld mit gefilterter Trefferliste statt einer langen Dropdown-Liste zum Durchscrollen.
db/seed-demo.sql(npm run db:seed-demo): spielt für lokale Entwicklung/Demos den ursprünglichen Beispieldatensatz ein (Essenszeiten, Allergene, Zusatzstoffe, Testgerichte, zwei Beispielwochen) – kein Teil der automatischen Migrationen, frische Deployments starten weiterhin leer.
Geändert
- Supabase vollständig entfernt – die App läuft jetzt komplett
selbst gehostet, einzige externe Abhängigkeit bleibt optional DeepL.
- Datenbank: selbst gehostetes PostgreSQL statt Supabase-Cloud-Postgres.
Schema in
db/migrations/*.sql, wird beim Start automatisch und idempotent angewendet (db/migrate.mjs). - Anmeldung: eigene Session-Verwaltung (opake, in der DB gespeicherte
Tokens,
httpOnly/sameSite=lax-Cookie) statt Supabase Auth. Passwörter werden mit Node-eigenemcrypto.scryptgehasht (keine neue Abhängigkeit). - Passkeys:
@simplewebauthnersetzt Supabase's bisher experimentelles WebAuthn-API. Die RP-Domain wird direkt ausAPP_URLabgeleitet – kein externer Konfigurationsschritt mehr nötig. - Logo/Favicon: lokale Dateiablage (Docker-Volume) statt
Supabase-Storage-Bucket, ausgeliefert über
/branding/[filename]. - Autorisierung (öffentlich lesbar / Kantinen-Team schreibt / nur Admin für Einstellungen & Benutzer) läuft jetzt in den API-Routen statt über Postgres-Row-Level-Security.
- E-Mail-Änderung im eigenen Konto läuft direkt nach Passwort-Bestätigung statt über eine Supabase-Bestätigungsmail (kein SMTP-Server vorhanden).
- Datenbank: selbst gehostetes PostgreSQL statt Supabase-Cloud-Postgres.
Schema in
- Datenbank startet beim allerersten Deployment leer; beim ersten Start
wird automatisch genau ein Admin-Benutzer mit zufällig generiertem
Passwort angelegt, die Zugangsdaten landen in
ZUGANGSDATEN.md.
Behoben
- „Mindestens 1 Passkey nötig, um die Passwort-Anmeldung zu deaktivieren" wurde bisher nur clientseitig geprüft; ein manuell gebauter Request hätte die Passwort-Anmeldung auch ohne Passkey deaktivieren können. Jetzt zusätzlich serverseitig erzwungen.
- Logo-/Favicon-Upload akzeptierte anhand des vom Browser gemeldeten
Dateityps auch SVG-Dateien. Da diese (anders als beim vorherigen
Supabase-Storage-Bucket auf fremder Domain) jetzt auf der eigenen
Origin ausgeliefert werden, hätte eine SVG mit eingebettetem
<script>bei direktem Aufruf der Datei-URL im Browser eines anderen angemeldeten Admins ausgeführt werden können. Upload akzeptiert jetzt nur noch eine Whitelist an Rasterformaten (png/jpg/webp/gif/ico); vor dem Merge im Rahmen der Sicherheitsprüfung gefunden und behoben.
Hinweis für bestehende Docker-Installationen
Das Docker-Volume pgdata diente bisher nur als Lese-Kopie/Backup und
wird mit v1.5 zur primären Datenbank. Es enthält noch das alte
Supabase-Mirror-Schema und muss vor dem Upgrade entfernt werden
(docker compose down -v, danach docker compose up -d --build), damit
die neuen Migrationen auf einer leeren Datenbank laufen.
[1.4.0] – 2026-07-18
Hinzugefügt
- Essenszeiten (Brotzeit/Mittagessen/…) vollständig verwaltbar: anlegen, umbenennen (inkl. EN/PL), Uhrzeit ändern, löschen und per Pfeil-Buttons neu anordnen – neue Seite „Essenszeiten" im Kantinen-Bereich. Löschen entfernt zugewiesene Gerichte automatisch aus allen Wochenplänen.
Geändert
- „Automatisch übersetzen"-Button bei Gerichten/Allergenen/Zusatzstoffen
ist nur noch sichtbar, wenn
DEEPL_API_KEYgesetzt ist, statt erst nach Klick auf die fehlende Konfiguration hinzuweisen.
Behoben
- Sprachumschalter (DE/EN/PL) erschien auf der öffentlichen Speisekarte in der mobilen Ansicht doppelt.
[1.3.0] – 2026-07-18
Hinzugefügt
- Mehrsprachigkeit für die komplette Anwendung (Deutsch, Englisch, Polnisch): Sprachumschalter (Cookie-basiert, kein Neuladen der Seite nötig), sämtliche Oberflächentexte übersetzt
- Gerichte, Bereiche, Allergene und Zusatzstoffe zusätzlich mit englischem/polnischem Namen; öffentliche Speisekarte zeigt je nach gewählter Sprache automatisch den passenden Namen (Fallback: Deutsch, falls keine Übersetzung hinterlegt ist)
- Automatische Übersetzung per DeepL: Button „Automatisch übersetzen“
beim Anlegen/Bearbeiten von Gerichten, Allergenen und Zusatzstoffen;
Ergebnis bleibt danach manuell korrigierbar. Aktivierung über die
neue Docker-Variable
DEEPL_API_KEY– ohne Key bleibt der Button inaktiv, die Felder lassen sich weiterhin von Hand ausfüllen - Rezeptvorschläge im öffentlichen Bereich: nicht angemeldete Besucher können über „Rezept vorschlagen“ ein fehlendes Gericht vorschlagen (Name + optionale Beschreibung), inkl. Dublettenprüfung gegen bestehende Gerichte und einfachem Honeypot-Spamschutz
- Neue Seite „Vorschläge“ im Kantinen-Bereich: eingereichte Vorschläge sichten, als Gericht übernehmen oder ablehnen
Behoben
- Passwort-wiederholen-Feld beim Bearbeiten eines Benutzers erschien erst, nachdem im ersten Passwortfeld schon getippt wurde – wirkte wie ein einzelnes Pflichtfeld
- Anmelden-Button überlappte auf Mobilgeräten mit breiten Logos (Banner-Format) das Logo, da er absolut in der oberen Ecke positioniert war
[1.2.0] – 2026-07-18
Hinzugefügt
- „Heute"-Button in der Wochennavigation (öffentliche Speisekarte und Wochenplan-Editor)
- Mobile Ansicht der öffentlichen Speisekarte: umschaltbar zwischen Tag (Standard) und Woche, unabhängig von der Desktop-Tabelle
- Allergene und Zusatzstoffe wieder pro Gericht auswählbar (Chip-Auswahl im Formular) und als kleine Kürzel neben dem Gerichtnamen sichtbar – in Speisekarte, mobiler Ansicht und Wochenplan-Editor
- Passwort-Wiederholung beim Anlegen neuer Benutzer und beim optionalen Passwort-Reset
- Passwort-Anmeldung pro Konto deaktivierbar (nur wenn mindestens ein Passkey registriert ist), Durchsetzung im Login-Flow
SITE_TITLE-Umgebungsvariable für Browser-Tab-Titel und Überschrift ohne Logo- Austauschbares Favicon über den Admin-Design-Bereich
Geändert
- Gemeinsame Navigation für Kantinen- und Admin-Bereich statt zwei getrennter Menüs
- Live-Suche bei den Gerichten
- Mehrfachauswahl zum Batch-Löschen von Gerichten
- App erreichbar auf
0.0.0.0statt nurlocalhost(Docker und nativer Betrieb) - Projekt umbenannt von „Speisekarte“ zu „canteen-menu“ (Docker-, npm- und Supabase-Namen; deutsche In-App-Texte unverändert)
- Öffentliche URL über
APP_URLkonfigurierbar (CSRF-Origin-Check hinter Reverse-Proxy, Passkey-Domain)
Behoben
- Next.js band
app/favicon.icozusätzlich zu einem dynamisch gesetzten Favicon ein – Favicon wird jetzt immer explizit gesetzt - Weiterleitungen (Logout, Zugriffsschutz) übernahmen nach der 0.0.0.0-Bindung fälschlich die Server-Adresse statt des aufgerufenen Hosts
[1.1.0] – 2026-07-17
Hinzugefügt
- Versionsnummer im Fußbereich jeder Seite
[1.0.0] – 2026-07-17
Erste vollständige Version.
Hinzugefügt
- Öffentliche Speisekarte ohne Login: Wochenansicht Mo–Fr mit den Bereichen Brotzeit, Mittagessen, Mittagessen vegetarisch und Suppe; Wochen-Navigation; Logo zentriert; Allergene & Zusatzstoffe unterhalb der Karte
- Anmeldung mit E-Mail/Passwort oder Passkey
- Kantinen-Bereich: Gerichte anlegen/bearbeiten/löschen, Wochenplan pflegen, globale Allergen-/Zusatzstofflisten verwalten
- Admin-Bereich: Benutzerverwaltung, Farbgebung, Logo-Upload
- Eigenes Konto verwalten: Name, E-Mail, Passwort, Passkeys
- Row-Level-Security, HTTP-Security-Header, CSRF-Origin-Check
- Docker-Setup (App-Image, lokale PostgreSQL-Kopie, Dev-Profil ohne lokales Node.js)
- Lokale PostgreSQL-Kopie mit Sync-Skript