Commit Graph
100 Commits
Author SHA1 Message Date
Nick SzaunigandClaude Sonnet 5 20981ec1ce fix: release a colony's queen when it is dissolved or merged away
Colony.currentQueenId is unique across colonies. dissolve() and merge()
set status to DISSOLVED/MERGED but never cleared currentQueenId, so a
colony that still had a queen when it was dissolved or merged kept
permanently "owning" her - any later attempt to assign that queen to a
different colony (e.g. requeening another colony with her) would fail
with a unique-constraint conflict, since the dead colony's stale
reference was still occupying that slot.

Both operations now clear currentQueenId and close the open
QueenColonyHistory row for that queen, mirroring the bookkeeping
assignColony() and the queen-status-transition fix already do
elsewhere. The merge survivor keeps its own queen; a merged-away
colony's queen is released, not auto-transferred, since which queen
"wins" a merge is a beekeeper decision.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-08 20:58:53 +02:00
Nick SzaunigandClaude Sonnet 5 cc46fa4d9e fix: mark a colony QUEENLESS when its queen is lost, dies, or is replaced
A very common real-world event - a queen dying over winter, absconding,
being superseded, sold, or replaced - had no side effect on the colony
record at all. Colony.currentQueenId kept pointing at a queen who was
no longer really there, colony.status stayed whatever it was, and the
QUEENLESS dashboard alert could never fire from this path.

QueensService.update() now detects a transition into
LOST/DECEASED/REPLACED/SOLD and, inside the same transaction, clears
currentQueenId on whichever colony currently points at that queen,
flips its status to QUEENLESS (unless the colony is already in a
terminal lifecycle status like DISSOLVED, which is left alone), and
closes the open QueenColonyHistory row - mirroring the bookkeeping
assignColony() already does when a queen changes colonies.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-07 13:30:18 +02:00
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 792806b967 fix: compute mites/day for natural mite fall varroa measurements
VarroaMeasurement.calculatedValue was already used as "Milben/Tag" by
the VARROA_THRESHOLD automation (thresholdMitesPerDay) and the annual
PDF report, but no client ever actually computed it - the frontend
form only collects raw miteCount and durationMinutes. That meant the
threshold check was comparing a raw, un-normalized mite count against
a per-day threshold: e.g. 12 mites counted over 5 days (2.4/day,
healthy) would incorrectly trigger the same alert as 12 mites counted
over 1 day (a real problem).

VarroaService now derives calculatedValue = miteCount / duration in
days for NATURAL_MITE_FALL measurements, on create and (only when no
value exists yet) on update, without ever overwriting an explicitly
provided or already-stored value. Other methods (Puderzucker,
Waschmethode, Drohnenbrutkontrolle) use a different, non-time-based
metric and are left for manual entry rather than guessing an
unverified formula.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-07 12:01:02 +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 8c4ae64e76 docs: record the Wartezeit design assumption in ARCHITECTURE.md
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 02:51:15 +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 81986b3275 feat: seed a starter annual task-template calendar
TaskTemplate had zero seed data - a fresh deployment showed a
completely empty recurring-task list with no hint of what to even put
there. Seeds 12 YEARLY templates, one per month, covering the standard
German-beekeeping annual cycle (Frühjahrsdurchsicht, Schwarmkontrolle,
Einfütterung, Varroa-Restentmilbung/Oxalsäure, Wintersitz-Kontrolle,
etc.) so a new beekeeper gets a ready-made starting point instead of a
blank page. Runs once (guarded by a taskTemplate.count() check, same
idempotency shape as the existing admin-user seed) and only ever adds
rows - admins can edit or deactivate any of them afterward.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-05 17:58:28 +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 ac45514356 fix: remove label cut lines and zero-pad printed dates
Backend Image / build-and-push (push) Successful in 2m26s
Frontend Image / build-and-push (push) Successful in 1m56s
The dashed rectangle drawn around every label slot (a cut/crop line)
wasn't needed and is gone, along with the width/height params that
existed only to draw it. Dates on the label (bottling date, MHD) now
use { day: '2-digit', month: '2-digit' } so single-digit values print
with a leading zero (05.03.2026, not 5.3.2026) - the default 'numeric'
formatting was dropping it.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-05 16:44:25 +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 2f223b6941 feat: Backend-Modul fuer vollstaendiges Daten-Backup (Export/Import)
Neues Modul src/backup/ fuer Backups und Datenuebertragung zwischen
Deployments: GET /backup/export liefert ein JSON-Bundle mit allen
fachlichen Daten (Bienenstaende bis Etikettenvorlagen, ~30 Tabellen),
POST /backup/import spielt so ein Bundle wieder ein.

