Da fachliche Objekte nie hart geloescht werden (nur status +
deactivatedAt), fehlte bisher ein Weg, ein versehentlich aufgeloestes
Volk oder deaktivierten Bienenstand wieder zu aktivieren. Neue
POST :id/reactivate-Endpunkte setzen status zurueck auf ACTIVE und
loeschen deactivatedAt; ConflictException wenn bereits aktiv.
/settings/webhooks: Liste, Anlage-Dialog mit Ereignis-Auswahl und
einmaliger Secret-Anzeige (Kopieren), Loeschen. Sidebar-Eintrag unter
Verwaltung. Live in Chrome verifiziert (Anlegen/Kopieren/Loeschen,
SSRF-Ablehnung, Mobile-Breakpoint); dabei einen Reset-Bug im
Anlage-Dialog gefunden und behoben (Fertig-Button loeschte den
Dialogzustand nicht, da onOpenChange bei programmatischem Schliessen
nicht feuert).
1. The URL guard only classified IP-literal hostnames; any plain domain
name (the common case) skipped validation entirely. Hostnames are now
resolved via DNS and every returned address is checked against the
same private/loopback/link-local ranges (net.BlockList), so an
attacker-controlled domain pointed at an internal IP is rejected too.
2. fetch() follows redirects by default, so a registered public URL could
302 to an internal target at delivery time. Delivery now uses
redirect: 'manual' so such a redirect is never followed.
WebhooksService.create() rejected obviously private, loopback, or
link-local URLs (incl. cloud metadata 169.254.169.254 and the
decimal/octal/hex IPv4 shorthand encodings the WHATWG URL parser
normalizes) so an admin-configured webhook can no longer be used to
proxy requests into internal infrastructure. Registration-time only,
no DNS resolution (DNS-rebinding is out of scope for this check).
WebhooksModule (WebhookSubscription: URL, Events, Secret, aktiv) mit
Event-Dispatcher, angehaengt an 10 Ereignisse in 8 bestehenden Services
(colony.created, inspection.created, queen.created, queen.replaced,
harvest.created, batch.created, batch.updated, product.stock_changed,
task.created, task.completed). Auslieferung ist best-effort und blockiert
die ausloesende Anfrage nicht bei Fehlern.
Tab-basierte /statistics-Seite (Voelker, Koeniginnen, Bienenstaende,
Varroa, Honig, Futter) mit animierten Balkendiagrammen, Sidebar-Eintrag
und Live-Verifikation ueber alle Tabs sowie Mobile-Breakpoint.
Sonner-Toaster global eingebunden. Erfolgs-Toast auf allen sechs
Schnellerfassungs-Formularen (Durchsicht, Varroa, Behandlung,
Fuetterung, Aufgabe, Ernte) - ohne Toast blieb nach dem Speichern
und Wegnavigieren keine sichtbare Bestaetigung, dass die Eingabe
gespeichert wurde. Wichtig fuer klares Feedback bei zeitkritischer
Erfassung am Bienenstand.
Ersetzt die horizontale Kopfzeilen-Navigation durch eine feste
Sidebar links: gruppiert nach Betrieb / Arbeit / Ernte & Lager /
Verwaltung mit Icon+Label je Eintrag, um kognitive Last zu
reduzieren. Aktiver Zustand klar farblich hervorgehoben. Mobile
Ansicht (Bottom-Nav + FAB) bleibt unveraendert bestehen.
Sanfte, kurze Seitenuebergangsanimation (PageTransition, respektiert
prefers-reduced-motion ueber motion-safe:) fuer Orientierung ohne
Ablenkung.
Ausserdem: wiederkehrenden Anzeigefehler in Status-Filtern behoben
(Select zeigte den rohen Sentinel-Wert "ALL" statt "Alle Status" an)
auf Voelker-, Honigchargen-, Aufgaben-, Koeniginnen- und
Bienenstand-Listen. Live in Desktop-, Tablet- und Mobilbreite
verifiziert, kein horizontales Overflow.
Bewertungsformular (Volksstaerke, Sanftmut, Wabensitz, Schwarm-
trieb, Brutbild, Honigertrag je 1-5) plus Verlauf auf der
Koenigin-Detailseite. Live verifiziert.
QueenEvaluation-Modell (Volksstaerke, Sanftmut, Wabensitz, Schwarm-
trieb, Brutbild, Honigertrag je Skala 1-5) als Sub-Ressource der
Queens-API: GET/POST /queens/:id/evaluations. Vorgezogen vor Modul
27 (Statistik), da dessen Koeniginnen-Auswertungen auf diesen Daten
aufbauen.
NotificationBell in der Kopfzeile: ungelesene Anzahl als Badge,
Dropdown mit Prioritaets-Badges (INFO/HINWEIS/WARNUNG/KRITISCH),
Klick markiert als gelesen und fuehrt zum verknuepften Objekt.
Ausserdem IDOR im Backend gefunden und behoben (siehe vorheriger
Commit) - live mit einem echten Datensatz verifiziert.
markRead() pruefte bisher nicht, ob die Benachrichtigung dem
anfragenden Nutzer gehoert - jeder authentifizierte Nutzer konnte
fremde Benachrichtigungen als gelesen markieren. Jetzt: 403 bei
fremder Benachrichtigung, weiterhin erlaubt fuer eigene und
Broadcast-Benachrichtigungen (userId = null). E2E-Regressionstest
ergaenzt.
NotificationsModule: Notification-Modell mit Prioritaeten INFO/
HINWEIS/WARNUNG/KRITISCH, GET /notifications (eigene + Broadcast),
POST /notifications/:id/read. record() als interne API fuer
kuenftige Automatisierungsregeln (Modul 30) vorgesehen.
/calendar Monatsansicht mit Voelkerdaten aus Durchsichten, Aufgaben,
Behandlungen, Fuetterungen, Ernten und Koeniginnen-Zuweisungen;
Bienenstand-Filter, Monatsnavigation, Klick auf Ereignis fuehrt zum
verknuepften Objekt. Nav um Kalender-Link ergaenzt.
CalendarModule mit GET /calendar?from=&to=&apiaryId= aggregiert
Durchsichten, Aufgaben, Behandlungen, Fuetterungen, Ernten und
Koeniginnen-Zuweisungen zu einem einheitlichen Ereignisfeed. Rein
lesende Aggregation, kein neues Datenmodell.
/equipment, /equipment/new, /equipment/:id mit Zustandsaenderung.
statusTone()-Helper um Equipment-Zustaende (NEW/GOOD/WORN/DEFECTIVE)
erweitert, Nav-Dropdown um Inventar ergaenzt.
/products, /products/new, /bottling/new, /bottling/:id. Verbindung
von der Honigcharge-Detailseite zur Abfuellung, Nav-Dropdown "Mehr"
fuer alle Rollen mit Produkte/Abfuellungen erweitert.
ProductsModule (GET/POST /products, GET/PATCH /products/:id) und
BottlingModule (GET/POST /bottling-batches) mit transaktionaler
Reduzierung der HoneyBatch-Restmenge beim Abfuellen; Ablehnung bei
Abfuellmenge > verfuegbarer Menge. Neue Prisma-Modelle Product und
BottlingBatch (Migration bottling_products).
Wiederverwendbarer QrCodeDialog (authentifizierter Blob-Fetch)
auf Volk-, Bienenstand-, Koenigin-, Honigcharge-Detailseiten und
je Lagerbehaelter-Zeile eingebunden.
QrModule mit GET /qr/:entityType/:id.svg fuer colonies (Deep-Link
direkt zu /inspections/new), apiaries, queens, honey-batches und
storage-containers. Nutzt pdfkit-Nachbar qrcode zur SVG-Erzeugung.
Module 22-33 (SOLL) mit Backend/Endpunkte/Frontend/Checkbox-Struktur
analog zum MVP spezifiziert. Spaeter-Liste (KANN) in software-seitig
umsetzbare Punkte und explizit nicht umsetzbare (native App, ML,
Sprach-/Sensorintegration) mit Begruendung aufgeteilt.
CSV-Export in den Listen-Views (Voelker, Honigchargen) und
PDF-Rueckverfolgbarkeitsbericht auf der Honigcharge-Detailseite.
apiFetchBlob() im API-Client ergaenzt fuer authentifizierte
Blob-Downloads (JWT im Authorization-Header statt Cookie).
ExportModule mit GET /export/colonies.csv, /export/honey-batches.csv,
/export/:entity.json (apiaries/colonies/queens/honey-batches/tasks)
und /export/:report.pdf (honey-batch-traceability via pdfkit).
Ausserdem fehlendes "typecheck"-Skript in package.json ergaenzt
(von CLAUDE.md dokumentiert, war aber nicht vorhanden).
Migrations-, Backup- und Restore-Vorgehen in ARCHITECTURE.md
Abschnitt 8 festgehalten. migrate deploy/dev bereits einziger
Migrationsweg (kein db push), /health/ready prueft DB bereits.
AuditModule um GET /audit-log?entityType=&entityId= erweitert
(nur Administrator). Zusaetzlich Jest-E2E-Parallelitaet begrenzt
(maxWorkers: 4), um wiederkehrende Verbindungsengpaesse gegen
Postgres unter vielen parallelen Testsuiten zu vermeiden.
Verifiziert: alle 73 Endpunkte über 17 Tags in /api/v1/docs
sichtbar; jeder Mutations-Endpunkt trägt @Roles (oder @Public bei
Login/Refresh/Logout), lesende Endpunkte sind für alle
authentifizierten Rollen offen (RolesGuard-Standardverhalten).
Mobile Bottom-Navigation und permanent erreichbare "+ Neu"-
Schnellaktion (Durchsicht/Varroa/Behandlung/Fütterung/Aufgabe/Ernte).
Header-Navigation überarbeitet, damit sie bei schmaler Breite intern
scrollt statt die Seite horizontal zu sprengen. Verifiziert bei
Smartphone- (390px) und Tablet-Breite (820px).
/search-Seite über Bienenstände, Völker, Königinnen, Aufgaben und
Honigchargen. Filterleisten (Status/Standort) in den Listen-Views
für Bienenstände, Völker, Königinnen und Honigchargen.
SearchModule mit GET /search?q= über Bienenstände, Völker,
Königinnen, Aufgaben und Honigchargen. Kombinierbare Filter
(Status, Standort, Bienenstand) in den bestehenden List-Endpunkten.
/dashboard als Startseite nach Login mit Kennzahlen-Kacheln und
Hinweisen (überfällige Aufgaben, auffällige Völker, Lagerartikel
unter Mindestbestand).
StorageModule mit Lagerbehaeltern, Lagerartikeln und
Bestandsbewegungen (Zugang/Abgang/Umlagerung/Korrektur), die den
Artikelbestand automatisch fortschreiben.
HoneyBatchesModule inkl. Honigmessungen und
Rückverfolgbarkeits-Endpunkt, der eine Charge über die Schleuderung
und Ernte bis zu den Ursprungsvölkern zurückverfolgt.
/harvests/new (Bienenstand + Mehrfachauswahl Völker), /harvests/[id]
und /extractions/new (Mehrfachauswahl Ernten). Bienenstand- und
Ernte-Detailseiten verlinken direkt in die Kette.
HarvestsModule und ExtractionsModule als Fundament der
Rückverfolgbarkeitskette: eine Ernte kann mehrere Völker umfassen,
eine Schleuderung mehrere Ernten zusammenführen.
/queens: Liste; /queens/new: Anlegeformular; /queens/[id]: Bearbeiten
(Zucht/Herkunft/Status/Notizen), "Volk zuweisen"-Dialog (nur ACTIVE
Voelker zur Auswahl) und Volk-Historie. Bearbeitungsformular initialisiert
Zustand direkt aus der geladenen Queen (gleiches Key-Reset-Pattern wie
bei Apiaries/Colonies).
Nav-Leiste und proxy.ts-Matcher um /queens erweitert.
10 Vitest-Tests, Lint, Typecheck und Build gruen. Kompletter Flow
(Anlegen, Volk zuweisen, Historie) live im Browser gegen den echten
Server verifiziert.
QueensModule: CRUD + assign-colony + history, entsprechend Lastenheft
§4.5 (Koeniginnenverwaltung). assign-colony schliesst die offene
QueenColonyHistory der bisherigen Koenigin am Zielvolk (falls vorhanden)
sowie eine evtl. noch offene History dieser Koenigin an einem anderen
Volk, bevor Colony.currentQueenId gesetzt und ein neuer History-Eintrag
angelegt wird - damit bleibt jeder Koeniginnenwechsel nachvollziehbar,
wie gefordert. Rollen wie bei Apiaries/Colonies: Lesen offen, Schreiben
auf ADMIN/BEEKEEPER beschraenkt.
5 Unit-Tests + 2 e2e-Tests, Lint, Typecheck und Build gruen.
/colonies: Liste; /colonies/new: Anlegeformular mit Bienenstand-/
Volkstyp-Auswahl; /colonies/[id]: Bearbeiten, Aktionen (Umsetzen,
Ableger bilden, Vereinigen, Aufloesen je als Dialog/Button) und
Historie-Anzeige. Aktionen deaktiviert, sobald das Volk nicht mehr
ACTIVE ist (Frontend-Pendant zum neuen Backend-Guard).
Beim Live-Testen im Browser gefunden und behoben:
- Echter Root-Cause fuer den fruehren Session-Abbruch nach Reload: der
Refresh-Token ist einmalig/rotierend, und React StrictMode ruft den
Session-Restore-Effect im Dev-Modus doppelt auf. Der vorherige
AbortController-Fix verhinderte nur, dass der Client die verworfene
Antwort nutzt - er verhinderte NICHT, dass die zweite Anfrage bereits
serverseitig verarbeitet wurde, bevor abort() griff. Fix: echtes
Request-Deduplizieren in refreshRequest() (gemeinsames In-Flight-
Promise) statt Abbruchversuch - der Standardansatz fuer rotierende
Refresh-Token.
- Historie-Text beim Vereinigen war fuer die aufnehmende Seite falsch
("Entstanden durch Vereinigung" impliziert Neuanlage) - jetzt
"Anderes Volk eingegliedert".
Nav-Leiste und proxy.ts-Matcher um /colonies erweitert.
9 Vitest-Tests, Lint, Typecheck und Build gruen. Kompletter Flow
(Anlegen, Umsetzen, Ableger bilden, Vereinigen, Aufloesen, Historie)
live im Browser gegen den echten Server verifiziert, inkl. Rollen-
filterung in den Aktions-Dialogen.
Beim Live-Test im Browser gefunden: move/split/merge liessen sich auch
auf einem bereits DISSOLVED/MERGED-Volk aufrufen und wuerden dort
klaglos durchlaufen - ein Datenintegritaetsproblem, kein reines
UI-Detail. assertActive() wirft jetzt ConflictException (409), wenn
das betroffene Volk (bzw. bei merge: Ziel- und alle zu vereinigenden
Voelker) nicht ACTIVE ist.
Neuer Unit-Test + neuer e2e-Test (dissolve, danach move -> 409).
/apiaries: Liste mit Status-Badge; /apiaries/new: Anlegen-Formular;
/apiaries/[id]: Bearbeiten (Name/Standort/Adresse/Notizen) + Deaktivieren.
Editierbares Formular liest den Ausgangszustand direkt aus der geladenen
Apiary (kein useEffect+setState-Sync, React-Key-Reset-Pattern statt
Effect - vermeidet kaskadierende Renders).
(app)/layout.tsx: einfache Nav-Leiste (Bienenstände fuer alle, Benutzer
nur fuer Admins) - Platzhalter bis zur echten Bottom-Nav/Sidebar aus
Modul 15. proxy.ts-Matcher um /apiaries/* erweitert.
Live im Browser gefunden+behoben: Base-UI-Button meldete fehlende
native Button-Semantik beim Rendern als Link (`nativeButton={false}`
noetig, wenn Button auf ein <a> zeigt).
8 Vitest-Tests, Lint, Typecheck und Build gruen. Kompletter Flow
(Anlegen, Liste, Bearbeiten, Deaktivieren, Navigation) live verifiziert.
/login: Formular gegen AuthProvider (Access-Token im Memory, Refresh
per httpOnly-Cookie), Fehlermeldung bei falschen Zugangsdaten.
(app)/layout.tsx: geschuetztes Layout, leitet ohne Session zu /login um,
Header mit Nutzer-E-Mail + Abmelden. settings/layout.tsx: zusaetzliches
Admin-Gate fuer /settings/users.
/settings/users: Liste, Anlegen-Dialog (shadcn/Base UI Dialog+Select),
Deaktivieren - alles ueber TanStack Query gegen die Users-API.
proxy.ts (Next 16 Nachfolger von middleware.ts): optimistische
Cookie-Praesenz-Pruefung fuer /login und /settings/*, echte
Autorisierung bleibt serverseitig (RolesGuard).
Backend-Fixes waehrend Browser-Verifikation gefunden und behoben:
- CORS fehlte komplett (app.enableCors, FRONTEND_ORIGIN) - jeder
Cross-Origin-Request von :3000 nach :3001 wurde geblockt.
- React-StrictMode-Doppel-Effect liess zwei parallele Refresh-Aufrufe
laufen; da Refresh-Tokens rotierend/einmalig sind, gewann einer und
der andere bekam 401 -> Nutzer flog nach jedem Reload aus der Session.
Fix: AbortController im Session-Restore-Effect.
- Manifest verwies auf nicht existente PNG-Icons (404); durch
public/icon.svg ersetzt.
7 Vitest-Tests (Login-Formular, Users-Liste, JWT-Decode), Lint,
Typecheck und Build gruen. Kompletter Flow (Login, Reload-Persistenz,
Benutzer anlegen/deaktivieren, Abmelden, Redirect ohne Session) live
im Browser gegen den echten Server verifiziert.
ARCHITECTURE.md §3 legt fest: Access-Token im Memory/Context, Refresh-Token
als httpOnly-Cookie. Vorherige Implementierung gab beide Tokens im JSON-Body
zurueck - vor dem Frontend-Aufbau (das genau gegen diesen Vertrag baut)
korrigiert.
/api/v1/auth/login und /refresh setzen jetzt ein httpOnly-Cookie
(Path /api/v1/auth, SameSite=Lax, secure in production) und liefern nur
noch { accessToken } im Body. /refresh und /logout lesen den Refresh-Token
aus dem Cookie statt aus dem Request-Body.
e2e-Tests und Live-Smoke-Test (curl mit Cookie-Jar) verifizieren
Login/Rotation/Logout-Invalidierung. 12 e2e- + 26 Unit-Tests, Lint,
Typecheck und Build gruen.
AuthModule: Login/Refresh/Logout mit JWT-Access-Token + rotierendem,
gehashtem Refresh-Token (RefreshToken-Tabelle). JwtAuthGuard + RolesGuard
global via APP_GUARD registriert (gilt fuer jeden kuenftigen Endpunkt),
@Public()-Decorator als Ausnahme (Auth-Endpunkte, /health).
UsersModule: CRUD + Deactivate, nur ADMIN-Rolle, Passwoerter mit bcrypt
gehasht, nie im Response enthalten.
AuditModule: AuditLogService (Grundstruktur), wird von UsersService bei
create/update/deactivate injiziert und geschrieben.
Geteilte App-Konfiguration (Prefix/Versioning/Pipes/Helmet) aus main.ts
in src/app.config.ts extrahiert; e2e-Tests nutzten das vorher nicht und
bekamen dadurch 404 auf jede /api/v1/... Route - jetzt ueber
test/utils/create-test-app.ts geteilt, betrifft alle kuenftigen
e2e-Tests gleichermassen.
prisma/seed.ts legt einen Admin-User fuer lokale Entwicklung an.
26 Unit-Tests + 11 e2e-Tests (gegen echte Postgres-Testdatenbank) gruen,
Lint/Typecheck/Build gruen, Login/Refresh/Logout/Rollenpruefung manuell
gegen laufenden Server verifiziert.