Commit Graph
105 Commits
Author SHA1 Message Date
Nick SzaunigandClaude Sonnet 5 32f11e5b39 fix: clamp {SEQ:N} width in number-format to prevent resource exhaustion
formatGeneratedNumber() passed the admin-supplied {SEQ:N} width straight
into String.padStart() with no upper bound. A careless or malicious
format value (e.g. {SEQ:999999999}) would try to allocate a
correspondingly huge string - on the backend that runs on every single
honey-batch/bottling creation once such a format is saved, and in the
newly added frontend preview it would run on every keystroke while
typing the format. Caps the effective width at 20 digits, far beyond
any realistic sequence number, in both the backend implementation and
its frontend port so they stay identical.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-07 12:41:49 +02:00
Nick SzaunigandClaude Sonnet 5 1c16db114e feat: show a live example number for active number-format rules
Admins configuring the honey-batch/bottling auto-numbering format
(WH-{YYYY}-{SEQ:4}) had no way to see what it would actually produce
before saving - a typo'd placeholder or unexpected format silently
breaks every batch number from then on. When the rule is active, the
format card now renders the next number it would generate, live as
the format is edited, using the same formatGeneratedNumber logic the
backend uses (ported to the frontend, no shared package between the
two apps) and the real next sequence value (sequenceValue + 1).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-07 12:36:06 +02:00
Nick SzaunigandClaude Sonnet 5 1dddc387ba fix: expose every backend-supported field in all edit forms
Audit requested by the user after finding queens couldn't set
hatchYear: many edit dialogs (and some create forms) were missing
fields the backend DTOs already accept, so that data could never be
entered or corrected anywhere in the UI. Fixes per entity:

- Colony: status (WEAK/QUEENLESS/CONSPICUOUS) was never settable
  anywhere, so the COLONY_ATTENTION dashboard alert could never fire.
  Added as a dropdown restricted to the non-terminal statuses;
  DISSOLVED/SOLD/DECEASED/MERGED stay on their dedicated confirm
  actions, which also set deactivatedAt.
- Apiary: description was missing from the edit form.
- Inspection: 10 fields (weather, temperature, broodAmount,
  broodPattern, openBroodPresent, cappedBroodPresent,
  queenCellsPresent, swarmMood, gentleness, combBehavior,
  honeySuperStatus) existed in the schema/DTOs but nowhere in the UI -
  added to both the create and edit forms.
- Varroa measurement: durationMinutes was on the create form but
  missing from edit.
- Feeding: batchNumber missing from both create and edit.
- Treatment: startDate, endDate, activeIngredient, batchNumber
  missing from both create and edit.
- Task: assignedUserId had no UI anywhere; added to create/edit,
  degrading gracefully if the current user's role lacks users:read.
- Extraction (honey-batch traceability variant): amount was missing,
  while the other edit dialog for the same entity already had it.
- Bottling: productId missing from edit.
- Harvest: grossWeight missing from edit; grossWeight and
  honeySuperCount missing from create.
- Queen: breeder, matingType, matingStatus, inseminationData,
  motherLine, fatherLine added to the edit form (hatchYear/
  markingColor/markingNumber were already fixed in a prior commit).

Left alone deliberately: fields the backend DTO itself excludes via
PickType (e.g. HoneyBatch's jarSize/jarCount/totalAmount, Bottling's
quantities) since mutating those post-hoc would desync inventory
deduction, and Colony's terminal statuses/code which have dedicated
lifecycle actions.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-07 12:20:18 +02:00
Nick SzaunigandClaude Sonnet 5 985ea09556 fix: allow entering a queen's hatch year and marking color/number
The backend has always accepted hatchYear/markingColor/markingNumber
(Queen DTOs, Prisma schema), and the queens list even already renders
a "Schlupfjahr" field - but neither the create nor the edit form ever
exposed inputs for them, so no user could actually set hatchYear
through the UI. That silently broke the OLD_QUEEN dashboard alert
just added: it can only ever fire for colonies whose queen has a
hatchYear, and there was no way to record one.

