Commit Graph
22 Commits
Author SHA1 Message Date
Nick SzaunigandClaude Sonnet 5 54919d82ed feat: Bild-Upload fuer Inhalte, Produkte und Ernten
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m34s
Echter Datei-Upload statt CSS-Platzhaltergrafiken, gespeichert unter
UPLOAD_DIR (Laufzeit-Verzeichnis, weiss nichts von seiner tatsaechlichen
Speicher-Anbindung — in Produktion ein von der Betreiberin selbst per
Kubernetes eingebundenes Volume, z.B. SMB; lokal ein simples ./uploads).

- lib/uploads.ts: Kernlogik. Bildformat wird ausschliesslich aus den
  tatsaechlichen Bilddaten erkannt (Magic-Bytes via sharp), nie aus
  Client-Dateiname/MIME-Type. Zufaellige, nicht erratbare Dateinamen
  (Path-Traversal-/Overwrite-Schutz). Re-Encoding validiert Pixel-Daten
  vollstaendig und entfernt eingebettete Metadaten (EXIF/ICC/XMP) als
  Nebeneffekt. Content-Type beim Ausliefern kommt ausschliesslich aus
  einer festen Endung->MIME-Zuordnung, nie aus Dateiinhalt/-namen.
- app/api/admin/upload/route.ts: Admin-only Upload-Endpunkt (CSRF- +
  Auth- + Rate-Limit-Pruefung, max. 8 MB, JPEG/PNG/WebP/AVIF).
- app/uploads/[...path]/route.ts: liefert hochgeladene Bilder aus
  (public/ wird zur Build-Zeit fest ins Image gebacken, kann also keine
  zur Laufzeit hinzugefuegten Dateien bedienen) — mit
  Path-Traversal-Schutz und nosniff-Header.
- content-registry.ts: neuer "image"-Feldtyp fuer Hero-/Startseite-
  Grafiken, admin/inhalte hat dafuer ein Upload-Widget.
- Produkt-/Ernten-Admin-Formulare: Mehrfach-Bild-Upload fuer
  Product.images/Harvest.images (beide Felder existierten im Schema,
  hatten aber bislang keine Admin-Oberflaeche). Oeffentliche Seiten
  (Shop, Ernten, Chargen-QR-Seite) zeigen jetzt echte Fotos statt
  Platzhalter, sobald vorhanden.
- Kubernetes: nur ein einfacher volumeMount in 05-app-deployment.yaml
  (Platzhalter-emptyDir, klar als CHANGE-ME markiert) — die eigentliche
  Volume-Quelle (SMB o.ae.) bindet die Betreiberin selbst ein, wie
  gewuenscht keine SMB/CSI-spezifischen Manifeste hinzugefuegt.

Verifiziert: tsc --noEmit clean, 139/139 Tests gruen (21 neu), pnpm
build clean. Upload-/Serving-Pipeline live gegen einen laufenden
Server getestet (Auth-/CSRF-Gates, Path-Traversal-Ablehnung,
Format-Ablehnung) — ein vollstaendiger authentifizierter Upload
end-to-end (braucht echten Admin-Login inkl. 2FA) wurde nicht live
durchgespielt, nur per Code-Review verifiziert.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 19:59:42 +02:00
Nick SzaunigandClaude Sonnet 5 3de9f2906e fix: Platzhalter auf Rechtstexten entfernen (Review-Hinweis, DPO, Hosting)
Auf Wunsch entfernt: der bislang auf JEDER Rechtstextseite sichtbare
"[PLATZHALTER — vor Veröffentlichung rechtlich prüfen lassen]"-Hinweis
(rechtliche Prüfung ist laut Auskunft bereits erfolgt). LegalPage
zeigt den Hinweis jetzt nur noch an, wenn noticeText nicht leer ist.

Datenschutzbeauftragte/r-Platzhalter durch eine begründete
Standard-Aussage ersetzt (§ 38 BDSG greift bei einem Betrieb dieser
Größe typischerweise nicht). Hosting-Platzhalter durch eine
zutreffende Aussage ersetzt: die Seite laeuft auf selbst betriebener
Infrastruktur, nicht bei einem externen Anbieter — kein AV-Vertrag
noetig.