Bewusst NICHT enthalten: User/Rollen/Rechte (werden ueber die
Benutzerverwaltung gepflegt, Permission-Zeilen werden ohnehin bei
jedem Start aus dem Code neu synchronisiert), Audit-Log (unveraenderliche
Historie), Notifications (fluechtig), Dokumente (nur die DB-Zeile waere
gesichert, nicht die tatsaechliche Datei auf der Festplatte).

Import laeuft in einer Transaktion, Tabellen in Abhaengigkeitsreihenfolge
(z.B. Bienenstand vor Volk, Volk vor Durchsicht). Selbstreferenzen
(Koenigin -> Mutterkoenigin) werden in zwei Durchgaengen aufgeloest,
damit die Reihenfolge im Export keine Rolle spielt. userId-Felder, die
im Zielsystem nicht existieren (z.B. Uebertragung auf ein anderes
Deployment), werden auf den ausfuehrenden Admin umgebogen statt die
Fremdschluessel-Pruefung scheitern zu lassen. AutomationRule wird ueber
triggerType statt id abgeglichen, da diese Zeilen code-fest sind.

Neue Berechtigungen backup:export/backup:import. 8 neue Unit-Tests
fuer die Sonderfaelle (Selbstreferenz, userId-Remapping,
AutomationRule-Abgleich, Audit-Eintrag).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-03 10:22:15 +02:00
Nick SzaunigandClaude Sonnet 5 390ec32f50 feat: Etikettendruck prueft auch gedrehte Anordnung fuer bessere Blattausnutzung
Backend Image / build-and-push (push) Successful in 2m25s
Frontend Image / build-and-push (push) Successful in 2m5s
Im automatischen Modus (Spalten/Zeilen leer) wird jetzt zusaetzlich
berechnet, wie viele Etiketten pro Blatt passen, wenn man sie um 90
Grad dreht (Breite/Hoehe vertauscht). Passt die gedrehte Anordnung mehr
drauf, wird sie verwendet - die Etiketten selbst bleiben inhaltlich
unveraendert (nur auf dem Blatt gedreht), jedes einzelne Etikett bleibt
beim Abziehen normal lesbar.

Bei explizit gesetzten Spalten/Zeilen (z.B. fuer ein vorgedrucktes
Avery-Raster) greift die Drehung nicht - ein reales Blatt hat feste,
nicht drehbare Felder.

Rotation erfolgt via pdfkit-Rotation um die Mitte des Feldes: ein
Etikett, exakt 90 Grad um seinen eigenen Mittelpunkt gedreht, ergibt
immer eine vertauschte Bounding-Box an derselben Stelle - unabhaengig
von der Drehrichtungskonvention. 2 neue Tests belegen ueber die
Seitenzahl, dass die Drehung greift, wenn sie mehr Etiketten
unterbringt, und dass ein explizites Raster unangetastet bleibt.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-03 08:14:51 +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 cd24523ea5 fix: PermissionsGuard blockiert seit dem Fail-Closed-Umbau alle @Public-Routen
Backend Image / build-and-push (push) Successful in 3m14s
Frontend Image / build-and-push (push) Successful in 1m54s
Beim Umstellen von PermissionsGuard auf Fail-Closed wurde der
@Public()-Check vergessen. JwtAuthGuard ueberspringt bei @Public()
zwar die Authentifizierung, aber PermissionsGuard laeuft als
eigenstaendiger, separat registrierter APP_GUARD trotzdem mit - und
verweigerte seitdem jede Route ohne @RequirePermission/
@AnyAuthenticatedUser, selbst wenn sie @Public() war. Betroffen: health
und health/ready (die gemeldeten 403), aber ebenso Login, SSO-Flow und
die oeffentliche Herkunftsseite.