Both forms now include Schlupfjahr, Zeichenfarbe and Zeichennummer.
Marking color is auto-suggested from the hatch year using the
internationally standardized queen marking color convention (last
digit of the year -> white/yellow/red/green/blue), editable like any
other suggestion.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 21:44:29 +02:00
Nick SzaunigandClaude Sonnet 5 edf13d3ae4 feat: warn on the dashboard when a queen is 3+ years old
Queen.hatchYear was already captured but never used anywhere - no
signal existed for "this colony's queen is overdue for evaluation or
replacement," despite that being one of the most common productivity
and swarming risk factors in beekeeping. Adds an OLD_QUEEN dashboard
alert alongside the existing overdue-task/swarm-check ones, flagging
any active colony whose current queen has completed 3+ seasons.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 18:43:37 +02:00
Nick SzaunigandClaude Sonnet 5 d42f8331f9 feat: warn against harvesting honey during a treatment's Wartezeit
Treatments (Ameisensäure, Oxalsäure, etc.) can require a legally
mandated withdrawal period before honey from that colony may be sold.
Nothing in the app tracked this before, so a beekeeper had no
safeguard against harvesting into an active withdrawal window.

Treatments now optionally record withdrawalPeriodDays (from the
product's Packungsbeilage). The new harvest form fetches each
candidate colony's active withdrawal window and shows an inline
warning naming the product and the date it clears, computed as
max(endDate ?? date) + withdrawalPeriodDays across a colony's
treatments, restricted to windows still in the future.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 02:50:34 +02:00
Nick SzaunigandClaude Sonnet 5 0881a38800 feat: warn on the dashboard when a swarm check is overdue
Schwarmkontrolle already captures nextCheckDate per check, but it was
only ever visible by opening that one colony's tab. During swarm
season a missed follow-up check can mean losing half the colony, so
this now surfaces as a dashboard alert (OVERDUE_SWARM_CHECK) like
overdue tasks already do - only the most recent check per colony is
considered, so a completed follow-up clears the warning.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 02:35:17 +02:00
Nick SzaunigandClaude Sonnet 5 acbf5f16ce fix: add apple-touch-icon for iOS "Add to Home Screen"
iOS Safari ignores the web manifest for home-screen icons entirely and
needs its own apple-touch-icon link, which Next.js only emits when
app/apple-icon.tsx (or a literal apple-icon.png) exists. Without it,
iOS falls back to a screenshot of the open page instead of the brand
mark. Mirrors the existing public/icon.svg design via next/og.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-05 18:06:22 +02:00
Nick SzaunigandClaude Sonnet 5 c4f970f071 feat: add wind speed and a same-day hive-inspection verdict to weather
Temperature and rain were already tracked, but wind matters just as
much for whether it's safe/sensible to open a hive - bees get more
defensive and frames are harder to handle in wind. Adds
wind_speed_10m_max to the Open-Meteo request and a derived
inspectionSuitable flag per day (>=15C, <=1mm rain, <=25km/h wind;
null when a value is missing rather than guessing). The apiary weather
card now shows a clear "good/not good for a Durchsicht today" banner
plus wind alongside each day's forecast.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-05 17:54:31 +02:00
Nick SzaunigandClaude Sonnet 5 1f67cd5548 feat: extend offline queueing to all quick-capture forms
Only Durchsicht had an offline fallback (queue to IndexedDB, sync on
reconnect); Behandlung, Fütterung, Varroamessung, Schwarmkontrolle and
Ernte just failed outright with no signal - exactly the situation a
beekeeper is in at the actual hive. Generalizes lib/offline/db.ts and
sync.ts to six queues (version-bumped IndexedDB schema, existing queued
inspections preserved), wires the same "server rejected it vs. no
network" fallback into all five remaining */new pages, and updates the
sync badge to count/drain all six.

Also: doc-hygiene pass (ARCHITECTURE.md/TASKS.md) to replace the stale
pg_dump/CronJob backup plan with the in-app BackupModule that actually
shipped earlier this session.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-05 17:45:32 +02:00
Nick SzaunigandClaude Sonnet 5 1054a845ef feat: allow a honey batch to be split across multiple storage containers
Backend Image / build-and-push (push) Successful in 3m9s
Frontend Image / build-and-push (push) Successful in 2m45s
A batch that doesn't fit in one container (e.g. 125 kg across 5 x 25 kg
buckets) had no way to be represented - HoneyBatch.storageContainerId
is a single nullable FK. Adds HoneyBatchContainerAllocation as an
additive join table (container + amount per batch), independent of the
existing single-container/relocate() flow which still covers the
simple one-container case unchanged.

