From b512093a7e0b801554032d6cde5954cd4c539884 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Sat, 18 Jul 2026 11:02:16 +0200 Subject: [PATCH] v1.5: Docker/Compose/.env.example auf selbst gehostetes Postgres umgestellt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dockerfile: keine NEXT_PUBLIC_*-Build-Args mehr (Branding-URLs sind jetzt relativ, kein Anon-Key-Konzept mehr – das Image wird dadurch umgebungsunabhängig), db/migrate.mjs + scripts/ werden zusätzlich in die Runner-Stage kopiert (nicht Teil von Next's Standalone-Trace), CMD wendet Migrationen an und legt bei Bedarf den Admin an, bevor der Server startet. docker-compose.yml: db-Dienst ist jetzt die primäre Datenbank (kein Supabase-Dump-Mount mehr), neues Volume branding_data für Logo/Favicon, Bind-Mount für ZUGANGSDATEN.md. .env.example: Supabase-Variablen durch DATABASE_URL + optionales ADMIN_EMAIL ersetzt. package.json: @supabase/ssr und @supabase/supabase-js entfernt. README.md komplett auf die neue Architektur aktualisiert. --- .dockerignore | 2 +- .env.example | 20 +++--- Dockerfile | 14 ++--- README.md | 151 +++++++++++++++++++++------------------------ docker-compose.yml | 39 ++++++------ package-lock.json | 120 ----------------------------------- package.json | 2 - 7 files changed, 105 insertions(+), 243 deletions(-) diff --git a/.dockerignore b/.dockerignore index 3bf1d9e..af09176 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,7 +5,7 @@ node_modules .env* !.env.example ZUGANGSDATEN.md -supabase/local +data/branding Dockerfile docker-compose.yml README.md diff --git a/.env.example b/.env.example index 00f35de..a8b1b0a 100644 --- a/.env.example +++ b/.env.example @@ -1,25 +1,23 @@ # Vorlage für .env.local (Entwicklung) bzw. die Umgebungsvariablen # beim Hoster (Live-Betrieb). Echte Werte niemals committen! -# Supabase-Projekt-URL (Dashboard -> Project Settings -> API) -NEXT_PUBLIC_SUPABASE_URL=https://.supabase.co - -# Öffentlicher anon-Key (darf im Browser landen, RLS schützt die Daten) -NEXT_PUBLIC_SUPABASE_ANON_KEY= - -# Service-Role-Key (GEHEIM! Nur serverseitig, für die Benutzerverwaltung) -SUPABASE_SERVICE_ROLE_KEY= +# Verbindung zur (selbst gehosteten) PostgreSQL-Datenbank. +DATABASE_URL=postgres://postgres:@localhost:5432/canteen_menu # Öffentliche URL, unter der die App erreichbar ist (Schema + Domain). # Wird für den CSRF-Origin-Check hinter Reverse-Proxies genutzt und ist -# zugleich die Passkey-Domain: nach einer Änderung einmalig -# SUPABASE_ACCESS_TOKEN= node scripts/configure-passkeys.mjs -# ausführen, damit Passkeys auf der neuen Domain funktionieren. +# zugleich die Passkey-Domain (RP-ID/Origin werden direkt daraus +# abgeleitet, kein separater Konfigurationsschritt nötig). APP_URL=http://localhost:3000 # Titel im Browser-Tab und Überschrift, solange kein Logo hochgeladen ist. SITE_TITLE=Canteen Menu +# Optional: E-Mail-Adresse für den automatisch angelegten Admin-Erstnutzer +# (nur relevant, solange die users-Tabelle noch leer ist). Ohne Angabe +# wird admin@ verwendet. +ADMIN_EMAIL= + # Optional: DeepL-API-Key für automatische Übersetzung von Gerichten nach # Englisch/Polnisch (kostenloser Free-Tier unter deepl.com/de/pro-api, # Free-Keys enden auf ":fx"). Ohne Key bleibt "Automatisch übersetzen" diff --git a/Dockerfile b/Dockerfile index 7a6835f..aade5ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,12 +15,6 @@ WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . -# NEXT_PUBLIC_*-Variablen werden zur Build-Zeit ins Client-Bundle eingebacken -ARG NEXT_PUBLIC_SUPABASE_URL -ARG NEXT_PUBLIC_SUPABASE_ANON_KEY -ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL -ENV NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY - RUN npm run build # ---------- Runtime ---------- @@ -35,7 +29,13 @@ RUN addgroup -S nodejs && adduser -S nextjs -G nodejs COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +# Von Next's Standalone-Output nicht automatisch mitgetraced (werden nicht +# von App-Routen importiert, sondern eigenständig vor dem Start ausgeführt). +COPY --from=builder --chown=nextjs:nodejs /app/db ./db +COPY --from=builder --chown=nextjs:nodejs /app/scripts ./scripts + +RUN mkdir -p /data/branding && chown nextjs:nodejs /data/branding USER nextjs EXPOSE 3000 -CMD ["node", "server.js"] +CMD ["sh", "-c", "node db/migrate.mjs && node scripts/bootstrap-admin.mjs && exec node server.js"] diff --git a/README.md b/README.md index ccfca73..6cb6253 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,11 @@ Digitale Speisekarte für eine Firmenkantine (ca. 250 Mitarbeiter) mit öffentlicher Wochenansicht, Kantinen-Bereich zur Pflege der Gerichte und Admin-Bereich für Benutzerverwaltung und Branding. -**Stack:** Next.js 16 (App Router, TypeScript, Tailwind CSS) · Supabase -(PostgreSQL, Auth inkl. Passkeys, Storage, Row Level Security) +**Stack:** Next.js 16 (App Router, TypeScript, Tailwind CSS) · selbst +gehostetes PostgreSQL (via `pg`, eigene Migrationen) · eigene Session-Auth +(Passwort via `node:crypto` scrypt, Passkeys via `@simplewebauthn`) · +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`). @@ -14,14 +17,14 @@ markieren die jeweiligen Versionsstände (`git tag`). | Bereich | Funktionen | | --- | --- | -| Öffentlich (ohne Login) | Wochenplan Mo–Fr mit den Essenszeiten Brotzeit (09:00–10:00), Mittagessen, Mittagessen vegetarisch und Suppe (je 11:30–13:30); 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`) | 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 | +| 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) | | 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** (Registrierung im eigenen Konto nach dem ersten Login). Die Passwort-Anmeldung kann pro Konto deaktiviert werden, sobald mindestens -ein Passkey registriert ist. +ein Passkey registriert ist (serverseitig erzwungen). Die gesamte Oberfläche ist auf Deutsch, Englisch und Polnisch verfügbar (Sprachumschalter, Cookie-basiert). Gerichte, Essenszeiten, Allergene und @@ -39,18 +42,29 @@ app/ Next.js-Routen intern/ Kantinen-Bereich (Rolle: staff oder admin) vorschlaege/ Rezeptvorschläge sichten/übernehmen admin/ Verwaltung (Rolle: admin) - api/admin/users/ Benutzerverwaltung (serverseitig, Service-Role-Key) - api/translate/ DeepL-Anbindung (serverseitig) + branding/[filename]/ Logo/Favicon ausliefern (öffentlich) + api/auth/ Login, Logout, Passkeys + api/account/ Eigenes Konto (Name, E-Mail, Passwort, Toggle) + api/admin/users/ Benutzerverwaltung (nur Admin) + api/admin/branding/ Logo-/Favicon-Upload (nur Admin) + api/dishes/, api/sections/, api/allergens/, api/additives/, + api/menu-entries/, api/dish-suggestions/, api/settings/ + CRUD-Routen für den Kantinen-/Admin-Bereich + api/translate/ DeepL-Anbindung (serverseitig) api/locale/ Sprach-Cookie setzen components/ Client-Komponenten (WeekEditor, DishManager, …) components/i18n/ Sprachkontext, Sprachumschalter lib/i18n/ Wörterbücher (de/en/pl), Locale-Helfer -lib/supabase/ Supabase-Clients (Browser / Server / Admin) +lib/auth/ Sessions, Passwort-Hashing, WebAuthn-Konfiguration +lib/db/ pg-Pool, Query-Helfer, gemeinsame Ladefunktionen +lib/branding.ts Lokale Datei-Ablage für Logo/Favicon lib/ Typen, Datums-/Formatierungshelfer -proxy.ts Session-Refresh + Schutz von /intern und /admin -supabase/migrations/ Schema & Seed-Daten (Referenz/Doku) -supabase/local/ Skripte + Dumps für die lokale PostgreSQL-Kopie -scripts/ Hilfsskripte (DB-Migration, lokaler Sync) +proxy.ts Session-Prüfung + Schutz von /intern, /admin, /konto +db/migrations/ Datenbankschema (reines SQL, nummeriert) +db/migrate.mjs Wendet neue Migrationen an (idempotent, bei jedem Start) +scripts/bootstrap-admin.mjs + Legt bei leerer Datenbank automatisch einen + Admin mit Zufallspasswort an (siehe ZUGANGSDATEN.md) ``` ## Entwicklung @@ -66,25 +80,25 @@ docker compose --env-file .env.local --profile dev up dev # http://localhost:3000 (Hot-Reload, Quellcode wird gemountet) ``` -**Variante B – lokal installiertes Node.js:** +**Variante B – lokal installiertes Node.js + PostgreSQL:** ```bash npm install npm run dev # http://localhost:3000 (bindet an 0.0.0.0, auch im LAN erreichbar) ``` -## Umgebungsvariablen (Live-Betrieb) +In beiden Varianten wird die Datenbank beim Start automatisch auf den +neuesten Stand migriert und – falls noch kein Benutzer existiert – ein +Admin-Konto mit Zufallspasswort angelegt (siehe [Zugangsdaten](#zugangsdaten)). -Beim Webserver/Hoster müssen genau diese drei Variablen hinterlegt werden -(Werte: Supabase-Dashboard → Project Settings → API Keys): +## Umgebungsvariablen (Live-Betrieb) | Variable | Bedeutung | Geheim? | | --- | --- | --- | -| `NEXT_PUBLIC_SUPABASE_URL` | URL des Supabase-Projekts, z. B. `https://hvzikwjoyhoakedlywhj.supabase.co` | nein | -| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Öffentlicher anon-Key (RLS schützt die Daten) | nein | -| `SUPABASE_SERVICE_ROLE_KEY` | Service-Role-Key für die Benutzerverwaltung | **ja – niemals im Browser/Repo!** | -| `APP_URL` | Öffentliche URL der App, z. B. `https://menu.example.de` (Origin-Check hinter Reverse-Proxy; zugleich Passkey-Domain) | nein | +| `DATABASE_URL` | Verbindung zur PostgreSQL-Datenbank, z. B. `postgres://postgres:@db:5432/canteen_menu` | **ja** | +| `APP_URL` | Öffentliche URL der App, z. B. `https://menu.example.de` (Origin-Check hinter Reverse-Proxy; zugleich Passkey-RP-Domain – wird direkt daraus abgeleitet, kein separater Konfigurationsschritt nötig) | nein | | `SITE_TITLE` | Titel im Browser-Tab und Überschrift ohne Logo, Default `Canteen Menu` | nein | +| `ADMIN_EMAIL` | Optional: E-Mail des automatisch angelegten Admin-Erstnutzers (nur relevant, solange die Datenbank leer ist). Ohne Angabe: `admin@` | nein | | `DEEPL_API_KEY` | Optional: aktiviert „Automatisch übersetzen“ für Gerichte/Allergene/Zusatzstoffe (DeepL, kostenloser Free-Tier). Ohne Key funktioniert alles weiter, nur automatisch übersetzen ist deaktiviert | **ja – niemals im Browser/Repo!** | Build & Start auf dem Server – **mit Docker** (empfohlen, Node.js und @@ -93,82 +107,55 @@ PostgreSQL müssen nicht installiert sein): ```bash docker compose --env-file .env.local up -d --build # App: Port 3000 (hinter Reverse-Proxy mit HTTPS betreiben) -# Lokale DB-Kopie: Port 5433 (Dienst "db", optional) +# PostgreSQL: Port 5433 ``` -Oder klassisch ohne Docker: +Vor dem ersten Start: eine leere `ZUGANGSDATEN.md` im Projektverzeichnis +anlegen (`touch ZUGANGSDATEN.md`), da diese Datei per Bind-Mount in den +Container eingebunden wird und die automatisch generierten Admin- +Zugangsdaten dort landen. + +Oder klassisch ohne Docker (mit lokal erreichbarem PostgreSQL): ```bash npm ci npm run build -npm run start # Port 3000, bindet an 0.0.0.0 (hinter Reverse-Proxy betreiben) +npm run start # Port 3000, bindet an 0.0.0.0 (hinter Reverse-Proxy betreiben) ``` -### Passkeys für die Produktions-Domain freischalten - -Passkeys sind an die Domain gebunden. Aktuell ist `localhost` konfiguriert. -Vor dem Livegang: `APP_URL` auf die öffentliche URL setzen und einmalig - -```bash -SUPABASE_ACCESS_TOKEN= node scripts/configure-passkeys.mjs -``` - -ausführen (liest `APP_URL` aus der Umgebung; die URL kann alternativ als -Argument übergeben werden). Ohne lokales Node.js geht es auch über Docker: - -```bash -docker run --rm -v .:/app -w /app \ - -e SUPABASE_ACCESS_TOKEN= -e APP_URL=https://menu.example.de \ - node:24-alpine node scripts/configure-passkeys.mjs -``` - -Achtung: Bereits registrierte Passkeys der alten Domain verlieren dabei -ihre Gültigkeit und müssen neu registriert werden. +Passkeys sind an die Domain gebunden (RP-ID = Hostname aus `APP_URL`). +Vor dem Livegang `APP_URL` auf die öffentliche URL setzen – ein +zusätzlicher Konfigurationsschritt ist nicht nötig. Bereits registrierte +Passkeys einer vorherigen Domain verlieren dabei ihre Gültigkeit und +müssen neu registriert werden. ## Datenbank -### Supabase (führende Datenbank) +Reines PostgreSQL (kein Supabase, keine Row-Level-Security-Policies) – +alle Zugriffsrechte werden in den Route-Handlern geprüft (`lib/auth/session.ts`, +`requireRole`). -- Projekt-Ref: `hvzikwjoyhoakedlywhj` (Region `eu-central-1`, Frankfurt) -- Schema und Seed-Daten: `supabase/migrations/0001_init.sql` und `0002_seed.sql` -- Ausführen weiterer SQL-Dateien: - `SUPABASE_ACCESS_TOKEN= node scripts/db-apply.mjs ` +- Schema: `db/migrations/*.sql`, nummeriert, wird von `db/migrate.mjs` + automatisch angewendet (idempotent, bei jedem Start des Servers bzw. + Docker-Containers). +- Neue Migration hinzufügen: `db/migrations/000N_beschreibung.sql` + anlegen, wird beim nächsten Start automatisch eingespielt. +- Manuell migrieren: `npm run db:migrate` -Sicherheitsmodell (Row Level Security): +Rechte-Modell (in Anwendungscode statt Datenbank-RLS): -- `anon` darf ausschließlich lesen (Speisekarte, Essenszeiten, Allergene, - Zusatzstoffe, Einstellungen). -- `staff`/`admin` (Rolle im `profiles`-Eintrag) pflegen Gerichte, - Wochenplan und Kennzeichnungen. -- Nur `admin` ändert Einstellungen (Farben/Logo) und verwaltet Benutzer; - das Anlegen/Löschen von Benutzern läuft serverseitig über den - Service-Role-Key (`app/api/admin/users`). -- Storage-Bucket `branding` (Logo): öffentlich lesbar, Schreibzugriff nur Admin. - -### Lokale PostgreSQL-Kopie - -Die Datenbank `canteen_menu` (Kopie von Schema + Daten aus Supabase, inkl. -Minimal-Stubs für `auth`/`storage`) gibt es in zwei Varianten: - -**Per Docker:** Der Dienst `db` aus `docker-compose.yml` spielt die -Skripte aus `supabase/local/` beim ersten Start automatisch ein -(PostgreSQL 17, Host-Port 5433 – 5432 ist meist durch eine native -PostgreSQL-Installation belegt –, Passwort über `LOCAL_PG_PASSWORD`). - -**Nativ:** Auf dem Entwicklungsrechner läuft PostgreSQL 17 mit derselben -Datenbank. Neu aufbauen bzw. aktualisieren: - -```powershell -$env:SUPABASE_DB_PASSWORD = "" -$env:LOCAL_PG_PASSWORD = "" -powershell -File scripts\sync-local-db.ps1 -``` - -Hinweis: Die lokale Kopie dient als Ablage/Backup und für Auswertungen. -Logins (Passwort-Hashes, Passkeys) verbleiben ausschließlich in Supabase. +- Öffentlich lesbar (kein Login nötig): Speisekarte, Essenszeiten, + Allergene, Zusatzstoffe, Logo/Favicon. +- `staff`/`admin` pflegen Gerichte, Essenszeiten, Wochenplan und + Kennzeichnungen. +- Nur `admin` ändert Einstellungen (Farben/Logo) und verwaltet Benutzer. +- Logo/Favicon liegen als Dateien in `BRANDING_DIR` (Docker-Volume + `branding_data`), öffentlich ausgeliefert über `/branding/[filename]`. ## Zugangsdaten -Die Anmeldedaten (Admin, Demo-Mitarbeiter, Datenbank-Passwörter) stehen in -`ZUGANGSDATEN.md` – diese Datei ist bewusst **nicht** im Repository -(`.gitignore`). Nach dem ersten Login bitte die Startpasswörter ändern. +Beim allerersten Start mit leerer Datenbank legt `scripts/bootstrap-admin.mjs` +automatisch einen Admin-Benutzer mit zufällig generiertem Passwort an und +schreibt die Zugangsdaten nach `ZUGANGSDATEN.md` – diese Datei ist bewusst +**nicht** im Repository (`.gitignore`). Nach dem ersten Login bitte das +Startpasswort ändern. diff --git a/docker-compose.yml b/docker-compose.yml index 61811cd..b82d7d4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,12 @@ # ============================================================= # Canteen Menu – Docker-Setup # -# Produktion (App + lokale PostgreSQL-Kopie): +# Produktion (App + PostgreSQL): # docker compose --env-file .env.local up -d --build # # Entwicklung (Dev-Server mit Hot-Reload, kein lokales Node nötig): # docker compose --env-file .env.local --profile dev up dev # -# Die --env-file-Angabe speist NEXT_PUBLIC_* in den Build und -# SUPABASE_SERVICE_ROLE_KEY in die Laufzeit-Umgebung. -# # "name" fixiert den Compose-Projektnamen explizit auf "canteen-menu" – # ohne diese Angabe leitet Compose ihn aus dem Ordnernamen ab. # ============================================================= @@ -21,32 +18,32 @@ services: app: build: context: . - args: - NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL} - NEXT_PUBLIC_SUPABASE_ANON_KEY: ${NEXT_PUBLIC_SUPABASE_ANON_KEY} image: canteen-menu-app container_name: canteen-menu-app environment: - NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL} - NEXT_PUBLIC_SUPABASE_ANON_KEY: ${NEXT_PUBLIC_SUPABASE_ANON_KEY} - SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_ROLE_KEY} + DATABASE_URL: postgres://postgres:${LOCAL_PG_PASSWORD:-canteen_menu}@db:5432/canteen_menu # Öffentliche URL der App (Domain hinter dem Reverse-Proxy). - # Wird für den CSRF-Origin-Check genutzt; dieselbe URL ist die - # Passkey-Domain (siehe scripts/configure-passkeys.mjs). + # Wird für den CSRF-Origin-Check genutzt; dieselbe URL ist auch die + # Passkey-Domain (RP-ID/Origin werden direkt daraus abgeleitet). APP_URL: ${APP_URL:-http://localhost:3000} # Titel im Browser-Tab und Überschrift, solange kein Logo hochgeladen ist. SITE_TITLE: ${SITE_TITLE:-Canteen Menu} + # Optional: E-Mail des automatisch angelegten Admin-Erstnutzers. + ADMIN_EMAIL: ${ADMIN_EMAIL:-} # Optional: DeepL-API-Key für automatische Übersetzung von Gerichten - # (Kürzel-Erklärungen, Bereichen) nach Englisch/Polnisch. Ohne Key + # (Essenszeiten, Kürzel-Erklärungen) nach Englisch/Polnisch. Ohne Key # bleibt "Automatisch übersetzen" inaktiv, manuelle Eingabe geht immer. DEEPL_API_KEY: ${DEEPL_API_KEY:-} ports: - "0.0.0.0:3000:3000" + volumes: + - branding_data:/data/branding + - ./ZUGANGSDATEN.md:/app/ZUGANGSDATEN.md + depends_on: + - db restart: unless-stopped - # ---------- Lokale PostgreSQL-Kopie (Ablage/Auswertungen) ---------- - # Beim ersten Start werden die Skripte aus supabase/local - # automatisch in alphabetischer Reihenfolge eingespielt. + # ---------- PostgreSQL (primäre Datenbank) ---------- db: image: postgres:17-alpine container_name: canteen-menu-db @@ -59,7 +56,6 @@ services: - "5433:5432" volumes: - pgdata:/var/lib/postgresql/data - - ./supabase/local:/docker-entrypoint-initdb.d:ro restart: unless-stopped # ---------- Dev-Server (nur mit --profile dev) ---------- @@ -70,18 +66,21 @@ services: working_dir: /app command: sh -c "npm install && npm run dev" environment: - NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL} - NEXT_PUBLIC_SUPABASE_ANON_KEY: ${NEXT_PUBLIC_SUPABASE_ANON_KEY} - SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_ROLE_KEY} + DATABASE_URL: postgres://postgres:${LOCAL_PG_PASSWORD:-canteen_menu}@db:5432/canteen_menu APP_URL: ${APP_URL:-http://localhost:3000} SITE_TITLE: ${SITE_TITLE:-Canteen Menu} + ADMIN_EMAIL: ${ADMIN_EMAIL:-} DEEPL_API_KEY: ${DEEPL_API_KEY:-} ports: - "0.0.0.0:3000:3000" volumes: - .:/app - dev_node_modules:/app/node_modules + - branding_data:/app/data/branding + depends_on: + - db volumes: pgdata: dev_node_modules: + branding_data: diff --git a/package-lock.json b/package-lock.json index 3bce378..88a81e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,6 @@ "dependencies": { "@simplewebauthn/browser": "^13.3.0", "@simplewebauthn/server": "^13.3.2", - "@supabase/ssr": "^0.12.3", - "@supabase/supabase-js": "^2.110.7", "next": "16.2.10", "pg": "^8.22.0", "react": "19.2.4", @@ -1525,102 +1523,6 @@ "node": ">=20.0.0" } }, - "node_modules/@supabase/auth-js": { - "version": "2.110.7", - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.110.7.tgz", - "integrity": "sha512-M5Bpl4hCv6kHcOO/xM06Dyfg1mYLHljMkp1plhzG9IRZPc3czvyMsSN1XpL5+GKisOKM3lSN59zhpcm6sMVXfA==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/functions-js": { - "version": "2.110.7", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.110.7.tgz", - "integrity": "sha512-megYmexlYEoR/0qlsr4Snh9wtzAodO7MAri3NMevZrXzNvQRKlvmTcSBoKGLQEPDakgDZMqbMdf9DwoZz6qfoA==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/phoenix": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.5.tgz", - "integrity": "sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==", - "license": "MIT" - }, - "node_modules/@supabase/postgrest-js": { - "version": "2.110.7", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.110.7.tgz", - "integrity": "sha512-ban6YV0djhVaqVYezlOARKLIuOBSvLLhyQVZjA2nxPrtswhxHCl1+gI4giFgI9ATQAaMNbUZb4JXiuL5lEA/5g==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/realtime-js": { - "version": "2.110.7", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.110.7.tgz", - "integrity": "sha512-AMtZjyFA2gsmjuxopPNS/sRznLQHG0Ht5x+ytTPTOh3vAcOTUlVRLx7gW4/CONNnbb3PKOkE+HmM35HOSbmomQ==", - "license": "MIT", - "dependencies": { - "@supabase/phoenix": "0.4.5", - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/ssr": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.12.3.tgz", - "integrity": "sha512-qWXJ/dI7CiYDKyTgIPqJ4Qkh8y5edh2LdF/nkN48z47mmEVzAO2OE+YErYeQ/UemVfonn/F4kFz+lhLqoVMdpw==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.2" - }, - "peerDependencies": { - "@supabase/supabase-js": "^2.110.5" - } - }, - "node_modules/@supabase/storage-js": { - "version": "2.110.7", - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.110.7.tgz", - "integrity": "sha512-2tcDE8cjEDy1uKxKavBpKQod1JdMV1jDXQag48TCa+kycmJOltc0yVabC0BUlhOwAl6WykXU2aOsH3ELMtZrmQ==", - "license": "MIT", - "dependencies": { - "iceberg-js": "^0.8.1", - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/supabase-js": { - "version": "2.110.7", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.110.7.tgz", - "integrity": "sha512-AnfO3A230Shy6RMO7cya3Wl1OcXnABJrzH8vP+fY7/RFjhzcchB7DjKkkTIAntlwekD+GkSFzEvt2tC+D4Fp8w==", - "license": "MIT", - "dependencies": { - "@supabase/auth-js": "2.110.7", - "@supabase/functions-js": "2.110.7", - "@supabase/postgrest-js": "2.110.7", - "@supabase/realtime-js": "2.110.7", - "@supabase/storage-js": "2.110.7" - }, - "engines": { - "node": ">=22.0.0" - } - }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -3150,19 +3052,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4450,15 +4339,6 @@ "hermes-estree": "0.25.1" } }, - "node_modules/iceberg-js": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", - "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", - "license": "MIT", - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", diff --git a/package.json b/package.json index 3ecfd81..fb0694d 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,6 @@ "dependencies": { "@simplewebauthn/browser": "^13.3.0", "@simplewebauthn/server": "^13.3.2", - "@supabase/ssr": "^0.12.3", - "@supabase/supabase-js": "^2.110.7", "next": "16.2.10", "pg": "^8.22.0", "react": "19.2.4",