Commit Graph
78 Commits
Author SHA1 Message Date
Nick SzaunigandClaude Sonnet 5 f57cffe6a8 fix: Rechteausweitung ueber Rollen-Rechtezuweisung geschlossen
RolesService.create/update uebernahm eine explizit uebergebene
permissionKeys-Liste ungeprueft. Ein Nutzer mit nur roles:update
konnte damit JEDER Rolle - auch der eigenen, ohne users:assign-role -
beliebige weitere Rechte (bis hin zu allen 92 Katalog-Eintraegen)
hinzufuegen, da das Bearbeiten der eigenen Rollen-Rechte nicht ueber
User.roleId laeuft und daher vom bereits gefixten
users:assign-role-Check nicht erfasst wurde.

Neue Regel (Standard-RBAC-Prinzip "keine Rechte ueber das eigene
Rechte-Set hinaus vergeben"): create/update pruefen jetzt, dass der
aufrufende Nutzer jedes zu vergebende Recht selbst bereits besitzt,
bevor die Rolle angelegt/geaendert wird.

roles.service.ts hatte bislang keine Unit-Tests; neue Spec-Datei deckt
den Fix sowie die uebrigen Methoden (create/update/deactivate/
reactivate) ab.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 14:58:44 +02:00
Nick SzaunigandClaude Sonnet 5 8b8616ce03 fix: Etiketten werden beim Drucken abgeschnitten, wenn Spalten/Zeilen zu gross gewaehlt sind
Eine explizit im Etiketten-Designer eingetragene Spalten-/Zeilenzahl
wurde beim PDF-Rendern ungeprueft uebernommen. Passt sie nicht auf ein
A4-Blatt (z.B. 90mm breite Etiketten x 4 Spalten bei 5mm Rand), ragen
die hinteren Spalten/Zeilen ueber den Blattrand hinaus - der Drucker
schneidet sie dann einfach ab, statt sie zu drucken.

- Backend (label-templates.service.ts): Spalten/Zeilen werden jetzt auf
  das tatsaechlich auf A4 passende Maximum begrenzt, bevor gerendert
  wird - betrifft auch bereits gespeicherte Vorlagen mit zu grossen
  Werten.
- Frontend (label-designer.tsx): zeigt die maximal passende Spalten-/
  Zeilenzahl direkt im Seiten-Layout-Panel an und warnt inline, wenn
  der eingetragene Wert das Blatt sprengen wuerde.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 14:53:15 +02:00
Nick SzaunigandClaude Sonnet 5 19d5e5d65c fix: PermissionsGuard schliesst jetzt statt bei fehlendem Decorator zu oeffnen
PermissionsGuard erlaubte bisher jede Route ohne @RequirePermission
automatisch fuer jeden eingeloggten Nutzer (fail-open) und verliess sich
allein auf test/authorization-coverage.e2e-spec.ts, um eine vergessene
Decorator-Angabe vor dem Deploy zu erwischen - der Guard selbst hatte
kein Default-Deny.

Neuer Decorator @AnyAuthenticatedUser() macht das bisher stillschweigend
erlaubte Verhalten explizit fuer die 12 Routen, die es tatsaechlich
brauchen (ProfileController komplett, NotificationsController komplett,
AuthController.me - alle bereits vorher auf der Allowlist des e2e-Tests).
PermissionsGuard verweigert jetzt jede Route, die weder
@RequirePermission noch @AnyAuthenticatedUser traegt - eine vergessene
Decorator-Angabe schlaegt jetzt zur Laufzeit fehl (403), nicht mehr nur
im Test. Der e2e-Test prueft jetzt denselben Decorator-Vertrag statt
einer separat gepflegten Allowlist, kann also nicht mehr vom
Guard-Verhalten abweichen.

Neue Unit-Tests fuer PermissionsGuard decken beide Codepfade und das
Fail-Closed-Verhalten ab.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 14:44:28 +02:00
Nick SzaunigandClaude Sonnet 5 b7ff92abd3 fix: SSO-MFA-Umgehung, Datei-Magic-Bytes und oeffentliches Swagger geschlossen
Weitere vom Sicherheitsaudit gemeldete Luecken behoben, die bisher als
niedrige Prioritaet offen gelassen wurden:

- SSO-Login (Entra ID) prüft jetzt lokale MFA genauso wie der
  Passwort-Login: ist mfaEnabled gesetzt, liefert /auth/sso/complete
  denselben mfaRequired-Codepfad wie /auth/login, bevor Tokens
  ausgestellt werden. Frontend (Login-Seite, AuthContext) zeigt in dem
  Fall den bestehenden Code-Eingabe-Schritt statt die Anmeldung
  stillschweigend abzuschliessen.
- Avatar- und Dokument-Uploads pruefen jetzt die tatsaechlichen
  Datei-Bytes (Magic Numbers fuer PNG/JPEG/WEBP/PDF) statt nur dem
  client-angegebenen MIME-Typ/data:-URL-Praefix zu vertrauen.
- Swagger-UI (/api/v1/docs) ist jetzt standardmaessig deaktiviert
  (ENABLE_SWAGGER=true erforderlich), da sie zuvor das komplette
  API-Schema ungeschuetzt oeffentlich preisgab.

npm-audit-Warnungen (deepmerge-ts/mysql2 unter prisma) bewusst nicht
gefixt: kein stabiles Prisma-Release loest sie bislang auf, nur eine
Release-Candidate-Hauptversion - das Risiko eines Downgrades/RC-Upgrades
der Datenbankschicht uebersteigt den Nutzen fuer eine Kette, die zur
Laufzeit ohnehin nicht erreichbar ist (nur Prisma-CLI, nur MySQL-Treiber,
dieses Projekt nutzt Postgres).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 09:09:58 +02:00
Nick SzaunigandClaude Sonnet 5 1b16b49913 fix: Rechteausweitung ueber Rollenzuweisung und weitere Auth-Haertung
Ein Sicherheitsaudit deckte auf, dass jede Rolle mit users:update sich
selbst per PATCH /users/:id auf eine Admin-Rolle heraufstufen konnte,
da roleId ohne eigene Berechtigung und ohne Selbstbezug-Pruefung
akzeptiert wurde. Ausserdem lag ein fest im Code hinterlegtes
Standard-Admin-Passwort vor, das bei fehlender SEED_ADMIN_PASSWORD-Env
greift.

- Neue Berechtigung users:assign-role, getrennt von users:create/update,
  fuer jede Rollenzuweisung erforderlich
- Selbstaenderung der eigenen Rolle serverseitig blockiert (auch fuer
  Admins), ebenso Selbst-Reset der eigenen MFA ueber die Admin-Route
- Seed erzeugt bei fehlender Env ein zufaelliges statt eines fixen
  Passworts
- Wiederverwendung eines bereits rotierten Refresh-Tokens (Diebstahl-
  Indiz) widerruft nun alle Sessions des betroffenen Nutzers
- Rate-Limit auf MFA-Enrollment-Bestaetigung ergaenzt

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 08:57:42 +02:00
Nick Szaunig 135738fdfc fix: Profilbild-Upload auf JSON/Base64 umgestellt statt multipart
POST /profile/avatar schlug live mit net::ERR_FAILED fehl (Verbindung
abgebrochen, keine Antwort) - selbst bei einer winzigen Testdatei.
CORS-Preflight lief sauber durch, nur der eigentliche multipart-POST
nicht; passt zum bereits dokumentierten Cloudflare-Verhalten bei
multipart-Uploads (siehe .gitea/workflows/*-image.yml). Umgangen durch
Umstellung auf denselben JSON-Content-Type, der fuer alle anderen
Endpunkte nachweislich zuverlaessig funktioniert: Bild wird
clientseitig als data:-URL kodiert und im JSON-Body gesendet.
Globales JSON-Body-Limit auf 5mb angehoben (deckt 2MB-Bild plus
Base64-Overhead).
2026-09-02 08:32:08 +02:00
Nick Szaunig c6941a797f feat: Etiketten-Seitenlayout auf dem A4-Bogen frei waehlbar
Backend Image / build-and-push (push) Successful in 2m16s
Frontend Image / build-and-push (push) Successful in 2m0s
- LabelTemplate bekommt columns/rows (optional, leer = automatisch
  passend), plus konfigurierbare Raender/Abstaende - damit ein Etikett
  exakt auf das vorgedruckte Raster eines physischen Etikettenbogens
  (z.B. Avery) gelegt werden kann statt nur automatisch berechnet
- Startposition beim Drucken waehlbar, fuer bereits angebrochene Boegen
  (erste Seite startet ab der gewaehlten Position, Folgeseiten wieder
  von vorne)
2026-09-01 19:43:01 +02:00
Nick Szaunig 938239e16e fix: fehlende Dateien fuer Etiketten-Elemente-Umbau nachreichen
Backend Image / build-and-push (push) Successful in 2m19s
Frontend Image / build-and-push (push) Successful in 1m55s
git add -u im letzten Commit hat neue (untracked) Dateien übersprungen:
die Migration für LabelTemplate.elements, die neuen Typ-/DTO-Dateien und
den kompletten Drag-and-Drop-Editor fehlten dadurch im Build.
2026-09-01 16:59:48 +02:00
Nick Szaunig 5f4e1a7ba9 fix: Bug fixed für Rollen
Backend Image / build-and-push (push) Failing after 1m12s
Frontend Image / build-and-push (push) Successful in 1m14s
2026-09-01 16:54:42 +02:00
Nick Szaunig 8432313fa9 feat: Profilseite, Etikettendruck mit QR-Code, mehr Automatisierungen und Entra-ID-SSO
Backend Image / build-and-push (push) Successful in 2m28s
Frontend Image / build-and-push (push) Successful in 2m10s
- Profilseite mit MFA-Einrichtung, Passwort/E-Mail/Name aendern, Konto deaktivieren
- Bearbeiten-Buttons fuer Bienenstaende/Voelker/Koeniginnen/Aufgaben-Datensaetze
- Restliche englische Enum-Werte durch deutsche Labels ersetzt (SelectValue-Fix)
- Sidebar-Eintraege fuer Ernten/Schleuderungen ergaenzt
- Automatisierungen von 3 auf 6 Regeln erweitert (Schwarmstimmung, kritischer Gesundheitsstatus, Lagerbestand)
- Responsive Grid-Layout fuer Volk-/Koeniginnen-/Honigchargen-/Bienenstand-Detailseiten
- Etikettenvorlagen mit Platzhaltern inkl. QR-Code (verweist auf Herkunftsseite) und PDF-Druck
- Entra-ID-SSO (Authorization Code + PKCE, kein Auto-Provisioning, Secrets nur ueber Environment-Variablen)
2026-08-31 18:56:08 +02:00
Nick Szaunig 0a52b29c57 fix: Bug fixed für alle Seiten und das Profil und label
Backend Image / build-and-push (push) Successful in 2m44s
Frontend Image / build-and-push (push) Successful in 2m47s
2026-08-27 15:15:55 +02:00
Nick Szaunig 133edcd5ba fix: fehlende Dateien fuer Volk-Ueberarbeitung nachreichen
Backend Image / build-and-push (push) Successful in 1m16s
Frontend Image / build-and-push (push) Successful in 3m7s
labels.ts und die ColonyCondition-Migration waren untracked und fehlten
im letzten Commit, dadurch schlug der Docker-Build mit "Module not
found: @/lib/colonies/labels" fehl.
2026-08-27 10:51:07 +02:00
Nick Szaunig 8520678a48 Volk Überarbeittung und Englisch Varaiblen ersetzt
Backend Image / build-and-push (push) Successful in 3m43s
Frontend Image / build-and-push (push) Failing after 1m10s
2026-08-27 10:38:06 +02:00
Nick Szaunig f2b9993427 fix: OOM-Crash beim Seed-Schritt durch Vorkompilieren beheben
Backend Image / build-and-push (push) Successful in 3m11s
Frontend Image / build-and-push (push) Successful in 2m46s
ts-node prisma/seed.ts lief bisher live im Container - der
TypeScript-Compiler dafuer braucht deutlich mehr Heap als das
512Mi-Pod-Limit, daher sporadische "JavaScript heap out of memory"-
Crashes beim Start (manchmal lief's, manchmal nicht, reine
GC-Zufaelligkeit an der Speichergrenze).

Fix: seed.ts wird jetzt wie main.ts beim Docker-Build vorkompiliert
(tsconfig.seed.json, rootDir bewusst auf prisma/ statt Projekt-Root
gesetzt, damit der relative Import '../generated/prisma' nach dem
Kompilieren weiterhin auf denselben Pfad zeigt). Laufzeit-CMD startet
nur noch plain `node dist/seed.js` - kein TypeScript-Compiler mehr im
Container. ts-node/typescript zurueck zu devDependencies (nur noch
fuer den Build-Schritt gebraucht, nicht mehr zur Laufzeit).

incremental in tsconfig.seed.json explizit deaktiviert - ein stale
tsconfig.seed.tsbuildinfo liess tsc beim zweiten Testlauf sonst
faelschlich "nichts zu tun" melden (derselbe Fallstrick wie bei
tsconfig.build.tsbuildinfo).

Verifiziert: tsc/eslint clean, 304/304 Tests, Build reproduzierbar
(zweimal hintereinander ohne Cache-Reset getestet), node dist/seed.js
lief erfolgreich gegen die echte lokale DB (erkannte den bestehenden
Admin korrekt und legte ihn nicht erneut an).
2026-08-27 08:02:58 +02:00
Nick Szaunig ce3a0c44ac feat: globales HTTP-Access-Log und Exception-Logging
Backend Image / build-and-push (push) Successful in 2m50s
Frontend Image / build-and-push (push) Successful in 2m22s
Bisher waren 4xx-Fehler (falsches Passwort, Validierungsfehler,
fehlende Berechtigung) im Log komplett unsichtbar - Nest loggt
standardmaessig nur unbehandelte 5xx-Fehler. Ein Login, das aus
Infra-Gruenden fehlschlaegt (siehe TLS/Cloudflare-Vorfall), war dadurch
allein aus den Logs nicht diagnostizierbar.

- AllExceptionsFilter (global): loggt jede Exception mit Methode/Pfad/
  Status (4xx als warn, 5xx als error mit Stacktrace). Client-Antwort
  bleibt unveraendert - HttpExceptions liefern weiter exakt ihren
  bisherigen Body, unbehandelte Fehler weiter nur eine generische
  500-Meldung ohne interne Details.
- requestLoggerMiddleware (global): eine Zeile pro Request (Methode,
  Pfad, Status, Dauer), damit auch erfolgreiche Requests im Log
  auftauchen - vorher gab's dafuer gar keine Spur.
- AuthService.login()/verifyMfaAndLogin(): konkrete Ursache pro
  Fehlschlag (unbekannter User, deaktiviert, falsches Passwort,
  falscher MFA-Code) zusaetzlich zum bestehenden Audit-Log-Eintrag.

Nirgends werden Passwoerter, Tokens oder Request-Bodies geloggt - nur
Methode, Pfad, Status, Dauer und die (bereits client-sichere)
Fehlermeldung.

Verifiziert: tsc/eslint clean, 304/304 Tests (7 neu), Build clean.
2026-08-27 07:29:51 +02:00
Nick Szaunig 05876838b0 feat: Admin-Seed automatisch beim Deploy ausfuehren
Backend Image / build-and-push (push) Successful in 2m35s
Frontend Image / build-and-push (push) Successful in 2m46s
Bisher wurde nirgends automatisch ein erster Admin-User angelegt -
prisma/seed.ts existierte, war aber weder im Dockerfile-CMD noch sonst
irgendwo verdrahtet. Auf einer frischen Datenbank haette also niemand
sich einloggen koennen.

- Dockerfile-CMD: migrate:deploy && seed && node dist/main.js
- ts-node/typescript von devDependencies zu dependencies verschoben
  (seed.ts laeuft als rohes TypeScript zur Laufzeit, kein
  Kompilierschritt), tsconfig.json zusaetzlich ins Runtime-Image kopiert
- seed.ts gegen Unique-Constraint-Race bei mehreren gleichzeitig
  startenden Pod-Replicas gehaertet (P2002 abgefangen statt Absturz)
- Login-Banner im Log nur beim tatsaechlichen Erst-Anlegen (URL aus
  FRONTEND_ORIGIN, Benutzername, Passwort) - bewusst nicht bei jedem
  Neustart, sonst wuerden veraltete/geaenderte Zugangsdaten weiter
  geloggt und Credentials landen unnoetig dauerhaft in den Logs

Seed ist bereits idempotent per E-Mail-Check (auch bei deaktivierten
Usern), UsersService.assertNotLastSystemRoleUser verhindert ohnehin
das Deaktivieren des letzten aktiven Admins - kein Aenderungsbedarf
dort, siehe Konversation.

Verifiziert: tsc/eslint clean, 297/297 Tests, Build clean. Migration
und Schema live gegen die lokale DB bestaetigt (ueber WSL direkt, da
Windows->WSL-Portweiterleitung weiterhin kaputt ist); der eigentliche
Seed-Lauf selbst konnte nicht End-to-End gegen die DB verifiziert
werden (gleiches Verbindungsproblem), Banner-Ausgabe isoliert
smoke-getestet.
2026-08-27 06:38:23 +02:00
Nick Szaunig 62004483cf feat: MFA (TOTP) und Self-Service-Profilseite
Backend Image / build-and-push (push) Successful in 2m57s
Frontend Image / build-and-push (push) Successful in 2m30s
Benutzer koennen jetzt selbst Zwei-Faktor-Authentifizierung einrichten
und ihr Profil verwalten (Name, E-Mail, Passwort, Profilbild).

Backend:
- Neues MfaModule (src/auth/mfa/): AES-256-GCM-verschluesselte
  TOTP-Secrets (otplib), QR-Code-Erzeugung, kurzlebige Setup-/
  Challenge-JWTs fuer den 2-Schritt-Login-Flow
- Neues ProfileModule (src/profile/): PATCH /profile,
  POST /profile/password, POST/DELETE/GET /profile/avatar,
  POST /profile/mfa/enroll(/confirm), POST /profile/mfa/disable -
  alle self-scoped ueber actor.id, ohne @RequirePermission (siehe
  ALLOWLIST in authorization-coverage.e2e-spec.ts)
- AuthService.login() zweistufig: bei aktivem MFA erst
  { mfaRequired, mfaToken } statt Tokens, neuer Endpunkt
  POST /auth/mfa/verify schliesst den Flow ab
- Admin-Recovery: POST /users/:id/mfa/reset (users:update) fuer
  verlorene MFA-Geraete, da es keinen E-Mail-Reset-Flow gibt

Frontend:
- Login-Seite mit zweitem Schritt fuer den TOTP-Code
- Neue Profilseite (/profile, ausserhalb des admin-only
  Settings-Bereichs): Profilbild, Name/E-Mail, Passwort, MFA-Ein-/
  Ausrichtung mit QR-Code-Dialog
- Header zeigt jetzt Avatar mit Dropdown (Profil/Abmelden) statt
  nur E-Mail-Text
- Admin-Users-Tabelle zeigt MFA-Status + Reset-Button

Haertung nach Security-Review (Backend/Frontend-Diff, kein CRITICAL,
mehrere MEDIUM behoben):
- Access-Token wird bei Passwortwechsel sofort ungueltig
  (User.passwordChangedAt + JWT-iat-Vergleich in
  PermissionResolverService.isTokenValid, vorher isUserActive) statt
  erst nach bis zu 15 Minuten
- E-Mail-Aenderung verlangt jetzt aktuelles Passwort
- POST /profile/password und /profile/mfa/disable haben jetzt das
  gleiche strikte Throttling wie Login (SENSITIVE_ACTION_THROTTLE)
- Kein stiller leerer Verschluesselungs-Key-Fallback mehr (wirft
  jetzt explizit)

Bewusst zurueckgestellt (dokumentiert, kein aktiver Exploit-Pfad):
dedizierter Verschluesselungs-Key fuer MFA-Secrets statt
JWT_ACCESS_SECRET-Ableitung, dedizierte users:mfa-reset-Permission
statt users:update, separate JWT-Secrets pro Token-Zweck.

Verifiziert: tsc/eslint clean, 297/297 Backend- + 31/31
Frontend-Unit-Tests, Build clean auf beiden Projekten. e2e weiterhin
durch bekanntes WSL2/Postgres-Problem blockiert. Unabhaengiges
Security- und Code-Quality-Review durchgefuehrt (0 CRITICAL, 0 HIGH).
2026-08-27 00:25:34 +02:00
Nick Szaunig 76a9e9c3a4 feat: Backend migriert die Datenbank beim Start selbst statt ueber Job
Backend Image / build-and-push (push) Successful in 2m49s
Frontend Image / build-and-push (push) Successful in 2m31s
Kein separater Kubernetes-Job mehr. Dockerfile-CMD fuehrt jetzt
`npm run migrate:deploy` aus, bevor `node dist/main.js` startet -
Tabellen werden beim ersten Verbinden automatisch angelegt. Sicher bei
mehreren gleichzeitig startenden Pod-Replicas (replicas: 2), da Prisma
migrate deploy einen DB-seitigen Advisory-Lock nutzt. deployment.yaml
bekommt eine startupProbe (failureThreshold 30 x periodSeconds 10 = bis
zu 5 Min), damit liveness/readiness den Container waehrend der
Migration nicht neu starten. ARCHITECTURE.md entsprechend aktualisiert,
migrate-job.yaml entfernt.
2026-08-26 22:33:44 +02:00
Nick Szaunig a6e5b0331f feat: Kubernetes-Job fuer Prisma-Migrationen beim Deploy
Bisher lief keine Migration automatisch: dem Runtime-Image fehlten
Prisma-CLI, prisma/migrations/ und prisma.config.ts (nur dist+generated
wurden kopiert, prisma war devDependency). Jetzt:
- prisma von devDependencies zu dependencies verschoben (wird fuer
  migrate:deploy im --omit=dev Runtime-Image gebraucht)
- neues npm-Script migrate:deploy (prisma migrate deploy)
- Dockerfile kopiert prisma/, prisma.config.ts zusaetzlich ins Runtime-Image
- deploy/k8s/backend/migrate-job.yaml: Job, der dasselbe Backend-Image mit
  ueberschriebenem command nutzt statt eines eigenen Migrations-Images,
  laeuft einmalig vor dem Rollout (Reihenfolge/kubectl-Befehle als
  Kommentar in der Datei)

Verifiziert: tsc, 253/253 Unit-Tests, Build clean, `npx prisma --version`
laedt prisma.config.ts korrekt.
2026-08-26 22:28:44 +02:00
Nick Szaunig adfbc2ab83 fix: Access-Token wird bei deaktivierten Usern sofort ungueltig
Bisher griff der isActive-Check nur auf Routen mit @RequirePermission.
Ein deaktivierter User mit gueltigem Token konnte Routen ohne
Permission-Decorator bis zum Token-Ablauf (bis zu 15 Min) weiter
erreichen. JwtAuthGuard prueft jetzt PermissionResolverService.isUserActive
auf jedem authentifizierten Request, ueber denselben 30s-Cache wie die
Permission-Aufloesung, also ohne zusaetzliche DB-Last.
2026-08-26 14:17:49 +02:00
Nick SzaunigandClaude Sonnet 5 5a1015c4a0 fix: kritische Rechte-Luecke, Admin-Lockout-Schutz und Wetter-Haertung
Dispatchte security-reviewer und code-reviewer Agenten fuer eine
vollstaendige Pruefung des RBAC-Systems (d31aa79..HEAD). Ein
KRITISCHER Fund, zwei HIGH, ein weiteres MEDIUM, alle behoben:

KRITISCH: Deaktivieren eines Benutzers hat dessen Berechtigungen nicht
entzogen. PermissionResolverService.loadPermissions() prüfte nur den
Rollen-Status, nie User.isActive - ein bereits ausgestelltes Access-
Token behielt volle Rechte bis zum Ablauf (bis zu 15 Minuten), nicht
die vorgesehenen 30 Sekunden. UsersService rief zudem nie
invalidateUser() auf. Beides jetzt behoben; neue dedizierte
permission-resolver.service.spec.ts deckt das ab (existierte bisher
gar nicht - ein Grund, warum die Luecke unbemerkt blieb).

HIGH: Kein Schutz gegen den letzten aktiven Systemrollen-Nutzer
(ADMIN). Man konnte den letzten Admin deaktivieren oder umrollen und
sich damit komplett aussperren - ohne Selbsthilfe-Weg, da
prisma/seed.ts das Seeden ueberspringt, sobald die Admin-E-Mail
bereits existiert. UsersService.deactivate()/.update() pruefen jetzt
per assertNotLastSystemRoleUser(), analog zum bestehenden
Lockout-Schutz in RolesService fuer Systemrollen selbst.

HIGH: Der Open-Meteo-Aufruf in WeatherService hatte kein Timeout und
konnte unbegrenzt haengen. Jetzt mit AbortSignal.timeout(8000) plus
sauberem Fehler-Mapping.

MEDIUM: Trachtprognose fiel bei fehlenden Wetterdaten stillschweigend
auf "MAESSIG" zurueck statt einen eigenen Unbekannt-Zustand zu zeigen
- ein Imker haette eine echte Einschaetzung gesehen, wo keine moeglich
war. Neuer Rating-Wert UNBEKANNT in Backend und Frontend.

LOW: Veralteter Kommentar an PermissionsGuard, der noch den in Phase 3
geloeschten RolesGuard erwaehnte, korrigiert.

Verifiziert: tsc, lint, build auf beiden Projekten gruen. Backend-Unit
245/245 (17 neue Tests fuer die Fixes). Frontend-Unit 14/14. Volle
Backend-e2e-Suite konnte weiterhin nicht laufen (WSL/Postgres von
Windows aus nicht erreichbar, auch nach vollem "wsl --shutdown"-
Neustart) - jeder Fix hat aber gezielte, gruene Unit-Abdeckung.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 14:03:32 +02:00
Nick SzaunigandClaude Sonnet 5 f1d6b2956e fix: Volk-QR-Code fuehrt zur Volk-Uebersicht statt direkt zum Durchsicht-Formular
Scannen des QR-Codes eines Volks landete bisher direkt auf einem
leeren "Neue Durchsicht"-Formular ohne jeglichen Kontext (kein
Status, keine Historie, kein Menue) - erst nach dem Speichern kam man
auf /colonies/[id], die eigentliche Uebersichtsseite mit Status,
Aktionen, Durchsicht-/Varroa-/Schwarmkontroll-/Behandlungs-/
Fuetterungshistorie.

QR fuehrt jetzt direkt zu /colonies/[id]. Der "Neue Durchsicht"-
Button ist dort weiterhin prominent vorhanden. Lastenheft Abschnitt
4.23 erlaubt explizit beide Varianten ("Volkseite beziehungsweise
eine neue Durchsicht"), diese Aenderung bleibt also spezifikations-
konform.

Verifiziert: tsc, lint, unit (228/228, inkl. der angepassten
qr.service.spec.ts-Assertion) gruen. Volle e2e-Suite konnte wegen der
wiederkehrenden WSL/Postgres-Portweiterleitungs-Unzuverlaessigkeit
nicht neu durchlaufen werden - die geaenderte Logik (ein einzelner
String) ist durch den angepassten Unit-Test direkt abgedeckt.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 10:57:57 +02:00
Nick SzaunigandClaude Sonnet 5 ce9cea1b94 feat: Wetterintegration + Trachtprognose pro Bienenstand
Neues WeatherModule: liest Bienenstand-Koordinaten und ruft die freie
Open-Meteo-API ab (kein API-Key nötig), liefert 14 Tage Wetterhistorie
+ 7-Tage-Vorhersage sowie eine heuristische Trachtprognose (Saison +
Wetter der letzten 7 Tage + historischer Ernteschnitt für denselben
Kalendermonat am selben Standort - explizit kein ML-Modell, siehe
TASKS.md Abschnitt 26).

- GET /api/v1/apiaries/:id/weather, GET .../trachtprognose, beide
  ueber die bestehende apiaries:read-Berechtigung abgesichert (kein
  neuer Permission-Key nötig, gleiches Muster wie die bestehenden
  Colony-Sub-Controller).
- Bienenstände ohne hinterlegte Koordinaten liefern 400 statt eines
  stillen Fallbacks.
- test/weather.e2e-spec.ts ruft bewusst die echte Open-Meteo-API auf
  (kostenlos, kein Auth, stabil genug für einen einzelnen Testlauf) -
  validiert damit den echten Response-Vertrag statt nur ein Mock.
- TASKS.md: beide Punkte abgehakt, stale Duplikat-Zeilen im
  Später-Abschnitt bereinigt.

Verifiziert: tsc, lint, build, unit (228/228), volle e2e-Suite
163/163 gruen.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 09:43:08 +02:00
Nick SzaunigandClaude Sonnet 5 ae53f7889e feat: RBAC Phase 3 - LegacyRole-Enum, RolesGuard und @Roles vollstaendig entfernt
Letzte Phase des RBAC-Umbaus: die transitionale Rollen-Infrastruktur,
die seit Phase 1 parallel zum neuen Role/Permission-System mitlief,
ist jetzt komplett weg.

- Prisma-Migration: User.legacyRole-Spalte und LegacyRole-Enum
  gedroppt (nichts liest die Spalte mehr, seit Phase 2 jeder Controller
  auf @RequirePermission laeuft).
- roles.guard.ts, roles.guard.spec.ts, roles.decorator.ts geloescht;
  RolesGuard aus den APP_GUARD-Providern entfernt.
- AuthenticatedUser/JWT-Payload verlieren das role-Feld (permissions
  werden ohnehin frisch pro Request aus der DB aufgeloest, nicht aus
  dem Claim).
- Users-API: CreateUserDto/UpdateUserDto akzeptieren jetzt roleId
  (eine echte Role.id) statt der alten role-Enum-Werte; UsersService
  validiert, dass die Rolle existiert und aktiv ist, bevor sie
  zugewiesen wird.
- Alle 34 e2e-Fixtures und der Seed-Script folgen dem neuen Schema.

Verifiziert: tsc, lint, build, unit (220/220), volle e2e-Suite
159/159 gruen - keine uebersprungenen Tests.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 09:01:13 +02:00
Nick SzaunigandClaude Sonnet 5 0284077d3b feat: RBAC Phase 2e - Verwaltung auf @RequirePermission umgestellt, Coverage-Test aktiviert
Users, Webhooks, Automation, Import, AuditLog: die klassenweiten
@Roles(Role.ADMIN)-Blankets (die gefaehrlichsten der Migration, da sie
JEDE Route der Klasse abdecken) durch pro-Route @RequirePermission
ersetzt. Effektiver Zugriff unveraendert: ADMIN besitzt weiterhin alle
betroffenen Permission-Keys, jede andere Rolle bleibt ausgeschlossen.

Damit ist kein @Roles(...) mehr in der Codebase in Benutzung - alle
23 Controller laufen jetzt vollstaendig ueber @RequirePermission.
test/authorization-coverage.e2e-spec.ts (bisher it.skip mit TODO fuer
"nach Phase 2") ist jetzt scharf geschaltet und bestaetigt: 0 Routen
ohne Rechte-Pruefung, 0 ungueltige Permission-Keys.

Verifiziert: tsc, lint, build, unit (222/222), volle e2e-Suite
159/159 gruen - keine uebersprungenen Tests mehr.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 07:13:04 +02:00
Nick SzaunigandClaude Sonnet 5 8832081973 feat: RBAC Phase 2d - Auswertung & Ausgabe auf @RequirePermission umgestellt
Dashboard, Search, Calendar, Statistics, Qr, Export, Documents:
@Roles(...) durch @RequirePermission(...) ersetzt bzw. neu eingefuehrt
(alle diese Controller hatten zuvor gar keine Rollenpruefung -
jeder eingeloggte Nutzer konnte lesen). Documents behaelt STAFF-Zugriff
auf Upload und Loeschen.

Verifiziert: tsc, lint, volle e2e-Suite 158/159 gruen (1 getrackter Skip).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 07:09:20 +02:00
Nick SzaunigandClaude Sonnet 5 c510304813 feat: RBAC Phase 2c - Ernte & Lager auf @RequirePermission umgestellt
Harvests, Extractions, HoneyBatches, Storage, Products, Bottling,
Equipment: @Roles(...) durch @RequirePermission(...) ersetzt,
ungeschuetzte GET-Routen mit :read-Rechten geschlossen. HoneyBatches
behaelt die feine Unterscheidung relocate (auch STAFF) vs.
create/measurements (nur BEEKEEPER+); Storage bekommt konsequent
keine STAFF-Schreibrechte (deckt sich mit dem bisherigen @Roles-Stand).

Verifiziert: tsc, lint, volle e2e-Suite 158/159 gruen (1 getrackter Skip).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 07:05:32 +02:00
Nick SzaunigandClaude Sonnet 5 cdbdaedb8f feat: RBAC Phase 2b - Betrieb auf @RequirePermission umgestellt
Varroa, SwarmControl, Treatments, Feedings (+ jeweilige
Colony-Sub-Controller): @Roles(...) durch @RequirePermission(...)
ersetzt, ungeschuetzte GET-Routen mit :read-Rechten geschlossen.

Verifiziert: tsc, lint, e2e (varroa/swarm-control/treatments/feedings
einzeln gruen; volle Suite 158/159 gruen, 1 getrackter Skip).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 07:02:15 +02:00
Nick SzaunigandClaude Sonnet 5 f4b174ec56 feat: RBAC Phase 2a - Bienenvolk-Kern auf @RequirePermission umgestellt
Apiaries, Colonies, Queens, Inspections (+ ColonyInspections),
Tasks, TaskTemplates: @Roles(...) durch @RequirePermission(...) ersetzt.
Schliesst dabei die bisher ungeschuetzten GET-Routen (jeder eingeloggte
Nutzer konnte lesen, unabhaengig von seiner Rolle) mit :read-Rechten.
TaskTemplatesController verliert seinen klassenweiten @Roles-Blanket
zugunsten pro-Route-Rechten (gleiche effektive Berechtigung: nur
ADMIN/BEEKEEPER, jetzt ueber task-templates:* statt Rollen-Liste).

Verifiziert: tsc, lint, e2e (apiaries/colonies/queens/inspections/
tasks/task-templates, 29/29 gruen).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 06:57:04 +02:00
Nick SzaunigandClaude Sonnet 5 139ea6fdd7 feat: RBAC Rollen & Rechte (Phase 1 - Datenmodell, Roles-Verwaltung, Guard-Infrastruktur)
Replaces the fixed 5-value Role enum with a database-driven Role/Permission
model: admins can create custom roles with a precise per-resource/action
permission set (83-key code-owned catalog), instead of being locked to
ADMIN/BEEKEEPER/STAFF/READONLY/API_CLIENT.

- New Role/Permission/RolePermission tables; legacy Role enum renamed to
  LegacyRole and kept as User.legacyRole so all existing @Roles() call
  sites keep working unchanged during the migration.
- New RolesController/RolesService (CRUD, permission-set editing,
  deactivate/reactivate) with lockout-prevention invariants: system roles
  (ADMIN) can't be edited or deactivated, and a role still assigned to
  users can't be deactivated.
- New PermissionResolverService (30s-cached, DB-fresh per user) and
  PermissionsGuard + @RequirePermission(), registered as an additional
  APP_GUARD alongside the legacy RolesGuard so both systems run
  side-by-side until controllers are migrated in Phase 2.
- PermissionCatalogSyncService seeds any new catalog entries into the DB
  on boot, so future features only touch the TS catalog.
- New GET /auth/me returns the caller's role and effective permissions.
- Migration hand-written (destructive column rename) with grants derived
  from a fresh grep of every existing @Roles() call site, verified
  against 5 controllers read in full for STAFF's exact write boundary.
- test/roles.e2e-spec.ts covers the new endpoints and invariants end to
  end; test/authorization-coverage.e2e-spec.ts added but skipped until
  Phase 2 gives every controller a @RequirePermission.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 06:52:24 +02:00
Nick Szaunig 147417d7d4 fix: address code-review findings (startup validation, shutdown hooks, health check, deploy config)
Verified each finding against the real codebase first, since the review's
diff was polluted by a stale leftover agent-worktree copy under
.claude/worktrees/ (now removed) that made several already-fixed or
never-broken issues look current (CORS, missing typecheck script, PWA
icons, font CSS, Helmet/Swagger CSP - all confirmed fine live, not
touched).

Real fixes:
- main.ts: fail-fast on missing DATABASE_URL/JWT_ACCESS_SECRET at boot,
  call enableShutdownHooks() so PrismaService.onModuleDestroy() actually
  runs on SIGTERM during k8s rolling deploys, PORT fallback 3000 -> 3001
  to match every other place that documents the port.
- health.controller.ts: log the real underlying DB error instead of
  discarding it, add a 5s timeout to the readiness query so a hung DB
  fails the probe fast instead of blocking indefinitely.
- Frontend Dockerfile: declare ARG/ENV NEXT_PUBLIC_API_URL before the
  build step, since Next.js inlines NEXT_PUBLIC_* vars at build time and
  a runtime-only ConfigMap value never reaches the compiled bundle.
- Backend ingress.yaml: drop the no-op rewrite-target regex for a plain
  prefix path (identical behavior, less confusing).
2026-08-25 23:07:29 +02:00
Nick Szaunig 3f7c4ed404 feat: Jahresbericht-PDF pro Volk/Stand
Neue PDF-Reports im Export-Modul: colony-annual-report (Durchsichten,
Behandlungen, Varroakontrollen, Schwarmkontrolle, Ernten eines Volkes fuer
ein Jahr) und apiary-annual-report (Voelker-Uebersicht + Ernten eines
Stands fuer ein Jahr). Jahr ist optional, faellt auf das aktuelle Jahr
zurueck.

Frontend: "Jahresbericht"-Button auf Volk- und Stand-Detailseite, laedt
das PDF ueber den bestehenden Blob-Download-Mechanismus herunter.
2026-08-25 11:51:41 +02:00
Nick Szaunig f1298eff08 fix: harden auth against brute force, add auth audit logging, fix cookie/TLS gaps
Security review findings (HIGH-1/2/3, MEDIUM):

- Rate limiting via @nestjs/throttler: global 100 req/min default, stricter
  10 req/min on login and refresh. Also closes the MEDIUM finding (public
  batch-lookup enumeration) since it now sits behind the global limit too.
  Trust the K8s ingress's single proxy hop so throttling keys off the real
  client IP, not the ingress pod's IP.
- Auth events (login success/failure, logout, token refresh) are now
  audit-logged via new AuditAction values, closing the gap where the
  project's own audit-log principle never actually covered the Auth module.
- Refresh-token cookie now defaults to Secure=true (opt out only via
  COOKIE_SECURE=false for local HTTP dev), instead of keying off NODE_ENV
  which was never set in the K8s deploy templates. Added a TLS block +
  cert-manager annotation to both the backend and frontend ingress
  templates, which previously had no HTTPS termination configured at all.
2026-08-25 10:49:25 +02:00
Nick Szaunig 1a7331b6b2 feat: Schwarmkontrolle-Protokoll
Neues SwarmControlCheck-Modul: strukturierte Weiselzellenkontrollen pro
Volk mit Zahl/Verdeckelungsstatus der Zellen, Schwarmstimmung, ergriffener
Maßnahme und optionalem naechsten Kontrolltermin.

Frontend: neue Karte auf der Volk-Detailseite, Erfassungsformular unter
/swarm-control/new sowie Quick-Add-Eintrag.
2026-08-25 09:12:50 +02:00
Nick Szaunig 4609a0f9ec feat: Wiederkehrende Aufgaben-Vorlagen (recurring task templates)
Neue TaskTemplate-Entität mit YEARLY/MONTHLY/INTERVAL_DAYS-Wiederholung.
Fällige Aufgaben werden lazy bei GET /tasks erzeugt (Compare-and-Swap auf
nextDueDate), damit mehrere API-Replicas im Kubernetes-Cluster dieselbe
Vorlage nicht doppelt feuern - ohne zusätzliche Cron-Abhängigkeit.

Frontend: Verwaltungsseite unter /settings/task-templates zum Anlegen,
Auflisten und Deaktivieren von Vorlagen.
2026-08-25 05:36:55 +02:00
Nick Szaunig e458a7cf07 feat: Königinnen-Abstammungsbaum (Mutterlinie)
Neue Funktion jenseits der Lastenheft-Checkliste, explizit im Text als
spätere Erweiterung angedacht (§4.4: "ApiCore SOLL später
Abstammungsbäume darstellen können"). Königin.motherQueenId ist eine
neue optionale Selbstreferenz (additive Migration), zusätzlich zu den
bestehenden freien Textfeldern motherLine/fatherLine – letztere
bleiben für Fälle, in denen die Mutter nicht selbst in ApiCore erfasst
ist. Die Vaterlinie bleibt bewusst Freitext, da eine Königin mit vielen
Drohnen aus unterschiedlichen Völkern begattet wird und es keine
einzelne "Vater-Königin" gibt, die referenzierbar wäre.

Backend: GET /queens/:id/lineage baut die mütterliche Ahnenkette
(rekursiv über motherQueenId, Tiefe 8) sowie den Töchterbaum
(rekursiv über die neue daughterQueens-Relation) in einer Antwort;
create/update lehnen eine nicht existierende oder auf sich selbst
zeigende motherQueenId ab.

Frontend: neue "Abstammungsbaum"-Karte auf der Königin-Detailseite
zeigt die mütterliche Linie als Liste sowie die Töchter als
eingerückten Baum, beide mit Links zu den jeweiligen Königinnen.
Anlage- und Bearbeiten-Formular bekommen ein "Mutter"-Auswahlfeld
(Bearbeiten schließt die Königin selbst aus der Liste aus).

Live in Chrome verifiziert: dreistufige Kette (Großmutter → Mutter →
Tochter) korrekt in beide Richtungen dargestellt; dabei einen echten
Anzeigefehler gefunden und behoben – das Mutter-Auswahlfeld zeigte
nach dem Speichern die rohe Datenbank-ID statt des Königin-Codes an
(derselbe SelectValue-ohne-children-Fehler wie zuvor bei der
Import-Entitätsauswahl).
2026-08-25 05:13:45 +02:00
Nick Szaunig 68f459001a feat: Bearbeiten für alle Log-Einträge (Aufgaben, Durchsichten, Behandlungen, Fütterungen, Varroa-Messungen, Ernten, Schleuderungen, Abfüllungen)
Bisher liessen sich diese acht Objekttypen nur anlegen, nie nachträglich
korrigieren – Aufgaben hatten zwar schon einen PATCH-Endpunkt, aber
keine Bearbeiten-UI, die übrigen sieben hatten weder Backend- noch
Frontend-Unterstützung. Jetzt bekommt jeder Typ einen PATCH :id
Endpunkt (UpdateXDto per PartialType(PickType(...)) nach demselben
Muster wie UpdateColonyDto/UpdateApiaryDto, mit Audit-Log-Eintrag
inkl. Vorher-/Nachher-Stand) sowie eine Bearbeiten-UI:

- Aufgaben: Dialog auf der Aufgabenliste
- Durchsichten/Behandlungen/Fütterungen/Varroa-Messungen: Dialog pro
  Listeneintrag auf der Volk-Detailseite
- Ernten: Dialog auf der eigenen Ernte-Detailseite
- Schleuderungen: Dialog direkt in der Rückverfolgbarkeits-Karte der
  Honigcharge (keine eigene Detailseite nötig); Erntezeilen dort
  verlinken jetzt zur Ernte-Detailseite statt sie zu duplizieren
- Abfüllungen: Dialog auf der Abfüllungs-Detailseite, bewusst nur
  Datum/MHD/Losnummer editierbar – Glasgröße/-anzahl bleiben
  unveränderlich, weil sie bereits unumkehrbar die Honigcharge-Menge
  reduziert haben

Relationsstiftende Fremdschlüssel (colonyId, apiaryId/colonyIds,
harvestIds, honeyBatchId) bleiben überall bewusst nicht editierbar,
konsistent mit dem bestehenden apiaryId-Ausschluss bei UpdateColonyDto.
2026-08-25 04:28:37 +02:00
Nick Szaunig 53b781aa04 feat: NFC-Tags beschreiben mit demselben Deep-Link wie der QR-Code
Neuer Endpunkt GET /api/v1/qr/:entityType/:id/link liefert den vom
QR-Dienst bereits genutzten Deep-Link als reine URL statt als SVG,
damit die Web-NFC-API (client-seitig, Chrome/Android) ihn per
NDEFReader.write() auf einen physischen NFC-Tag schreiben kann.
QrService.resolveUrl() ist aus generateSvg() herausgezogen, damit
beide Endpunkte exakt denselben Link erzeugen.

Der bestehende QrCodeDialog bekommt dadurch für alle sechs
QR-fähigen Entitäten (Völker, Bienenstände, Königinnen, Honigchargen,
Lagerbehälter, Abfüllungen) automatisch einen "Auf NFC-Tag
schreiben"-Button, der nur erscheint, wenn window.NDEFReader
tatsächlich existiert – kein Fallback für andere Browser oder iOS,
wie in der Lastenheft vorgesehen.
2026-08-25 01:50:14 +02:00
Nick Szaunig d56550c0e5 feat: öffentliche Herkunftsseiten für Honigchargen + Kunden-QR-Code
Neuer unauthentifizierter Endpunkt GET /api/v1/public/batches/:lot
(Abschnitt 27 der Lastenheft) liefert kontrolliert freigegebene
Rückverfolgbarkeitsdaten einer Abfüllcharge – Produkt, Erntemonat,
Bienenstand-Namen, Schleuderdatum, Wassergehalt – ohne interne IDs,
Adressen oder Koordinaten preiszugeben. Die neue Seite /herkunft/{lot}
liegt außerhalb der (app)-Route-Group und damit ohne Login/Sidebar,
gedacht als Ziel eines QR-Codes auf dem Honigglas. Der bestehende
QR-Dienst kennt jetzt den Entity-Typ "bottling-batches" und verlinkt
intern auf diese neue öffentliche Seite; ein entsprechender
QR-Code-Button samt Klartext-Link steht ab sofort auf der
Abfüllungs-Detailseite bereit.
2026-08-25 01:35:47 +02:00
Nick Szaunig 4f2f31c05f feat(backend): CSV-Import für Stammdaten (Modul 33)
Neues ImportModule mit einem abhängigkeitsfreien CSV-Parser
(src/import/csv.ts, RFC4180-artig: Quoting, escapte Anführungszeichen,
CRLF/LF) passend zum bereits im ExportModule verwendeten
Schreibformat, sodass exportierte CSVs unverändert wieder importierbar
sind.

Endpunkt: POST /import/:entity (multipart, nur Administrator) für
apiaries, colonies, queens. Jede Zeile wird über die exakt gleichen
class-validator-DTOs geprüft, die auch die regulären POST-Endpunkte
nutzen (CreateApiaryDto/CreateColonyDto/CreateQueenDto) – keine
duplizierte Validierungslogik.

Ergebnisbericht pro Zeile (erstellt/übersprungen/Fehler + Meldung):
- Colonies/Queens: Zeilen mit bereits existierendem Code werden
  übersprungen statt einen Fehler zu werfen (idempotenter Re-Import)
- Colonies lösen den Bienenstand über eine "apiaryName"-Spalte auf
  (case-insensitive), da eine CSV keine internen IDs kennen kann;
  unbekannter Name → Fehlerzeile statt Abbruch des gesamten Imports

Unit-Tests für den CSV-Parser (Quoting, escapte Anführungszeichen,
CRLF, leere Zeilen) und den ImportService (alle drei Entitäten,
Validierungsfehler, Duplikat-Skip, unbekannter Bienenstand). E2E-Tests
über echten multipart-Upload (Rollenprüfung, Dateiendungsprüfung, alle
drei Entitäten, Re-Import-Idempotenz).
2026-08-24 23:00:46 +02:00
Nick Szaunig 3f47a6ae8d feat(backend): Automatisierungen mit drei fest hinterlegten Triggern (Modul 30)
Neues AutomationModule mit AutomationRule-Modell (ein Row je Trigger-Typ,
per Unique Constraint erzwungen). Drei fest im Code hinterlegte Trigger,
die Admins nur aktivieren/deaktivieren bzw. deren Konfiguration/Aufgaben-
text anpassen können:

- QUEENLESS_NO_EGGS: Königin nicht gesehen UND keine Stifte vorhanden
  (bei Durchsicht) → Kontrollaufgabe
- VARROA_THRESHOLD: Varroa-Schwellenwert überschritten (konfigurierbar,
  Standard 10 Milben/Tag) → Behandlungsprüfung
- FEED_STATUS_CRITICAL: Futterstatus "kritisch" (bei Durchsicht) →
  Fütterungsaufgabe

Jeder ausgelöste Trigger erzeugt eine Task UND eine Notification sowie
einen Audit-Log-Eintrag; Tasks lösen zusätzlich das bestehende
task.created-Webhook aus. InspectionsService und VarroaService rufen
die Auswertung direkt nach dem jeweiligen Erstellen auf.

Neue Endpunkte (nur Administrator): GET/PATCH /automation-rules.

Unit-Tests für AutomationService (alle drei Trigger, Deaktivierung,
Update mit Audit-Log) sowie E2E-Tests (Rollen-Check, alle drei Trigger
end-to-end über echte Inspection-/Varroa-Endpunkte, Deaktivierung
verhindert weitere Trigger). E2E-Suite zweimal hintereinander grün
verifiziert (Idempotenz der globalen Regel-Zeilen sichergestellt).
Migration auf Dev- und Test-Datenbank angewendet.
2026-08-24 17:25:42 +02:00
Nick Szaunig 59a320ae36 feat(backend): Lagerbehälter-Umlagerung für Honigchargen (Modul 32)
Neues HoneyBatchRelocation-Modell dokumentiert jede Umlagerung einer
Honigcharge zwischen Lagerbehältern (von/nach, Zeitpunkt, Benutzer,
Notiz). HoneyBatchesService.relocate() aktualisiert
HoneyBatch.storageContainerId transaktional zusammen mit dem
Historieneintrag; wirft NotFoundException bei unbekanntem Zielbehälter.

Neue Endpunkte: POST /honey-batches/:id/relocate,
GET /honey-batches/:id/relocations. honeyBatchInclude liefert jetzt
zusätzlich den aktuell zugeordneten storageContainer mit.

Unit- und E2E-Tests ergänzt (Mehrfachumlagerung, Historienreihenfolge,
404 bei unbekanntem Behälter). Migration auf Dev- und Test-Datenbank
angewendet.
2026-08-24 16:55:15 +02:00
Nick Szaunig 57979f0371 feat(backend): activate endpoint for deactivated users
Symmetrisch zu Modul-27/28-Fix fuer Colony/Apiary: kein Weg, einen
versehentlich deaktivierten Benutzer wieder zu aktivieren. Neuer
POST :id/activate-Endpunkt setzt isActive zurueck auf true und
loescht deactivatedAt; ConflictException wenn bereits aktiv.
2026-08-23 11:44:52 +02:00
Nick Szaunig 6346bfab1d feat(backend): reactivate endpoint for dissolved colonies and inactive apiaries
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.
2026-08-23 11:37:06 +02:00
Nick Szaunig 4a9fdd5c71 feat(backend): Dokumente und Bilder mit lokalem Datei-Upload (Modul 29)
DocumentsModule (Document: entityType/entityId, Dateiname, MIME-Typ,
Groesse, Speicherpfad, Hochgeladen-von) fuer Bienenstand/Volk/Koenigin/
Durchsicht/Honigcharge. Upload als multipart/form-data (Multer, 10 MB
Limit, MIME-Allowlist jpeg/png/webp/pdf), Speicherung unter
DOCUMENTS_STORAGE_PATH (austauschbar). ARCHITECTURE.md Annahme 7
dokumentiert die Multi-Replica-Einschraenkung ohne geteiltes Volume.
2026-08-23 11:29:22 +02:00
Nick Szaunig 79010a926b fix(backend): close two SSRF bypasses in webhook URL validation
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.
2026-08-23 11:08:40 +02:00
Nick Szaunig a0f7573af5 fix(backend): block SSRF via webhook subscription URLs
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).
2026-08-23 07:05:53 +02:00
Nick Szaunig 2458927d38 fix(backend): DELETE /webhooks/:id returns 204 No Content
Matches REST convention and the frontend api-client's existing 204
fast-path, avoiding a JSON-parse crash on an empty response body.
2026-08-23 06:58:13 +02:00
Nick Szaunig 59fdc816a0 feat(backend): Webhooks mit HMAC-signierter Auslieferung (Modul 28)
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.
2026-08-23 06:56:56 +02:00
Nick Szaunig e8177432b0 feat(backend): Statistik- und Reporting-Endpunkte (Modul 27)
Aggregations-Endpunkte fuer Voelker, Koeniginnen, Bienenstaende, Varroa,
Honig und Fuetterung nach Lastenheft Abschnitt 4.26.
2026-08-23 06:33:50 +02:00