- GET/PUT/DELETE /honey-batches/:id/containers[/:containerId], reusing
  the existing honey-batches:relocate permission.
- Assigning a container is an upsert (@@unique on batch+container), and
  the total assigned is validated against the batch's own amount when
  known.
- New "Aufteilung auf mehrere Behälter" card on the honey batch detail
  page, alongside (not replacing) the existing storage card.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 19:44:33 +02:00
Nick SzaunigandClaude Sonnet 5 c91dc9ebf1 fix: add missing Ernten/Schleuderungen list and detail views
The sidebar nav sent both entries straight to their "new" creation
form, with no list page to ever browse what had been created -
Schleuderung creation didn't even redirect to a detail page, just to
/colonies, so a saved extraction left no trace anywhere in the UI.

- New /harvests and /extractions list pages (mirrors the existing
  products/apiaries list pattern).
- New /extractions/[id] detail page + edit dialog, matching the
  harvests one that already existed but had nothing linking to it.
- Sidebar "Ernten"/"Schleuderungen" now point at the list pages instead
  of the creation forms.
- Extraction creation now redirects to its detail page instead of
  /colonies.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 19:44:19 +02:00
Nick SzaunigandClaude Sonnet 5 e133a677ab refactor: reuse the SSO app registration for M365 calendar sync
One Entra enterprise application now covers both interactive login and
the calendar sync's client-credentials Graph access, instead of
requiring a second app registration. GraphClientService reads
OIDC_ISSUER_URL/OIDC_CLIENT_ID/OIDC_CLIENT_SECRET (same as SSO) and
derives the tenant ID from the issuer URL's path, rather than three
dedicated M365_CALENDAR_* env vars. The app registration just needs the
"Calendars.ReadWrite" Application permission added alongside its
existing delegated login scopes.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-04 14:35:18 +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 SzaunigandClaude Sonnet 5 28ae0a2723 fix: lay out herkunft social links in a uniform count-aware grid
2 links center side-by-side, 3 sit in one row, and 4 form a centered
2x2 block - previously flex-wrap could leave an uneven number stranded
awkwardly on its own line.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-03 20:13:18 +02:00
Nick Szaunig 412d033d8d feat: add website social link, fix herkunft origin display, GUI backup schedule, and format-based batch numbering
Backend Image / build-and-push (push) Successful in 2m15s
Frontend Image / build-and-push (push) Successful in 2m17s
- SiteSettings gains websiteUrl (e.g. online shop link) alongside the
  existing social links, editable in Verwaltung > Automatisierung.
- /herkunft/[lot] now shows the apiary's Standort instead of its internal
  Name (so multiple apiaries at one location read as one origin), adds
  small icons per social link, and surfaces previously-fetched-but-unused
  data (Honigsorte, Abfülldatum, stat cards) to make the page a richer
  customer-facing info page.
- Backup schedule (path/cron/retention) is now also configurable via the
  GUI (Verwaltung > Backup), stored in a new BackupSettings singleton;
  an env var always wins over the GUI value per field, and the scheduler
  reloads its cron job live when GUI settings change.