NICHT ersetzt (siehe Chat): Name/Rechtsform/Handelsregister/USt-IdNr
im Impressum — diese Angaben kenne ich nicht und darf sie nicht
erfinden, eine falsche Handelsregisternummer waere ein echtes
rechtliches Risiko. Dafuer wurden die echten Werte beim Nutzer
angefragt.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 14:49:13 +02:00
Nick SzaunigandClaude Sonnet 5 d52753cfef feat: ApiCore-Sync auf Chargen erweitern
Der bestehende Ernten-Sync (admin/ernten) laedt jetzt zusaetzlich
Chargen aus ApiCore. Fachliche Zuordnung recherchiert direkt im
ApiCore-Backend-Quellcode (../ApiCore/ApiCore-Backend): Wabenhains
"Charge" (model Batch) entspricht ApiCores "BottlingBatch"
(Schleuderung -> HoneyBatch -> Abfuellung -> BottlingBatch), NICHT
"HoneyBatch" selbst — die Felder passen fast 1:1 (lotNumber/code,
date/fillDate, bestBeforeDate/bestBeforeDate, jarCount/glassCount).

- lib/apicore-client.ts: fetchApiCoreBottlingBatches() laedt
  GET /bottling-batches (inkl. verknuepfter HoneyBatch) und
  GET /extractions (inkl. verknuepfter Ernten) und loest daraus die
  zugehoerige ApiCore-Ernten-ID pro Charge auf.
- admin/actions/apicore-sync.ts: Chargen-Sync laeuft nach dem
  Ernten-Sync (damit frisch angelegte Ernten schon als harvestId
  verfuegbar sind), legt Chargen NUR NEU an (nie Update) — anders als
  Harvest hat Batch kein isPublished-Feld, ein Update wuerde
  insbesondere remainingGlasses (echter, durch Verkaeufe veraenderter
  Lagerbestand) stillschweigend zuruecksetzen. Nicht-fatal: schlaegt
  nur der Chargen-Teil fehl, bleiben bereits synchronisierte Ernten
  erhalten.
- admin/ernten/page.tsx + i18n: zeigt Chargen-Sync-Ergebnis separat an.
- .env.example: zwei zusaetzliche ApiCore-Berechtigungen dokumentiert
  (extractions:read, bottling:read).

Verifiziert: tsc --noEmit clean, 118/118 Tests gruen, pnpm build clean.
Konnte nicht gegen einen echten ApiCore-Server getestet werden (siehe
bereits bestehende Einschraenkung beim Ernten-Sync) — Feldnamen/Shape
direkt aus ApiCore-Backends Prisma-Schema und Service-Code entnommen.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 14:37:11 +02:00
Nick SzaunigandClaude Sonnet 5 5caa85ce3e feat: Apple-Touch-Icon fuer iOS "Zum Home-Bildschirm hinzufuegen"
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 4m12s
app/icon.svg deckt moderne Chrome/Android-Browser-Tabs und den
manifest.ts-Eintrag ab, aber iOS/iPadOS liest fuer "Zum Home-Bildschirm
hinzufuegen" ausschliesslich ein dediziertes Apple-Touch-Icon und
akzeptiert dafuer kein SVG (nur PNG/JPG) — ohne diese Datei waere
stattdessen ein Screenshot der Seite als Icon verwendet worden.
Rendert dieselbe Grafik wie icon.svg per next/og ImageResponse als
180x180-PNG. Per pnpm build + manuellem HTTP-Abruf/Sichtpruefung
verifiziert, dass Satori die SVG-Elemente korrekt rendert.

Social-Media-Icons (TikTok/YouTube/Instagram, site-footer.tsx) per
Code-Review geprueft: einfache, universell unterstuetzte SVG-Primitive
(rect/circle/path), werden nur gerendert wenn eine URL konfiguriert
ist — kein Fix noetig.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 09:26:37 +02:00
Nick SzaunigandClaude Sonnet 5 04cc3061cf perf: DB-Indizes, Footer als Server Component, Site-weite Fehlerseiten
Datenbank (prisma/migrations/20260904120000_add_performance_indexes):
7 fehlende Indizes auf Fremdschluessel-/Filter-Spalten ergaenzt
(Order.customerId+createdAt, Order.status, Order.createdAt,
Order.discountCodeId, OrderItem.orderId, Address.customerId,
AuditLog.createdAt) — bislang volle Tabellenscans bei Bestelluebersicht,
Admin-Dashboard, Checkout-Gutscheinpruefung und Audit-Log.