PermissionsGuard prueft jetzt @Public() als erstes und laesst diese
Routen durch, genau wie JwtAuthGuard es bereits tut. Regressionstest
ergaenzt.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 20:46:24 +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 33fa768062 fix: Mindestlaenge fuer JWT_ACCESS_SECRET beim Start erzwungen
Backend Image / build-and-push (push) Successful in 2m13s
Frontend Image / build-and-push (push) Successful in 1m59s
JWT_ACCESS_SECRET war zwar als Pflicht-Env-Var geprueft, aber ohne
Mindestlaenge - ein kurzer/erratbarer Wert haette nicht nur das
Faelschen von Access-/MFA-/SSO-Tokens fuer jeden Nutzer erlaubt,
sondern auch (ueber mfa-crypto.util.ts, das den AES-Schluessel fuer
gespeicherte TOTP-Secrets aus genau diesem Wert ableitet) das
Entschluesseln aller gespeicherten MFA-Secrets.

main.ts bricht den Start jetzt mit klarer Fehlermeldung ab, wenn
JWT_ACCESS_SECRET kuerzer als 32 Zeichen ist. .env.example und das
K8s-Secret-Template zeigen jetzt einen ausreichend langen Platzhalter
mit Erzeugungshinweis (openssl rand -hex 32).

Betrifft nur main.ts's bootstrap() - e2e-Tests bootstrappen ueber
test/utils/create-test-app.ts direkt via configureApp() und durchlaufen
diese Pruefung nicht, daher keine Testanpassung noetig.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 15:17:39 +02:00
Nick SzaunigandClaude Sonnet 5 4bd7e9791a fix: Robustheits-Luecken aus zweitem, unabhaengigem Sicherheitsaudit behoben
Ein zweiter, unabhaengiger Audit-Durchlauf (frischer Blick, keine
Vorkenntnis meiner eigenen Fixes) bestaetigte alle bisherigen Fixes als
korrekt verdrahtet und fand 3 LOW-severity Robustheitsluecken - keine
davon war ausnutzbar, aber alle sind jetzt behoben:

- HarvestsService.create/ExtractionsService.create pruefen jetzt, dass
  referenzierte colonyIds/harvestIds tatsaechlich existieren, bevor die
  Transaktion laeuft - vorher fuehrte eine unbekannte Id zu einem
  ungefangenen 500 mit rohem Prisma-Fehler statt einer sauberen 400.
- Etiketten-Bildelemente durchlaufen jetzt denselben Magic-Byte-Check
  wie Avatar-/Dokument-Uploads, statt pdfkits Bildparser ungepruefte
  Bytes zu uebergeben.