- New Automatisierung rules let an admin define a format (e.g.
  "WH-{YYYY}-{SEQ:4}") that auto-generates HoneyBatch.batchNumber and
  BottlingBatch.lotNumber on creation, with an atomic per-rule sequence
  counter and a manual reset. Manual entry still works when the rule is
  inactive.
2026-09-03 13:37:09 +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 SzaunigandClaude Sonnet 5 d998f611e2 feat: Verwaltungsmenü zu 3 Sammelseiten umgebaut - Benutzer & Rollen, Automatisierung, Backup
Backend Image / build-and-push (push) Successful in 2m17s
Frontend Image / build-and-push (push) Successful in 2m18s
Menü unter "Verwaltung" wie gewünscht neu strukturiert:

- "Benutzer & Rollen" (/settings/users-roles): Benutzerverwaltung und
  Rollen & Rechte als zwei Reiter auf einer Seite (vorher zwei
  getrennte Menüpunkte).
- "Automatisierung" (/settings/automation): Webhooks, Wiederkehrende
  Aufgaben, Etikettenvorlagen und Social Media Links als Reiter
  gebündelt - dazu (eigene Ergänzung, da sonst zwei fast gleich
  benannte Menüpunkte "Automatisierung"/"Automatisierungen"
  nebeneinander gestanden hätten) auch die bestehenden
  Automatisierungsregeln als fünfter Reiter "Regeln".
- "Backup" (/settings/backup): neuer voller Daten-Export/Import
  (Commit zuvor) plus der bisherige CSV-Import, beides auf einer Seite.

Aktiver Reiter wird als ?tab=... in der URL gehalten statt nur im
Client-State, damit z.B. "Speichern" im Etiketten-Designer wieder auf
den richtigen Reiter zurückführt.

