Commit Graph
15 Commits
Author SHA1 Message Date
Nick SzaunigandClaude Opus 5.5 c01a38835a feat: platform backbone and field capture, first part (checkpoint)
Platform backbone (plan 2026-10-13-plattform-rueckgrat), reviewed tasks:
- run roles api/worker/job/all with shared startup checks (Task 1)
- schema and migration for DomainEvent, deliveries, JobRun, ThrottlerHit (Task 2)
- audit, notifications and domain events written in the same transaction (Task 3)
- dispatcher with SKIP LOCKED claims, retries and stale release (Task 4)
- worker building blocks: tick, LISTEN/NOTIFY wake-up, health (Task 5)
- worker process and entry points (Task 6)
- throttler counters in Postgres (Task 19)
- separate keys per purpose with key ring (Task 20)
- public origin page without exact location (Task 22)
- shop keys split into read and write (Task 23)
- Kustomize base, components, overlay encry, CI validation (Task 26)

Field capture T3 (plan 2026-10-13-t3-erfassung-am-stand), reviewed tasks:
- brood frames, read endpoints, apiary overview and offline pack
- network basics, offline queue v3, navigation sheets, form building blocks
- new inspection with last-time hints and weather, varroa per day,
  treatment prefill, apiary page with round
- inspection photo (Task 10) is implemented, review pending

Not rolled out. Remaining tasks follow in later commits.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-13 17:57:54 +02:00
Nick SzaunigandClaude Sonnet 5 e7b157ec35 feat: sync calendar entries to a Microsoft 365 group calendar
Backend Image / build-and-push (push) Successful in 8m34s
Frontend Image / build-and-push (push) Successful in 8m41s
Adds a one-way push (ApiCore -> M365) of Aufgaben/Durchsichten/
Behandlungen/Fütterungen/Ernten/Königinnen-Zuordnungen into the calendar
of a Microsoft 365 group, via the Graph API.

- CalendarSyncModule (app-only client-credentials Graph auth, separate
  from the interactive SSO app registration; new env vars
  M365_CALENDAR_TENANT_ID/_CLIENT_ID/_CLIENT_SECRET).
- Outbox + minute-interval cron worker (CalendarSyncOutboxEntry) instead
  of a synchronous push, since Graph calls are too slow/flaky to block a
  domain create request on; failed syncs retry up to 5 times.
- Per-source-type toggle plus a group-ID field, configurable in
  Verwaltung > Automatisierung > Kalender-Sync (Verwaltung > Backup, an
  env var still wins on those fields where one applies to related config).
- Hooked into Tasks/Inspections/Treatments/Feedings/Harvests/Queens at
  their existing create (and Task update/complete) points; entries render
  as all-day Graph events to avoid timezone ambiguity.
- Documents the design decision in ARCHITECTURE.md (Annahme 9), since
  this is a net-new capability the lastenheft doesn't scope.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 09:23:35 +02:00
Nick Szaunig 7b23862a41 feat: expand backup to all data and add automatic scheduled backups
Backend Image / build-and-push (push) Successful in 2m16s
Frontend Image / build-and-push (push) Successful in 1m56s
Backup export/import now covers Role, User, RolePermission,
Notification, AuditLog and Document (with actual file content), not
just business data - matching "wirklich alle Daten". Role/User use
natural keys (role.key, resource+action) so a bundle restores cleanly
into a different database.

Adds BackupSchedulerService (@nestjs/schedule) for cron-based automatic
backups to a local path (BACKUP_STORAGE_PATH), with retention pruning
and a file-mtime guard against duplicate runs across replicas. New
GET /backup/schedule and POST /backup/schedule/run-now endpoints, plus
a status card and manual trigger in the frontend Backup settings page.
2026-09-03 11:33:48 +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 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 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 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 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 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 dd88b6a92c feat(backend): QR-Code-System (Modul 22)
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.
2026-08-22 10:51:42 +02:00
Nick Szaunig c281d04b16 feat(backend): Datenexport-Modul (Modul 21)
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).
2026-08-22 10:05:46 +02:00
Nick Szaunig 370102387f fix(backend): Refresh-Token als httpOnly-Cookie statt Response-Body
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.
2026-08-21 08:27:58 +02:00
Nick Szaunig eae3fea957 feat: Grundgeruest fertigstellen (Backend/Frontend/K8s)
Backend: Prisma-Client auf prisma-client-js + Driver-Adapter (@prisma/adapter-pg)
umgestellt (Prisma 7 verlangt Adapter, ESM-Client brach an Jest), erste Migration
erzeugt und angewendet, /api/v1 Versioning, Swagger unter /api/v1/docs, Helmet,
globale ValidationPipe, PrismaModule, HealthModule (/health, /health/ready mit
DB-Check), tsconfig.build.json Root-Dir-Fix (dist/main.js statt dist/src/main.js).

Frontend: Tailwind v4 + shadcn/ui initialisiert, TanStack Query Provider, PWA
Manifest (app/manifest.ts), Vitest + Testing Library Setup, next.config output
standalone.

deploy/k8s: Dockerfiles (Backend/Frontend, Multi-Stage) + Deployment/Service/
Ingress/ConfigMap/Secret-Vorlagen fuer beide Projekte.

Lokale Dev-/Test-Postgres-Instanz unter ApiCore-Backend/.devdb angelegt (eigene
Rolle, kein bestehendes Passwort noetig), .env/.env.test lokal befuellt.

Lint, Typecheck, Tests und Build sind fuer Backend und Frontend gruen; Server
wurde lokal gestartet und /health, /health/ready, /api/v1/docs manuell verifiziert.
2026-08-21 05:17:26 +02:00
Nick Szaunig c4a2d0648e chore: planning docs + backend/frontend scaffold + MVP Prisma schema
- CLAUDE.md, ARCHITECTURE.md, TASKS.md per Lastenheft-Vorgabe
- ApiCore-Backend: NestJS scaffold, Prisma 7 (DATABASE_URL via prisma.config.ts)
- ApiCore-Frontend: Next.js App Router scaffold (TS, Tailwind, ESLint)
- prisma/schema.prisma: alle MVP-Entitaeten aus Abschnitt 13/4.2-4.22/26,
  validiert und Client generiert

Grundgeruest (TASKS.md #0) noch nicht vollstaendig: shadcn/ui, TanStack Query,
PWA-Manifest, deploy/k8s/, Dockerfiles fehlen noch.
2026-08-21 04:48:16 +02:00