- Webhook-Secret-Redaktion im Audit-Log nutzt jetzt explizites delete
  statt sich auf Prismas JSON-Serialisierung zu verlassen, die
  undefined-Werte verwirft (fragil, falls sich die Serialisierung mal
  aendert).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 15:13:02 +02:00
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 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 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 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 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 80890a5fc3 feat: PostgreSQL als StatefulSet im Kubernetes-Cluster deployen
Backend Image / build-and-push (push) Successful in 3m8s
Frontend Image / build-and-push (push) Successful in 2m54s
Architektur-Entscheidung geaendert: PostgreSQL laeuft jetzt selbst im
Cluster statt auf einer bestehenden externen Instanz. Neu:
deploy/k8s/postgres/ mit StatefulSet (postgres:18-alpine,
volumeClaimTemplates, pg_isready-Probes), headless Service
(apicore-postgres) und Secret-Vorlage. Backend-DATABASE_URL zeigt auf
den neuen In-Cluster-Service. CLAUDE.md/ARCHITECTURE.md aktualisiert:
Backup-Verantwortung liegt jetzt beim Projekt, nicht mehr beim
externen Cluster-/DBA-Betrieb (Backup-CronJob als offener Punkt in
TASKS.md ergaenzt).
2026-08-26 17:43:49 +02:00
Nick Szaunig 41c82d7dc7 fix: Runner-Label und Registry-Push in Gitea-Actions-Workflows korrigieren
Backend Image / build-and-push (push) Successful in 4m44s
Frontend Image / build-and-push (push) Successful in 4m58s
runs-on ubuntu-latest passte auf keinen registrierten Runner (Label
docker-builder erforderlich, siehe funktionierendes Referenzprojekt).
Push laeuft jetzt ueber den cluster-internen Service gitea:80 statt
git.encry.de, da der Cloudflare-Proxy Registry-Blob-Uploads mit "413
Payload Too Large" ablehnt. Auth ueber gitea.actor + REGISTRY_TOKEN
(GITEA_TOKEN hat kein Package-Write-Recht). Image-Pfad wird jetzt
kleingeschrieben (OCI verbietet Grossbuchstaben) - betrifft auch den
Owner-Namen in den K8s-Deployment-Manifesten.
2026-08-26 16:58:28 +02:00
Nick Szaunig 8592051f7e chore: Image-Referenzen in K8s-Deployments auf Gitea-Registry setzen
Frontend Image / build-and-push (push) Canceled after 0s
Backend Image / build-and-push (push) Canceled after 0s
REGISTRY-Platzhalter durch git.encry.de/Nick.Rosenloecher ersetzt,
passend zu den neuen Gitea-Actions-Image-Workflows.
2026-08-26 15:42:45 +02:00
Nick Szaunig a1d3abe6e8 feat: Gitea-Actions-Workflows fuer Backend- und Frontend-Images
Frontend Image / build-and-push (push) Canceled after 0s
Backend Image / build-and-push (push) Canceled after 0s
Baut und pusht bei Push auf main sowie bei Git-Tags (vX.Y.Z) Docker-Images
fuer Backend und Frontend in die Gitea-eigene Container-Registry, zum
Deployen ins Kubernetes-Cluster. Tags: latest (main), Kurz-SHA (immer),
Versions-Tag (bei v*.*.*-Tags).
2026-08-26 15:11:27 +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 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 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 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 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 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
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 63ab0e842d docs: NFC-Tags in TASKS.md abgehakt 2026-08-25 01:50:26 +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 f346859121 docs: Herkunftsseiten + QR-Code in TASKS.md abgehakt 2026-08-25 01:36:08 +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 711536f299 docs: Offline-PWA in TASKS.md abgehakt 2026-08-25 01:14:28 +02:00
Nick Szaunig eb66c4bd53 feat: offline-fähige PWA mit IndexedDB-Sync für Durchsichten
Service Worker (public/sw.js) cached die App-Shell für Seiten-GETs
(network-first, cache-fallback). Durchsichten, die offline gespeichert
werden, landen in einer IndexedDB-Warteschlange (idb) statt zu
scheitern und werden automatisch synchronisiert, sobald wieder
Netzwerk verfügbar ist (Mount + online-Event), mit manuellem Retry
über ein Badge im App-Header. Ein modulweiter In-Flight-Lock in
sync.ts verhindert doppelte Synchronisierung bei gleichzeitigen
Aufrufen (gefunden über React-StrictMode-Doppel-Mount, das ohne den
Lock dieselbe Durchsicht zweimal anlegte).
2026-08-25 01:14:18 +02:00
Nick Szaunig 20239885b4 docs: Kartenansicht in TASKS.md als fertig markiert
Siehe vorherigen Commit fuer Implementierung und Live-Verifikation.
2026-08-25 00:40:39 +02:00
Nick Szaunig aa3a777381 feat(frontend): Kartenansicht für Bienenstände (Später-Liste)
Neue Seite /apiaries/map zeigt alle Bienenstände mit hinterlegten
Koordinaten auf einer Leaflet-Karte (OpenStreetMap-Kacheln), mit
Marker-Popup, das zum jeweiligen Bienenstand verlinkt. Karte wird
per next/dynamic mit ssr:false geladen, da Leaflet auf window/document
zugreift.

Breiten-/Längengrad waren zwar seit dem MVP im Backend-DTO vorhanden,
aber über kein Formular erreichbar – ohne diese Lücke wäre die Karte
dauerhaft leer geblieben. Daher Breitengrad-/Längengrad-Felder auf der
Bienenstand-Anlegen- und Bearbeiten-Seite ergänzt.

Marker-Icons werden aus public/leaflet/ bedient statt aus
node_modules-Bild-Imports, da Turbopack Bilder aus node_modules nicht
als StaticImageData auflöst (führte zu "iconUrl not set"-Laufzeitfehler,
live im Browser gefunden und behoben) – der von Leaflet empfohlene
Workaround für Bundler-Icon-Probleme.