Technisch: bestehende Seiteninhalte in Reiter-Komponenten extrahiert
(PageHeader entfernt, Rest unverändert übernommen), Dialog-Unterkomponenten
in die neuen Ordner verschoben (git mv, Historie bleibt erhalten). Die
Etiketten-Editor-Routen (/settings/label-templates/new, /[id]) bleiben
unverändert bestehen, nur der Rücksprung nach dem Speichern zeigt jetzt
auf den neuen Reiter. settings/layout.tsx's Berechtigungsliste um
label-templates:read, site-settings:read, backup:export/import ergänzt -
schließt nebenbei eine bestehende Lücke (eine Rolle mit ausschließlich
diesen Rechten wurde vorher faelschlich von der ganzen
Verwaltungssektion ausgesperrt.

Neuer Reiter-Baustein src/components/tab-switcher.tsx (generisch, nach
Vorbild von ActiveHistoryTabs). sidebar.tsx's NavItem.permission
unterstützt jetzt auch ein Array (irgendeine der Berechtigungen reicht),
fuer die neuen gebündelten Menüpunkte.

Lokal verifiziert: typecheck/lint/test/build fehlerfrei, Build zeigt
alle neuen Routen korrekt, Live-Test im Browser (ohne Login) zeigt
sauberen Redirect zu /login ohne Konsolenfehler.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-03 10:37:22 +02:00
Nick SzaunigandClaude Sonnet 5 1e53758753 fix: proxy.ts redirete staendig zurueck zu /login, obwohl eingeloggt
Backend Image / build-and-push (push) Successful in 2m19s
Frontend Image / build-and-push (push) Successful in 2m2s
Der voran gegangene Fix (proxy.ts nach src/ verschoben, damit Next.js
sie ueberhaupt erkennt) hat eine zweite, bislang tote Stelle live
geschaltet: eine Cookie-Pruefung auf "refreshToken", die zu /login
umleitet, wenn das Cookie fehlt - fuer /settings, /apiaries, /colonies,
/queens.

Dieses Cookie wird vom BACKEND gesetzt (eigene Domain,
apicorebackend.* statt apicore.*, Pfad /api/v1/auth) und ist dem
Browser zufolge nie Teil einer Anfrage an das Frontend - proxy.ts lief
auf der Frontend-Domain und konnte dieses Cookie strukturell nie sehen.
Solange die Datei am falschen Ort lag und nie ausgefuehrt wurde, blieb
das folgenlos; sobald sie lief, hat sie jeden Aufruf dieser Seiten
serverseitig zurueck zu /login geschickt, unabhaengig vom tatsaechlichen
Login-Status.

Die Cookie-Pruefung komplett entfernt statt repariert: es gibt keinen
Weg, ein cross-origin gesetztes Cookie hier sichtbar zu machen, ohne
seinen Schutz (Pfad-Scoping) aufzuweichen. Die echte Session lebt
ohnehin nur clientseitig (Access-Token im Speicher, Refresh-Cookie nur
fuer Backend-Aufrufe) - src/app/(app)/layout.tsx prueft das bereits
korrekt und leitet bei Bedarf weiter. proxy.ts setzt jetzt nur noch die
Security-Header.

Lokal verifiziert: /settings/users ohne jedes Cookie liefert jetzt 200
statt eines Redirects, CSP zeigt weiterhin den korrekten
Laufzeit-API_URL.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-03 01:20:51 +02:00
Nick SzaunigandClaude Sonnet 5 c95e1c4b18 fix: Login komplett kaputt seit dem Security-Header-Commit - CSP blockierte jede API-Anfrage
Backend Image / build-and-push (push) Successful in 2m16s
Frontend Image / build-and-push (push) Successful in 2m1s
Root Cause, zweifach:

1. proxy.ts lag im Projekt-Root statt unter src/ (wo app/ tatsaechlich
   liegt). Next.js hat die Datei dadurch nie erkannt - ueberprueft via
   Build-Output ("Proxy (Middleware)" fehlte komplett) und via
   routes-manifest.json. Das betraf nicht nur die neuen
   Security-Header, sondern auch die schon laenger bestehende
   Login-Redirect-Logik, die dadurch vermutlich nie serverseitig
   gegriffen hat - vorher unbemerkt, weil Client-seitige Auth-Checks
   das kaschiert haben.

2. Die CSP (mit dynamischem connect-src aus API_URL) lag in
   next.config.ts's headers() - die wird beim Docker-Build EINMAL
   aufgeloest und ins Standalone-Output eingefroren, nicht bei jedem
   Container-Start neu gelesen, wie im Code-Kommentar faelschlich
   behauptet. Jedes Deployment bekam dadurch permanent
   "connect-src http://localhost:3001" (den Fallback, da API_URL beim
   Build nicht gesetzt ist) - unabhaengig vom tatsaechlich im Pod
   gesetzten API_URL. Jede Anfrage vom Frontend ans Backend (Login,
   SSO-Status, Herkunftsseite) wurde dadurch von jedem Browser
   CSP-blockiert.

Fix: CSP + Security-Header ziehen nach proxy.ts (jetzt korrekt unter
src/) um - Proxy laeuft mit Node.js-Runtime frisch bei jeder Anfrage,
liest process.env.API_URL also korrekt zur Laufzeit. next.config.ts
behaelt nur noch die statische sw.js-Cache-Control-Regel.

Lokal end-to-end verifiziert: Build zeigt jetzt "ƒ Proxy (Middleware)"
(vorher komplett abwesend), CSP-Header zeigt nach Server-Start einen
distinct getesteten API_URL-Wert korrekt an (nicht mehr den
Localhost-Fallback), und ein echter Login-Klick im Browser schickt die
Anfrage jetzt an die richtige, zur Laufzeit gesetzte Backend-Origin.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 21:44:23 +02:00
Nick SzaunigandClaude Sonnet 5 351f9ef34c feat: Abfüllungs-Archiv und Social-Media-Links auf der Herkunftsseite
Abfüllungen hatten keine Übersichtsseite - die Navigation ging direkt
auf /bottling/new, sodass eine angelegte Abfüllung danach nirgends
mehr sichtbar war. Neue Seite /bottling listet alle Abfüllungen
(Archiv), Klick führt zur bestehenden Detail-/Bearbeiten-Seite.
Navigationslink zeigt jetzt auf die Liste statt direkt auf "Neu".

Neues Modul site-settings (Singleton-Tabelle SiteSettings) für
YouTube-/Instagram-/TikTok-Links, verwaltbar unter Verwaltung > Website
(neue Berechtigung site-settings:read/update). Die öffentliche
Herkunftsseite (/herkunft/[lot]) zeigt die konfigurierten Links jetzt
zusätzlich zur Chargen-Herkunft an - PublicService liefert sie über
das bereits oeffentliche /public/batches/:lotNumber mit aus, keine
zusaetzliche unauthentifizierte Route noetig.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 19:57:29 +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 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 Szaunig 7bd2a3dc3a feat: Sicherheits-Header fuer das Frontend ergaenzt
apicore.encry.de lieferte bisher gar keine Security-Header aus (per
curl bestaetigt) - CSP, X-Frame-Options, X-Content-Type-Options,
Referrer-Policy, HSTS fehlten komplett, waehrend das Backend das
laengst per helmet() macht. Ergaenzt fuer alle Routen. Bewusst ohne
CSP-Nonce (haette dynamic rendering fuer jede Seite erzwungen und
damit die bestehende statische Optimierung gekostet) - stattdessen
script-src/style-src 'unsafe-inline', aber default-src/connect-src/
img-src/object-src/frame-ancestors/form-action/base-uri strikt
eingeschraenkt. Lokal mit dem echten Production-Build (standalone
server + next.config.ts) verifiziert: Login-Seite rendert/hydratisiert
sauber, keine CSP-Verstoesse in der Konsole, Header per curl bestaetigt.
2026-09-02 08:45:56 +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 abe1f6e8cf fix: Etiketten-Import zeigt konkreten Grund statt generischem Fehler
Eine KI-erzeugte JSON ohne Kenntnis des echten Schemas (z.B. eigene
Feldnamen wie xMm/yMm oder erfundene Elementtypen wie rectangle/svg)
scheiterte bisher mit "Datei enthält keine gültige Etikettenvorlage"
ohne Hinweis, was falsch ist. Jetzt wird pro Element benannt, was
fehlt oder welcher Typ nicht unterstützt wird.
2026-09-01 20:13:36 +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 9659b8d266 feat: Etiketten-Editor erheblich verbessert - Import/Export, Zoom, Duplizieren
- Vorlage als JSON exportieren/importieren, damit man sich ein Etikett
  auch per KI erstellen und danach importieren kann
- Zoom-Regler fuer den Canvas (50-200%), da Etikettgroessen stark variieren
- X/Y-Positionsfelder pro Element fuer praezise Platzierung statt nur Drag
- Element duplizieren-Button
- Entf/Backspace loescht das ausgewaehlte Element
2026-09-01 19:21:24 +02:00
Nick Szaunig 55b6dff31b fix: Kontomenü stürzt beim Öffnen ab (Base UI error #31)
Backend Image / build-and-push (push) Successful in 3m9s
Frontend Image / build-and-push (push) Successful in 2m55s
DropdownMenuLabel (rendert Menu.GroupLabel) stand direkt im
DropdownMenuContent statt in einem DropdownMenuGroup - Base UI wirft
dafür beim Öffnen einen ungefangenen Fehler, der den kompletten
React-Baum crasht. Das war die eigentliche Ursache des seit Sitzungsbeginn
gemeldeten "This page couldn't load" beim Klick auf das Profilbild
oben rechts, nicht der Service Worker (der war schon korrekt).
2026-09-01 17:23:39 +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 9282777ac9 Env Update
Backend Image / build-and-push (push) Successful in 3m8s
Frontend Image / build-and-push (push) Successful in 2m24s
2026-08-27 08:32:47 +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 6776bdfe73 fix: Root und 404 leiten unangemeldete User direkt zu /login
Backend Image / build-and-push (push) Successful in 2m52s
Frontend Image / build-and-push (push) Successful in 2m38s
Bisher gingen "/" und unbekannte URLs immer erst zu /dashboard, das
dann bei fehlendem Login zu /login weiterleitete - unnoetiger Umweg.
Beide pruefen jetzt selbst den Auth-Status (gleiches Muster wie
(app)/layout.tsx: waehrend isLoading nichts tun, danach je nach User
direkt zu /dashboard oder /login).
2026-08-26 23:21:06 +02:00
Nick Szaunig b3157484c4 feat: Unbekannte und nicht erlaubte Seiten leiten zum Dashboard um
Backend Image / build-and-push (push) Successful in 2m55s
Frontend Image / build-and-push (push) Successful in 2m23s
- src/app/not-found.tsx: jede nicht existierende Route landet auf
  /dashboard statt der Next.js-Standard-404-Seite (der bestehende
  Auth-Guard in (app)/layout.tsx schickt nicht angemeldete User von
  dort automatisch weiter zu /login).
- settings/layout.tsx: fehlt die Berechtigung fuer den
  Einstellungen-Bereich, wird jetzt zu /dashboard umgeleitet statt nur
  einen Hinweistext anzuzeigen (gleiches Redirect-Muster wie der
  bestehende Login-Guard).

Login-nach-Anmeldung-Redirect zu /dashboard und der Auth-Gate in
(app)/layout.tsx bestanden bereits.
2026-08-26 23:14:47 +02:00
Nick Szaunig 9de123ab32 fix: Root-Route zeigt Next.js-Scaffold statt der App
Backend Image / build-and-push (push) Successful in 3m23s
Frontend Image / build-and-push (push) Successful in 2m30s
src/app/page.tsx war seit dem Projekt-Scaffold nie ersetzt worden -
die echte App laeuft komplett unter (app)/dashboard, (auth)/login etc.,
aber "/" selbst wurde nie verdrahtet und zeigte weiterhin die
create-next-app-Standardseite. / leitet jetzt zu /dashboard weiter;
(app)/layout.tsx uebernimmt von dort den bestehenden Auth-Redirect zu
/login fuer nicht angemeldete User.

Root Cause via systematic-debugging gefunden: Pod lief mit korrektem,
frisch gepushtem Image (kubectl describe bestaetigt), Ursache lag im
App-Code selbst, nicht in Build/Deploy-Pipeline.
2026-08-26 23:05:08 +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 0518a5740f feat: Wetter-Widget auf der Bienenstand-Detailseite
Neue WeatherCard auf /apiaries/[id]: Trachtprognose-Badge mit
Begruendung, 7-Tage-Vorhersagestreifen (Temperatur + Niederschlag).
Zeigt einen Hinweis statt eines Fehlers, wenn fuer den Bienenstand
noch keine Koordinaten hinterlegt sind.

TASKS.md: Wetterintegration + Trachtprognosen jetzt vollstaendig
(Backend aus vorherigem Commit + Frontend hier) abgehakt.

Verifiziert: tsc, lint, build, alle Unit-Tests (14/14) gruen. Live-
Browser-Check war blockiert durch dieselbe wiederkehrende WSL/
Postgres-Portweiterleitungs-Unzuverlaessigkeit wie zuvor in der
Session - die Funktionalitaet ist stattdessen durch die Backend-e2e-
Suite abgedeckt, die echten Postgres UND die echte Open-Meteo-API
aufruft (163/163 gruen).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 09:47:44 +02:00
Nick SzaunigandClaude Sonnet 5 335c572dfc feat: RBAC Phase 5 - Rollenverwaltung im Frontend
Neue /settings/roles-Seite (Aktiv/Historie, Karten-Grid) zum Anlegen,
Bearbeiten, Deaktivieren und Reaktivieren von Rollen mit einer
Rechte-Matrix (nach Bereich gruppierte Checkboxen, Gruppen-Toggle,
Live-Zaehler), passend zum Backend aus Phase 1-3.

- AuthContext liest Rolle/Rechte jetzt ueber GET /auth/me statt aus
  dem JWT (das seit Phase 3 keinen role-Claim mehr traegt); neue
  hasPermission()-Hilfsfunktion ersetzt String-Vergleiche gegen "ADMIN".
- Sidebar zeigt Verwaltungs-Eintraege jetzt einzeln nach Berechtigung
  (statt Alles-oder-Nichts fuer ADMIN), inkl. neuem "Rollen & Rechte"-
  Eintrag.
- Settings-Layout blockt nur noch, wenn gar keine Verwaltungs-
  Berechtigung vorhanden ist, statt stur auf die ADMIN-Rolle zu pruefen.
- Benutzerseite: Rollen-Auswahl laedt jetzt aktive Rollen von
  GET /roles statt einer festen 5-Werte-Liste; sendet roleId statt
  role an POST/PATCH /users.
- Totes jwt.ts (Rolle wurde bislang aus dem Token dekodiert) entfernt.

Live im Browser getestet (Login, Sidebar, Rollen anlegen/bearbeiten/
deaktivieren/reaktivieren, Benutzer-Dialog): dabei einen echten a11y-
Bug gefunden und behoben - Gruppen-Checkboxen fuer mehrwortige
Bereichsnamen ("Wiederkehrende Aufgaben", "Rollen & Rechte") hatten
keinen zugaenglichen Namen, weil die HTML-id ein Leerzeichen enthielt;
jetzt ueber einen Slug erzeugt.

Verifiziert: tsc, lint, build, alle Unit-Tests (14/14) gruen.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 09:27:23 +02:00
Nick Szaunig d31aa79f50 feat: card-grid boxes with Aktiv/Historie split on main list pages
Replace the list-row layout with a responsive box grid (EntityCard):
icon avatar, title, badge, and 1-2 key fields visible at a glance per
box, matching what the user asked for ("Kaestchen" over rows).

Add an Aktiv/Historie tab split (ActiveHistoryTabs) so deactivated or
terminal-status entries move out of the default view instead of mixing
in with active ones:
- Apiaries, Colonies, Queens: split on deactivatedAt (the field the app
  already uses everywhere for "Historie statt Loeschen").
- Task templates: split on the existing active boolean.
- Tasks: OPEN/IN_PROGRESS vs DONE/CANCELLED.
- Honey batches: everything vs SOLD/CONSUMED.
- Products: ACTIVE vs DISCONTINUED.
- Storage containers: everything vs OUT_OF_SERVICE.
Equipment and stock items have no lifecycle field in the schema, so
they keep the plain grid without a Historie tab.

Verified live: deactivating a Bienenstand moves it out of Aktiv into
Historie immediately, both counts update, reactivating moves it back.
2026-08-26 00:25:23 +02:00
Nick Szaunig c586bff18f feat: modernize main list pages from tables to card-based rows
New shared ListItemCard component: icon avatar, title/subtitle, trailing
badges/actions, chevron affordance when linked to a detail page. Applied
to the 9 primary data-list pages (Bienenstaende, Voelker, Koeniginnen,
Honigchargen, Aufgaben, Lager, Produkte, Inventar, Wiederkehrende
Aufgaben), replacing the plain HTML tables with the same rounded-card
list aesthetic already used elsewhere in the app (inspections,
treatments, varroa, etc.), for a consistent look across the whole
product. Admin/settings tables (Benutzer, Webhooks, Audit-Log, Import)
intentionally left as tables per user's scoping choice.
2026-08-25 23:49:31 +02:00
Nick Szaunig faefd47e4b feat: dashboard tiles link to their matching list page
Each stat tile now navigates to /apiaries, /colonies, /queens, /tasks,
/honey-batches or /storage on click instead of being inert.
2026-08-25 23:21:16 +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