Bundle-Groesse: Footer (TikTok/YouTube/Instagram-Icons, Copyright-Zeile)
aus dem client-seitigen site-chrome.tsx in eine eigene Server Component
(site-footer.tsx) ausgelagert — brauchte "use client" nur wegen
useI18n(), jetzt getDictionary()-Server-Aufruf wie auf jeder Admin-Seite.
Entfernt ungenutztes clientseitiges JS/Hydration aus jedem Seitenaufruf
(Footer steht im Root-Layout).

UX: app/not-found.tsx und app/error.tsx ergaenzt (Next.js-Konventionen
fuer 404 bzw. Laufzeitfehler) — beide fehlten komplett, unbekannte Routen
und Rendering-Fehler zeigten bisher Next.js' generische, unbrandete
Standardseiten statt einer zur Seite passenden Nutzerfuehrung.
Dictionary-Kollision dabei aufgefallen und aufgeloest: eine bereits
bestehende, admin-interne errorPage-Uebersetzung (src/app/admin/error.tsx,
seit dem urspruenglichen App-Grundgeruest vorhanden) blieb unangetastet,
die neue site-weite Fehlerseite nutzt einen eigenen Schluessel
(siteErrorPage).

Verifiziert: tsc --noEmit clean, 118/118 Tests gruen, pnpm build clean.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 09:07:37 +02:00
Nick SzaunigandClaude Sonnet 5 9e8da18875 fix: Race Condition bei DHL-Versandmarken-Erstellung
Zwei schnell aufeinanderfolgende Klicks auf "DHL-Versandmarke
erstellen" konnten beide den findUnique-Check bestehen und zwei
echte, kostenpflichtige DHL-Sendungen anlegen (gleiche Race-Condition
wie beim zuvor gefixten Retouren-Erstattungs-Bug). Ein reines
create()-mit-Unique-Constraint waere hier nicht ausreichend gewesen,
da Shipment-Datensaetze bereits ohne DHL existieren koennen
(automatisch bei Statuswechsel auf VERSENDET, oder manuell erfasst).
Fix: Serializable-Transaktion claimt den Datensatz atomar mit einem
Sentinel-Wert, bevor der DHL-Aufruf ueberhaupt startet; bei einem
fehlschlagenden DHL-Aufruf wird der Sentinel wieder entfernt.

Gefunden bei eigener Nachpruefung der neuen Admin-Actions dieser
Session (apicore-sync.ts war bereits sauber idempotent, kein Fix
noetig).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 08:52:00 +02:00
Nick SzaunigandClaude Sonnet 5 154b75e0d8 test: Testabdeckung fuer CSRF-Schutz, Entra-ID-SSO und DHL-Client
Keine der in dieser Session neu hinzugefuegten sicherheitskritischen
Module (lib/csrf.ts, lib/entra.ts, lib/dhl.ts) hatte bisher Tests.

- csrf.test.ts: Same-Origin-Erkennung, Ablehnung gefaelschter
  Cross-Origin-Requests, tolerantes Verhalten bei fehlendem/kaputtem
  Origin-Header.
- entra.test.ts: PKCE-Paar-Erzeugung (RFC 7636), Authorize-URL-Aufbau,
  Extraktion des verifizierten E-Mail-Claims (nie ein unplausibles
  Feld raten).
- dhl.test.ts: Adress-Mapping (Strasse/Hausnummer-Trennung,
  ISO2->ISO3-Laendercode), Fehlerbehandlung bei nicht unterstuetztem
  Land, DHL-Validierungsfehlern und HTTP-Fehlern, statt einen falschen
  Erfolg vorzutaeuschen.

Alle 118 Tests (13 Dateien) gruen, tsc --noEmit clean.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 08:48:08 +02:00
Nick SzaunigandClaude Sonnet 5 c59913b75b fix: Race Conditions bei Gutscheincode-Einloesung und Retouren-Erstattung
- Checkout: Gutscheincode-Nutzungslimit wird jetzt innerhalb derselben
  Serializable-Transaktion wie die Bestellanlage frisch nachgeprueft, statt
  nur einmalig vorab. Zwei gleichzeitige Checkouts mit demselben
  maxUses:1-Code konnten sich sonst beide durchsetzen.
- Admin/Retouren: Erstattungsstatus wird jetzt atomar (bedingtes Update,
  ANGENOMMEN -> ERSTATTET) VOR dem Stripe-Aufruf gesetzt statt danach. Ein
  Doppelklick auf "Erstatten" konnte sonst zwei echte Stripe-Erstattungen
  ausloesen. Bei einem fehlschlagenden Stripe-Aufruf wird der Status wieder
  zurueckgesetzt, damit erneut versucht werden kann.