Live verifiziert: Koordinaten speichern, Karte zeigt Marker an
korrekter Position, Popup verlinkt korrekt, Empty State bei fehlenden
Koordinaten, mobile Breite ohne Overflow. Zero Konsolenfehler.
2026-08-25 00:38:08 +02:00
Nick Szaunig 4b9f5c7f47 docs: v1.x-Kette end-to-end live verifiziert, v1.x vollständig abgehakt
Kompletten Ablauf live im Browser durchgeklickt: E2E-Kette-Stand ->
E2E-V-001 -> E2E-Q-001 (zugewiesen) -> Durchsicht -> Behandlung
(Ameisensäure) -> Ernte -> Schleuderung -> Honigcharge E2E-HC-001 ->
Lagerbehälter LB-01 (Umlagerung) -> Abfüllung E2E-LOT-001 -> Produkt
"Sommerhonig 500g". Jeder Schritt mit korrekt verknüpften Daten und
ohne Konsolenfehler bestätigt.

Damit ist die gesamte v1.x-Liste aus TASKS.md abgeschlossen.
2026-08-24 23:31:03 +02:00
Nick Szaunig c50c674e94 docs: Modul 33 (Import) und Build-Grün-Checks in TASKS.md abgehakt
Backend und Frontend beide fertig, getestet und live verifiziert
(siehe die beiden vorherigen Commits). Gesamter Backend- und
Frontend-Build war während der gesamten v1.x-Arbeit durchgehend grün.

Offen bleibt nur noch die abschließende End-to-End-Verifikation der
erweiterten Kernkette (Stand -> Volk -> ... -> Produkt) als
zusammenhängender manueller Durchlauf.
2026-08-24 23:07:42 +02:00
Nick Szaunig dfea017f75 feat(frontend): CSV-Import-Seite mit Ergebnisbericht (Modul 33)
Neue Seite /settings/import (nur Administrator, Sidebar-Link unter
Verwaltung): Datentyp wählen (Bienenstände/Völker/Königinnen), CSV-Datei
hochladen, danach Ergebnisbericht mit Zusammenfassung (erstellt/
übersprungen/Fehler) und Zeile-für-Zeile-Tabelle inkl. Statusbadge und
Fehlermeldung.

Live gegen das Backend verifiziert: Upload einer CSV mit einer gültigen
und einer ungültigen Zeile erzeugt korrekt 1 erstellt / 1 Fehler mit
Validierungsmeldung, der neu angelegte Bienenstand erscheint danach in
der regulären Bienenstände-Liste. Zero Konsolenfehler.
2026-08-24 23:07:07 +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 e051ad956b docs: Modul 30 (Automatisierungen) in TASKS.md als fertig markiert
Backend und Frontend beide fertig, getestet und live verifiziert
(siehe die beiden vorherigen Commits).
2026-08-24 22:47:58 +02:00
Nick Szaunig 5d32026092 feat(frontend): Automatisierungen-Verwaltung + erreichbare Trigger (Modul 30)
Neue Seite /settings/automation-rules (nur Administrator, Sidebar-Link
unter Verwaltung): zeigt alle drei fest hinterlegten Regeln mit
Beschreibung, Aktiv/Inaktiv-Umschalter, editierbarem Aufgabentext und
– nur bei Varroa-Schwellenwert – editierbarem Schwellenwert.

Zusätzlich zwei Lücken im Durchsicht-Formular geschlossen, ohne die
wären zwei der drei Trigger über die Oberfläche nie auslösbar gewesen:
- "Stifte vorhanden"-Checkbox ergänzt (eggsPresent wurde vom Formular
  bisher gar nicht gesendet, obwohl Backend und Typ es unterstützen)
- "Futtervorrat ausreichend"-Checkbox durch ein Select mit
  ausreichend/niedrig/kritisch ersetzt (bisher nur binär, "kritisch"
  war nicht erreichbar)

Live gegen das Backend verifiziert: Speichern einer Durchsicht mit
Königin nicht gesehen + keine Stifte erzeugt automatisch die erwartete
Aufgabe (sichtbar unter /tasks) und eine Benachrichtigung, zero
Konsolenfehler. Aktiv/Inaktiv-Umschalter auf der Automatisierungen-Seite
live getestet.
2026-08-24 17:34:29 +02:00