Beide Luecken kamen aus dem Security-Assessment dieser Session.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-03 23:20:38 +02:00
Nick SzaunigandClaude Sonnet 5 b6db9254a9 fix: fehlende Dateien aus vorherigem Commit nachreichen
CSRF-Schutz, DHL-Versandmarken, Entra-ID-SSO, ApiCore-Sync und die
Harvest-isPublished-Migration waren gestaged aber nicht im letzten
Commit enthalten — Gitea-Actions-Build brach mit "Module not found"
fuer genau diese Dateien ab.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-03 20:10:50 +02:00
Nick Szaunig 4d3100e9ba feat: Security CSRF Fix, Logout optimierung, Footer Anpassung, Rechtstexteanpassung
Docker Image bauen und veroeffentlichen / build-and-push (push) Failing after 1m33s
2026-09-03 13:51:50 +02:00
Nick Szaunig 0753a3bc8a SQL and Seed into App
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 1m43s
2026-08-19 08:06:09 +02:00
Nick Szaunig 484da97b2e SQL and Seed into App
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 1m38s
2026-08-19 07:57:45 +02:00
Nick Szaunig d207c8b2b2 SQL and Seed into App
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 3m35s
2026-08-19 07:43:53 +02:00
Nick Szaunig 68781589d8 SQL and Seed into App
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 3m14s
2026-08-19 07:27:27 +02:00
Nick Szaunig 8587265155 SQL and Seed into App 2026-08-19 07:26:47 +02:00
Nick Szaunig 4ed102afe7 Update
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 4m20s
2026-08-19 04:13:24 +02:00
Nick Szaunig 544b51fda1 Update
Docker Image bauen und veroeffentlichen / build-and-push (push) Failing after 11s
2026-08-19 04:04:21 +02:00
Nick Szaunig 332710ab00 Update
Docker Image bauen und veroeffentlichen / build-and-push (push) Failing after 1m58s
2026-08-19 03:41:25 +02:00
Nick Szaunig 04da561380 fix: Gitea-Registry-URL nicht aus gitea.server_url ableiten
Docker Image bauen und veroeffentlichen / build-and-push (push) Failing after 3m2s
gitea.server_url ist in manchen Gitea-Versionen leer (go-gitea/gitea#23276)
und liefert selbst im Erfolgsfall eine volle URL mit https://-Schema statt
des fuer docker login/Image-Tags benoetigten reinen Hostnamens. Registry
jetzt fest als git.encry.de hinterlegt, k8s-Manifeste entsprechend
vorausgefuellt.
2026-08-18 01:36:03 +02:00
Nick Szaunig 5889f58da7 feat: Docker-, Kubernetes- und Gitea-Actions-Deployment-Pipeline
Docker Image bauen und veroeffentlichen / build-and-push (push) Canceled after 0s
Dockerfile (Multi-Stage, "output: standalone"), docker-compose.yml mit
Caddy als einzigem nach aussen offenem Reverse-Proxy (verhindert
X-Forwarded-For-Faelschung fuer das App-eigene Rate-Limiting), Gitea
Actions Workflow der bei jedem Push zwei Image-Tags in die Gitea-
Container-Registry baut (schlankes Laufzeit-Image + ein Migrations-
Image mit Prisma-CLI), sowie rohe Kubernetes-Manifeste (Namespace,
ConfigMap, Secret-Vorlage, Postgres-/MySQL-StatefulSet, Migrations-Job,
App-Deployment, Ingress) fuer den Cluster-Betrieb. Siehe k8s/README.md
fuer den vollstaendigen Einrichtungs-/Deployment-Ablauf.
2026-08-17 14:30:46 +02:00
Nick Szaunig c7927f2883 feat: Wabenhain Honig-Shop Anwendung
Vollstaendige Next.js/Prisma-E-Commerce-Anwendung fuer eine Imkerei:
Shop, Warenkorb, Stripe-Checkout, Kundenkonto mit 2FA, Admin-Bereich
(Produkte, Ernten, Chargen, Bestellungen, Gutscheine, admin-editierbare
Website-Inhalte), i18n (DE/EN), Rate-Limiting/CSP/Audit-Log-Haertung,
PostgreSQL- und MySQL-Unterstuetzung ueber austauschbare Prisma-
Treiber-Adapter, Unit-/Komponenten-/E2E-Tests.
2026-08-17 14:30:28 +02:00
Nick Szaunig 5c76fcc266 Initial commit from Create Next App 2026-08-08 04:57:59 +02:00