From c294bfddf2657b28fe936d81bb8c09591398c93b Mon Sep 17 00:00:00 2001 From: Schalli Date: Wed, 23 Sep 2026 15:35:30 +0200 Subject: [PATCH] docs(quick-260923-le6): Proxmox-Abnahmebefunde behoben (PMG-Teilsumme, Aktualisieren nur fuer Admins) Uebergabe-Notizen der pausierten Sitzung entfernt, die Arbeit ist wieder aufgenommen. Co-Authored-By: Claude Opus 5.5 (1M context) --- .planning/.continue-here.md | 149 ------------ .planning/HANDOFF.json | 49 ---- .planning/STATE.md | 3 +- .../260923-le6-PLAN.md | 221 ++++++++++++++++++ .../260923-le6-SUMMARY.md | 174 ++++++++++++++ 5 files changed, 397 insertions(+), 199 deletions(-) delete mode 100644 .planning/.continue-here.md delete mode 100644 .planning/HANDOFF.json create mode 100644 .planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-PLAN.md create mode 100644 .planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-SUMMARY.md diff --git a/.planning/.continue-here.md b/.planning/.continue-here.md deleted file mode 100644 index b80378a..0000000 --- a/.planning/.continue-here.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -context: default -phase: null -task: null -total_tasks: null -status: paused -last_updated: 2026-09-23T13:15:42.473Z ---- - - -Kein laufender Meilenstein; die Arbeit laeuft als Quick-Tasks. **Version 1.3.1 ist am -23.09. freigegeben** (Tag `v1.3.1` auf `ad004b2`, Abbilder `live` + `v1.3.1`, Gitea-Release -mit beiden Desktop-Paketen). - -**14 Commits liegen lokal und sind NICHT gepusht.** Arbeitsbaum sauber. Der lokale Stack -laeuft mit dem Proxmox-Stand (web, api, db neu gebaut). - -Unterbrochen wurde NICHT mitten in einer Aufgabe: das Proxmox-Modul ist vollstaendig gebaut, -geprueft und die drei Befunde aus dem Rundgang sind behoben. Offen ist EIN Befund aus der -Abnahme (siehe Restarbeit) und der Push. - - - - -Diese Sitzung (22.09. abends bis 23.09. nachmittags): - -- **quick-260922-vdk** — Dashboard-Raster misst seine Breite auch aus dem Leerzustand heraus. - Meldung kam aus dem **Linux-Client**; Ursache war ein `useEffect(..., [])`, das den - ResizeObserver nie anhaengte, wenn das Dashboard beim Einhaengen leer war. Breite blieb auf - dem Startwert 1200 -> rechts ein toter Streifen. Im echten AppImage gegengemessen. -- **Platte aufgeraeumt** — 3,6 GB frei -> 66 GB. Nur Wegwerf-Sachen (Docker-Build-Cache 27,9 GB, - 58 verwaiste Image-Schichten 43,3 GB, verwaiste CI-Volumes). -- **Freigabe 1.3.1** — CHANGELOG abgeschlossen, `live` vorgezogen, Tag gepusht. -- **quick-260923-ad9** — Dashboard-Reiter: mehrere Dashboards je Benutzer, per Ziehen sortierbar, - der erste ist der Standard. Bestand wandert per Migration in den ersten Reiter. -- **quick-260923-dhh** — Proxmox-Modul (PVE/PBS/PMG), nur beobachten, sieben Aufgaben. -- **quick-260923-ku6** — drei Befunde aus dem Proxmox-Rundgang behoben. - -Jeder Punkt wurde im Browser bzw. im echten Client geprueft; die Protokolle stehen in den -jeweiligen SUMMARY.md unter `.planning/quick/`. - - - - -1. **Offener Befund der Abnahme (zuerst):** `sumOrNull` in - `apps/api/src/proxmox/proxmox-normalize.ts` liefert bei EINEM fehlenden Teilwert die halbe - Summe statt `null` — also einen stillen Falschwert genau dort, wo die Feldnamen am - schlechtesten belegt sind (PMG, Annahme A5). Ein Test fuer „nur eine Haelfte vorhanden“ - fehlt bisher ganz. -2. **Kleinbefund:** „Jetzt aktualisieren“ auf der Proxmox-Modulseite wird allen mit - Modulzugriff gezeigt; der Endpunkt verlangt aber ADMIN/SUPER_ADMIN. Bei anderen passiert - beim Klick nichts. -3. **Push** (14 Commits) und CI abwarten. -4. **Proxmox-Dashboard-Kachel** als eigener kleiner Auftrag ueber den Weg aus 260922-m1h - (`WIDGET_TYPES` + `WIDGET_MODULE_SLUGS` + `registerWidget`). -5. **Freigabe 1.4.0**, sobald Reiter und Proxmox auf alpha bestaetigt sind (neue Funktionen, - also Minor, nicht Patch). - - - - -- **Proxmox: nur beobachten.** Einzige nicht-lesende Anfrage ist die Ticket-Anmeldung; sie liegt - an genau einer Stelle und `proxmox-nur-lesen.spec.ts` zaehlt sie gegen eine benannte Konstante. -- **Keine SSRF-Adresssperre** — Proxmox steht per Definition im internen Netz, eine Sperre wuerde - jede echte Adresse blockieren. Schutz ist, dass nur ein Administrator Adressen eintraegt. -- **PMG nur Benutzer/Passwort** (Recherche A1, Forenbeleg). Faellt beim Test auf, wenn es doch - Token kann — dann nachruesten. -- **Zertifikats-Ausnahme je Serverzeile**, niemals global. -- **Dashboard-Reiter:** der erste Reiter ist der Standard; „als Favorit festlegen“ heisst nach - vorn ziehen. Kein zusaetzliches Kennzeichen. -- **Dashboard-Raster:** belegter Platz bleibt gesperrt, nichts weicht aus. Nutzeransage 22.09. -- **KEIN Umbau der Proxmox-Modulseite.** Der Nutzer hat den Wunsch nach mehr Detailgrad und - Statusfarben am 23.09. ausdruecklich zurueckgezogen („vergiss das letzte, was ich geschrieben - habe“). **Nicht von selbst wieder aufgreifen.** - - - -- Die Proxmox-Feldnamen bei PBS und PMG sind nicht aus erster Quelle belegt (A2/A3/A5). Nur der - Test des Nutzers auf alpha gegen echte Server klaert das. Die Auswertung ist deshalb nachsichtig - gebaut, und jede Feldnamenliste liegt als EINE benannte Konstante in `proxmox-normalize.ts`. -- Live steht moeglicherweise noch auf 1.3.0: der Nutzer hat gezogen, bevor die `live`-Abbilder - fertig waren (Tag-Lauf war zuerst rot). Ein zweiter `pull` steht aus, unbestaetigt. - - -## Required Reading (in order) - -1. `.planning/quick/260923-dhh-proxmox-modul-pve-pbs-und-pmg-anbinden-n/260923-dhh-VERIFICATION.md` - — der offene Befund steht dort mit Reproduktion. -2. `.planning/quick/260923-dhh-.../260923-dhh-RESEARCH.md` — Annahmen-Log A1…A5, die einzige - Stelle, an der steht, was an der Proxmox-Anbindung belegt und was abgeleitet ist. -3. `.planning/quick/260923-ku6-.../260923-ku6-SUMMARY.md` — die Merge-Regel des Verbindungstests - (Formular vs. gespeichert, Geheimnisfelder). -4. `.planning/STATE.md` — Abschnitt „Current Position“ und die letzten Zeilen der Quick-Tabelle. - -## Critical Anti-Patterns (do NOT repeat these) - -- [ANTI-PATTERN]: Im Desktop-Client gegen `getBoundingClientRect()`/`getComputedStyle()` messen → - 260922-vdk: die Werte blieben auf dem ALTEN Stand, obwohl `style.width` bereits richtig war. - WebKitGTK friert die Animationsuhr ein, wenn das Fenster im Hintergrund liegt, und die - `width`-Uebergangsanimation bleibt stehen. **Mitigation:** im Client gegen die GESETZTEN Werte - messen (`style.width`, `style.transform`) oder gegen die React-Eigenschaften aus dem Fiber. -- [ANTI-PATTERN]: Ein Bestaetigungsfenster nur ueber `[role="dialog"]` suchen → der Loeschen-Dialog - der Reiter traegt `role="alertdialog"`; die Pruefung sah „es passiert nichts“, obwohl das Fenster - offen war. **Mitigation:** auf beide Rollen abfragen. -- [ANTI-PATTERN]: Nach dem Tag sofort auf dem Server `pull` ausfuehren → eine Freigabe loest DREI - Pipelines aus; faellt der Tag-Lauf um (bei 1.3.1: ein flackernder Test mit 5-Sekunden-Grenze), - werden Abbild-Bau und Desktop-Pakete UEBERSPRUNGEN, und `pull` holt still den alten Stand. - **Mitigation:** erst pruefen, dass die Abbilder `live`/`beta` einen neuen Zeitstempel tragen, - dann ziehen. Todo zum flackernden Test liegt unter `.planning/todos/pending/`. -- [ANTI-PATTERN]: Eine Vermutung ueber die Ursache aus einem Bildausschnitt ableiten → beim - Raster-Fehler fuehrte der erste Verdacht („eine Nachbarkachel blockiert“) in die Irre; erst das - volle Fenster und das Rueckrechnen der Spaltenbreite (51,5 px, letzte Spalte 13) haben die echte - Ursache gezeigt. **Mitigation:** bei Bildbefunden das GANZE Fenster anfordern, nicht den - Ausschnitt, und die Geometrie nachrechnen. - -## Infrastructure State - -- **Lokaler Stack:** `docker compose` mit web, api, db, gebaut auf dem Proxmox-Stand. - `up` allein baut NICHT neu. DB ohne Host-Port — Prisma vom Host ueber die Container-IP - (`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tessera-ctl-db-1`, - `tessera:tessera_dev`). -- **Proxmox-Nachbau:** Node-Skript mit selbstsigniertem Zertifikat im Scratchpad - (`scratchpad/fake-proxmox.mjs`, Port 8006, aus dem api-Container ueber `172.21.0.1` erreichbar). - Laeuft noch; `/tmp` ueberlebt keinen Neustart, das Skript ist aber schnell neu gebaut. - Endpunkte: `/api2/json/cluster/resources`, `/status/datastore-usage`, - `/admin/datastore/{name}/snapshots`, `/statistics/mail`, `POST /access/ticket`. -- **Lokale Testdaten (duerfen weg):** zwei Proxmox-Server in der DB („Nachbau PVE“ und - „Nachbau kaputth“, letzterer absichtlich unerreichbar), Proxmox im Marktplatz aktiviert, - im Admin-Dashboard ein Reiter „Dashboard“ mit 5 Kacheln. -- **alpha** (`alpha.tessera.ctl.de`): Stand 1.3.1-Beta; Reiter und Proxmox fehlen dort noch, - weil nicht gepusht. -- **Live** (`tessera.ctl.de`): 1.3.1 freigegeben; ob der Server sie wirklich zieht, ist offen. - - -Der rote Faden: erst ein gemeldeter Fehler aus dem Linux-Client (Kachel liess sich nicht ganz -nach rechts ziehen), der sich als Breitenmessung entpuppte; dann Freigabe; dann zwei Wuensche des -Nutzers nacheinander — Dashboard-Reiter und das Proxmox-Modul. Beim Proxmox-Modul ist die -Besonderheit, dass hier KEIN echter Server steht: deshalb wurde ein Nachbau gebaut, die Auswertung -bewusst nachsichtig ausgelegt und jede Feldnamenliste an genau eine Stelle gelegt, damit der -Nutzer sie nach seinem Test auf alpha in einem Griff korrigieren kann. - - - -Start with: den offenen Befund der Abnahme beheben — `sumOrNull` in -`apps/api/src/proxmox/proxmox-normalize.ts` muss `null` liefern, sobald EIN Teilwert fehlt -(heute `(a ?? 0) + (b ?? 0)`), plus ein Test fuer „nur eine Haelfte vorhanden“. Gleich mit -erledigen: „Jetzt aktualisieren“ nur fuer ADMIN/SUPER_ADMIN anzeigen. Danach Tore laufen lassen, -committen, die 14 Commits pushen und die CI abwarten. - diff --git a/.planning/HANDOFF.json b/.planning/HANDOFF.json deleted file mode 100644 index 607feb4..0000000 --- a/.planning/HANDOFF.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "version": "1.0", - "timestamp": "2026-09-23T13:15:42.473Z", - "phase": null, - "phase_name": "kein laufender Meilenstein — Quick-Tasks; zuletzt Proxmox-Modul", - "phase_dir": null, - "plan": null, - "task": null, - "total_tasks": null, - "status": "paused", - "completed_tasks": [ - {"id": 1, "name": "quick-260922-vdk Dashboard-Raster misst seine Breite auch aus dem Leerzustand", "status": "done", "commit": "d9f2af3,cf67c8a,39b1f74"}, - {"id": 2, "name": "Freigabe 1.3.1 (CHANGELOG, live-Zweig, Tag v1.3.1, Gitea-Release, Desktop-Pakete)", "status": "done", "commit": "ad004b2 (Tag v1.3.1)"}, - {"id": 3, "name": "Todo notiert: flackernder Test tenant-selector blockierte die Freigabe", "status": "done", "commit": "fcad460"}, - {"id": 4, "name": "quick-260923-ad9 Dashboard-Reiter (mehrere Dashboards, Ziehen sortiert, erster ist Standard)", "status": "done", "commit": "9c51823,df7a5e7,d34f682,05feaa3,58ce88e,0094a60"}, - {"id": 5, "name": "quick-260923-dhh Proxmox-Modul PVE/PBS/PMG, nur beobachten", "status": "done", "commit": "3a1bfd9,4f8a368,998aba9,fccaf8d,723cf68,06fcdc0,3091b04"}, - {"id": 6, "name": "quick-260923-ku6 drei Befunde aus dem Proxmox-Rundgang behoben", "status": "done", "commit": "710034c,f1bb7f7,a9e0d5b"}, - {"id": 7, "name": "Platte aufgeraeumt (Docker-Build-Cache, verwaiste Images, CI-Volumes)", "status": "done", "commit": null} - ], - "remaining_tasks": [ - {"id": 8, "name": "OFFENER BEFUND der Abnahme: sumOrNull in normalizePmg liefert bei EINEM fehlenden Teilwert die halbe Summe statt null (stiller Falschwert)", "status": "not_started"}, - {"id": 9, "name": "Kleinbefund: 'Jetzt aktualisieren' wird allen mit Modulzugriff gezeigt, der Endpunkt verlangt aber ADMIN — Klick bleibt bei anderen wirkungslos", "status": "not_started"}, - {"id": 10, "name": "Push (14 Commits liegen lokal) und CI abwarten", "status": "not_started"}, - {"id": 11, "name": "Proxmox-Dashboard-Kachel als eigener Auftrag (Weg aus quick-260922-m1h)", "status": "not_started"}, - {"id": 12, "name": "Freigabe 1.4.0, wenn Reiter und Proxmox auf alpha bestaetigt sind", "status": "not_started"} - ], - "blockers": [ - {"description": "Proxmox-Feldnamen bei PBS und PMG sind nicht aus erster Quelle belegt (Annahmen A2/A3/A5) — nur der Test des Nutzers gegen echte Server klaert das", "type": "external", "workaround": "Auswertung ist nachsichtig gebaut: fehlendes/anders benanntes Feld -> 'unbekannt', nie Absturz. Feldnamenlisten liegen je als EINE benannte Konstante in proxmox-normalize.ts"}, - {"description": "Der Nutzer prueft Proxmox selbst auf alpha gegen seine echten Server; hier gibt es keinen echten PVE/PBS/PMG", "type": "human_action", "workaround": "Lokal laeuft ein selbst gebauter Nachbau (HTTPS, selbstsigniert) im Scratchpad — siehe context_notes"} - ], - "async_jobs": [], - "human_actions_pending": [ - {"action": "Live-Server auf 1.3.1 ziehen und in der Seitenleiste 'v1.3.1 · Live' pruefen", "context": "Der Nutzer hat am 23.09. zu frueh gezogen (der Tag-Lauf war wegen eines flackernden Tests rot, die live-Abbilder entstanden erst um 07:37/07:38 nach dem Neustart). Er meldete danach '1.3.0' — ein zweiter pull steht aus, unbestaetigt.", "blocking": false}, - {"action": "Nach dem Push: alpha ziehen und die Dashboard-Reiter ausprobieren", "context": "Reiter liegen seit 0094a60 in der Beta, CI war gruen. Datenbankaenderung laeuft beim API-Start automatisch mit.", "blocking": false}, - {"action": "Proxmox auf alpha einrichten: Server anlegen (Adresse, Token oder Benutzer/Passwort), Zertifikats-Ausnahme setzen, 'Verbindung testen'", "context": "Erst dieser Test klaert die offenen Feldnamen-Annahmen. Nur-Lese-Rolle: PVE PVEAuditor; bei PBS und PMG die jeweils lesende Rolle.", "blocking": false} - ], - "decisions": [ - {"decision": "Proxmox: nur beobachten, keine Eingriffe — einzige nicht-lesende Anfrage ist die Ticket-Anmeldung, maschinell begrenzt durch proxmox-nur-lesen.spec.ts", "rationale": "Ausdrueckliche Nutzeransage 22./23.09.", "phase": null}, - {"decision": "Keine SSRF-Adresssperre nach Vorbild isPublicHttpUrl", "rationale": "Proxmox steht per Definition im internen Netz — eine Sperre wuerde jede echte Adresse blockieren. Schutz ist stattdessen, dass nur ein Administrator Adressen eintraegt.", "phase": null}, - {"decision": "PMG nur Benutzer/Passwort, kein API-Token", "rationale": "Recherche A1: PMG kennt offenbar keine Token (Forenbeleg, keine Primaerquelle). Faellt beim Test auf, wenn es doch geht — dann nachruesten.", "phase": null}, - {"decision": "Zertifikats-Ausnahme je Serverzeile, niemals global", "rationale": "Muster LdapConfig.tlsRejectUnauthorized + undici-Dispatcher aus favorites/icon-discovery.service.ts", "phase": null}, - {"decision": "Dashboard-Reiter: der ERSTE Reiter ist der Standard, 'als Favorit festlegen' = nach vorn ziehen; kein zusaetzliches Kennzeichen", "rationale": "Nutzer praezisierte sich selbst mit 'bzw.' — verschiebbare Reiter, erster ist Standard", "phase": null}, - {"decision": "Dashboard-Raster: belegter Platz bleibt gesperrt, nichts weicht aus (preventCollision bleibt)", "rationale": "Ausdrueckliche Nutzeransage 22.09.: 'Da wo schon ein widget ist, soll keins hinkommen, nichts verschieben.'", "phase": null}, - {"decision": "KEIN Umbau der Proxmox-Modulseite (Detailgrad/Statusfarben)", "rationale": "Der Nutzer hat den Wunsch am 23.09. ausdruecklich zurueckgezogen: 'vergiss das letzte, was ich geschrieben habe'. Nicht von selbst wieder aufgreifen.", "phase": null} - ], - "uncommitted_files": [], - "next_action": "Den offenen Befund der Abnahme beheben: sumOrNull in apps/api/src/proxmox/proxmox-normalize.ts muss null liefern, sobald EIN Teilwert fehlt (heute: (a ?? 0) + (b ?? 0), also halbe Summe als vollstaendige Zahl). Test fuer 'nur eine Haelfte vorhanden' ergaenzen (fehlt heute in proxmox-normalize.spec.ts). Gleich mit erledigen: 'Jetzt aktualisieren' nur fuer ADMIN/SUPER_ADMIN anzeigen. Danach Tore laufen lassen, committen, pushen, CI abwarten.", - "context_notes": "Sitzung vom 22.09. abends bis 23.09. nachmittags. Reihenfolge: Raster-Breitenfehler (im ECHTEN Linux-Client nachgemessen, nicht im Browser) -> Platte aufgeraeumt (3,6 GB frei -> 66 GB) -> Freigabe 1.3.1 -> Dashboard-Reiter -> Proxmox-Modul. 14 Commits liegen lokal und sind NICHT gepusht. Lokale Umgebung: Container laufen mit dem Proxmox-Stand; im lokalen Dashboard des Admin liegt ein Reiter 'Dashboard' mit 5 Kacheln; in der lokalen DB stehen ZWEI Test-Proxmox-Server ('Nachbau PVE' auf https://172.21.0.1:8006 und 'Nachbau kaputth' auf Port 9999, letzterer absichtlich unerreichbar) und das Proxmox-Modul ist im Marktplatz aktiviert — alles Testdaten, duerfen weg. Der Proxmox-Nachbau (Node-Skript + selbstsigniertes Zertifikat) liegt im Scratchpad unter /tmp/claude-1000/-home-vicolab-projects-tessera-ctl/3048d06f-c57f-475e-a181-e61dc35ebe9a/scratchpad/fake-proxmox.mjs und laeuft dort noch; /tmp ueberlebt einen Neustart nicht, das Skript ist aber in 20 Zeilen neu gebaut (Endpunkte: /api2/json/cluster/resources, /status/datastore-usage, /admin/datastore/{name}/snapshots, /statistics/mail, POST /access/ticket)." -} diff --git a/.planning/STATE.md b/.planning/STATE.md index fa80e31..ec3b336 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -7,7 +7,7 @@ status: verified stopped_at: "22.09.2026: 1.3.0 freigegeben; danach quick-260922-hk4 — Bilderrahmen-Bilder liegen jetzt im Dateibereich (user-files) statt in der Datenbank, Umzug laeuft automatisch beim Start, Selbstheilung aus der alten data-Spalte eingebaut; im Browser nachgewiesen. NAECHSTER SCHRITT, vom Nutzer noch nicht bestaetigt: (1) einmaliges Aufraeumen, damit ein Modul seine Dashboard-Kachel selbst mitbringt (heute sieben Hartkodierungen je Kachel; Katalog zeigt auch Kacheln gesperrter Module; gesperrte Kachel bleibt leer statt zu erklaeren) — das Geruest WIDGET_MODULE_MAP existiert und ist leer; (2) danach das Proxmox-Modul (PVE/PBS/PMG) und seine Kachel. Offen beim Nutzer: Live-Server auf 1.3.0 ziehen, neuen Client per Browser installieren." last_updated: "2026-09-23T15:30:00.000Z" last_activity: 2026-09-23 -last_activity_desc: Quick 260923-dhh — Proxmox-Modul gebaut und gegen einen Nachbau geprueft; drei Befunde in 260923-ku6 behoben, ein Befund der Abnahme (PMG-Teilsumme) offen +last_activity_desc: Quick 260923-le6 — letzte Abnahmebefunde zum Proxmox-Modul behoben (PMG-Teilsumme, Aktualisieren-Knopf nur fuer Admins); 1.3.1 laeuft auf live (Nutzer bestaetigt) state_head: 4d485432c003a6caf68f6d85aff7de0bd27794e2 progress: total_phases: 18 @@ -468,6 +468,7 @@ Gerettet aus `.continue-here.md`. Relevant fuer die noch offenen Live-Tests. | 260923-dhh | **Proxmox-Modul (PVE, PBS, PMG) — nur beobachten.** Sieben Aufgaben: Tabellen `ProxmoxServer`/`ProxmoxServerStatus` mit RLS, Zugang verschluesselt per `CryptoService`, undici-Klient mit Dispatcher nur fuer die eingetragene Adresse, Zwischenlager statt Live-Abfrage, Hintergrunddienst je Mandant (`onApplicationBootstrap`, Tender-Muster), Einstellungsseite mit Verbindungstest, Modulseite, Doku. Zugang wahlweise API-Token oder Benutzer/Passwort; **PMG nur Passwort** (Recherche A1: PMG kennt offenbar keine Token). Kopfzeilen-Formate unterscheiden sich je Produkt (`PVEAPIToken=…=…` vs. `PBSAPIToken=…:…`) und liegen an EINER Stelle. **Riegel „nur lesen“ maschinell erzwungen:** `proxmox-nur-lesen.spec.ts` zaehlt die nicht-lesenden Aufrufe gegen eine benannte Konstante — einzige Ausnahme ist die Ticket-Anmeldung. **Keine SSRF-Adresssperre** (Proxmox steht per Definition im internen Netz, eine Sperre wuerde jede echte Adresse blockieren) — Schutz ist, dass nur ein Administrator Adressen eintraegt. **Rundgang gegen einen selbst gebauten Proxmox-Nachbau** (HTTPS, selbstsigniert, echte Antwortformen): Modul im Marktplatz freigeben, Server anlegen, Zertifikatsfehler korrekt benannt, nach gesetzter Ausnahme „Verbindung erfolgreich“, Zahlen der Modulseite exakt wie im Nachbau (18/42 % Last, 3 laufend / 1 gestoppt), unerreichbarer Server meldet „Der Server ist nicht erreichbar“. **Drei Befunde daraus in 260923-ku6 behoben.** **Ein Befund der Abnahme OFFEN:** `sumOrNull` in `normalizePmg` liefert bei EINEM fehlenden Teilwert die halbe Summe statt `null` — stiller Falschwert genau dort, wo die Feldnamen am schlechtesten belegt sind. **Zahlen:** api 1240 → 1311 Tests, web 693 → 708, type-check 4/4, lint 5/5, 53 Warnungen unveraendert. | 2026-09-23 | 3a1bfd9,4f8a368,998aba9,fccaf8d,723cf68,06fcdc0,3091b04 | [260923-dhh-proxmox-modul-pve-pbs-und-pmg-anbinden-n](./quick/260923-dhh-proxmox-modul-pve-pbs-und-pmg-anbinden-n/) | | 260923-ku6 | **Drei Befunde aus dem Proxmox-Rundgang behoben.** (1) „Verbindung testen“ pruefte den GESPEICHERTEN Stand statt der Eingabe — wer den Zugang tippt und vor dem Speichern testet, bekam die Antwort zum alten Wert; jetzt eigene Route `POST servers/test` mit Merge-Regel: normale Felder folgen dem Formular (auch geleert), Geheimnisfelder folgen „leer → gespeicherten Wert behalten“, weil das Formular Geheimnisse nie vorbefuellt. (2) Ein frisch angelegter Server zeigte „Ein unerwarteter Fehler ist aufgetreten“, obwohl nur noch nichts abgefragt war — jetzt eigener ruhiger Zustand mit Verweis auf „Jetzt aktualisieren“. (3) Die Klasse `uppercase` faerbte die ganze Zeile und zeigte die Adresse als „HTTPS://…“ — jetzt nur noch das Produktkuerzel. **Zahlen:** api 1311 → 1316, web 708 → 712, 53 Warnungen gehalten (eine neu ausgeloeste `useOptionalChain`-Warnung gleich mit aufgeloest). | 2026-09-23 | 710034c,f1bb7f7 | [260923-ku6-drei-nachbesserungen-aus-dem-browser-run](./quick/260923-ku6-drei-nachbesserungen-aus-dem-browser-run/) | | 260923-ku6 | **Drei Nachbesserungen aus dem Browser-Rundgang zu 260923-dhh (Proxmox-Modul).** Befund 1 (wichtig): „Verbindung testen" pruefte den gespeicherten Server statt des Formulars — im Formular abgeschaltete Zertifikatspruefung oder ein neu eingetipptes Geheimnis griffen erst nach dem Speichern. Fix: neues `TestProxmoxServerDto` + Merge-Baustein `resolveEffectiveTestServer` in `ProxmoxService`, neue Route `POST servers/test` fuer die Neuanlage (noch kein gespeicherter Server), Geheimnisfelder behalten die bestehende „leer gelassen -> gespeicherten Wert weiterverwenden"-Regel. Befund 2 (wichtig): ein frisch angelegter, nie abgefragter Server zeigte faelschlich „Ein unerwarteter Fehler ist aufgetreten" statt eines ruhigen Hinweises — behoben ueber `status.lastPolledAt === null`. Befund 3 (kosmetisch): `uppercase` faerbte die ganze Statuszeile inkl. Adresse gross — jetzt nur noch das Produktkuerzel. **Zahlen:** api 1311 → 1316, web 708 → 712, type-check 4/4, lint 5/5, Biome web 53 Warnungen unveraendert. | 2026-09-23 | 710034c,f1bb7f7 | [260923-ku6-drei-nachbesserungen-aus-dem-browser-run](./quick/260923-ku6-drei-nachbesserungen-aus-dem-browser-run/) | +| 260923-le6 | **Zwei Abnahmebefunde zum Proxmox-Modul behoben.** (1) `sumOrNull` in `normalizePmg` liefert jetzt `null`, sobald EIN Teilwert (Spam/Viren je Richtung) fehlt — vorher stille Teilsumme als vollstaendige Zahl (Blocker aus 260923-dhh-VERIFICATION, Wahrheit 7). (2) „Jetzt aktualisieren“ nur noch fuer ADMIN/SUPER_ADMIN sichtbar (Endpunkt verlangte das schon); `ServerCard` bekommt `isAdmin`, Nicht-Admins lesen bei nie abgefragtem Server „Die Werte erscheinen nach der naechsten automatischen Abfrage“ statt eines Verweises auf den Knopf. Neuer Seitentest `proxmox-page-roles.test.tsx` (5 Rollenfaelle). Offener Randfall: inaktiver, nie abgefragter Server — Text passt dort nicht ganz, Nutzerentscheidung. Proxmox-Tests api 82, web 26 gruen; Typpruefung beider Seiten fehlerfrei; Biome ohne neue Befunde. | 2026-09-23 | c13d657,2eb86e1,2f8dd14,e1b191b | [260923-le6-proxmox-abnahmebefunde-sumornull-null-be](./quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/) | ## Deferred Items diff --git a/.planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-PLAN.md b/.planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-PLAN.md new file mode 100644 index 0000000..576a669 --- /dev/null +++ b/.planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-PLAN.md @@ -0,0 +1,221 @@ +--- +phase: quick +plan: 260923-le6 +type: execute +wave: 1 +depends_on: [] +files_modified: + - apps/api/src/proxmox/proxmox-normalize.ts + - apps/api/src/proxmox/proxmox-normalize.spec.ts + - apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx + - apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx + - apps/web/src/messages/de.json + - apps/web/src/messages/en.json + - apps/web/src/app/(portal)/modules/proxmox/page.tsx + - apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx +autonomous: true +requirements: [] + +estimate: + tokens: 45000 + raw_tokens: 45000 + tasks: 3 + confidence: low + +must_haves: + truths: + - "PMG: fehlt von einem Paar (spamcount_in/spamcount_out bzw. viruscount_in/viruscount_out) genau eine Haelfte, ist spamCount bzw. virusCount null (Anzeige 'unbekannt') — in beide Richtungen, nie eine Teilsumme" + - "PMG: sind beide Haelften vorhanden, bleibt die Summe wie bisher (10+2 -> 12); fehlen beide, bleibt null" + - "Auf der Proxmox-Modulseite sehen nur ADMIN und SUPER_ADMIN den Knopf 'Jetzt aktualisieren'; USER (und ein noch nicht geladener Benutzer) sehen ihn nicht" + - "Ein noch nie abgefragter Server zeigt Admins weiterhin 'Noch keine Abfrage gelaufen. Klicken Sie oben auf „Jetzt aktualisieren“.'; Nicht-Admins sehen stattdessen einen Text ohne Verweis auf den Knopf" + - "Sonst aendert sich an der Modulseite nichts (Festlegung: kein Umbau, keine zusaetzlichen Details/Statusfarben)" + artifacts: + - path: apps/api/src/proxmox/proxmox-normalize.ts + provides: "sumOrNull liefert null, sobald ein Teilwert null ist" + - path: apps/api/src/proxmox/proxmox-normalize.spec.ts + provides: "Testfaelle 'nur eine Haelfte vorhanden -> null' fuer Spam und Viren, beide Richtungen" + - path: apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx + provides: "optionale Eigenschaft isAdmin (Vorgabe false), waehlt den Hinweistext fuer noch nie abgefragte Server" + - path: apps/web/src/app/(portal)/modules/proxmox/page.tsx + provides: "Aktualisieren-Knopf nur fuer Admins, reicht isAdmin an ServerCard weiter" + - path: apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx + provides: "Seitentest: Knopf sichtbar fuer ADMIN/SUPER_ADMIN, unsichtbar fuer USER/null" + key_links: + - from: "apps/web/src/app/(portal)/modules/proxmox/page.tsx" + to: "ServerCard" + via: "isAdmin={isAdmin}" + pattern: "isAdmin=\\{isAdmin\\}" + - from: "apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx" + to: "apps/web/src/messages/de.json proxmox.card.notPolledYetAutomatic" + via: "t('card.notPolledYetAutomatic')" + pattern: "card\\.notPolledYetAutomatic" +--- + + +Zwei Befunde aus der Abnahme des Proxmox-Moduls beheben, sonst nichts: + +1. **PMG-Teilsumme (API):** `sumOrNull(a, b)` in `apps/api/src/proxmox/proxmox-normalize.ts` addiert heute einen fehlenden Teilwert als 0, sobald nur EINE Haelfte null ist. Dadurch zeigt die Seite z. B. „Spam: 10“ als vollstaendige Tageszahl, obwohl `spamcount_out` fehlte. Das ist der in `.planning/quick/260923-dhh-proxmox-modul-pve-pbs-und-pmg-anbinden-n/260923-dhh-VERIFICATION.md` (Wahrheit 7, Blocker) belegte Fehler. Kuenftig ist die Summe null, sobald ein Teilwert null ist. +2. **Aktualisieren-Knopf nur fuer Admins (Web):** Der Knopf „Jetzt aktualisieren“ erscheint heute bei allen, die Zugriff auf das Modul haben. Der Endpunkt `POST servers/:id/poll` verlangt aber `@Roles(Role.ADMIN, Role.SUPER_ADMIN)` (`apps/api/src/proxmox/proxmox.controller.ts:89-90`), deshalb passiert beim Klick fuer alle anderen nichts. Kuenftig sehen nur ADMIN/SUPER_ADMIN den Knopf. Der Hinweis „Noch keine Abfrage gelaufen. Klicken Sie oben auf …“ darf Nicht-Admins nicht mehr auf einen Knopf verweisen, den sie nicht sehen. + +**Festlegung (locked, vom Nutzer):** KEIN Umbau der Proxmox-Modulseite. Der Nutzer hat seinen Wunsch nach mehr Details bzw. Statusfarben ausdruecklich zurueckgezogen. Nur diese zwei Korrekturen, keine weiteren Anzeige-, Layout- oder Textaenderungen. + +Hinweis zum Zuschnitt: Tracer-first entfaellt (wie `--no-tracer`). Es handelt sich um zwei voneinander unabhaengige Fehlerkorrekturen, jede in genau einer Schicht, ohne neue Architektur, die ein Durchstich absichern muesste. Aufgabe 1 (API) und Aufgabe 2/3 (Web) beruehren keine gemeinsamen Dateien. Aufgabe 3 braucht die Eigenschaft `isAdmin` aus Aufgabe 2. + +Purpose: Das Modul soll keinen still falschen, plausibel aussehenden Wert zeigen (eigener Anspruch in `proxmox-normalize.ts` und `ServerCard.tsx`: „ein still falscher Wert waere schlimmer als ein ehrliches unbekannt“). Ausserdem soll kein Knopf erscheinen, der fuer den Betrachter wirkungslos ist. +Output: korrigierte `sumOrNull` samt Tests; `ServerCard` mit `isAdmin`-Eigenschaft und einem zweiten Hinweistext in de/en; Modulseite, die den Knopf nur Admins zeigt, samt neuem Seitentest. + + + +@~/.claude/gsd-core/workflows/execute-plan.md +@~/.claude/gsd-core/templates/summary.md + + + +@.planning/STATE.md +@./CLAUDE.md +@.planning/quick/260923-dhh-proxmox-modul-pve-pbs-und-pmg-anbinden-n/260923-dhh-VERIFICATION.md + +@apps/api/src/proxmox/proxmox-normalize.ts +@apps/api/src/proxmox/proxmox-normalize.spec.ts +@apps/web/src/app/(portal)/modules/proxmox/page.tsx +@apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx +@apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx +@apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx + + +Bereits im Code vorhanden (vom Planer gelesen, nicht erneut suchen): + +- `apps/web/src/lib/stores/auth-store.ts`: `useAuthStore((s) => s.user)`, `user.role` ist `'SUPER_ADMIN' | 'ADMIN' | 'USER'`. +- `page.tsx` berechnet BEREITS `const isAdmin = user?.role === 'ADMIN' || user?.role === 'SUPER_ADMIN';` (Zeile 18-19) und nutzt es fuer den Einstellungs-Link. Das ist das etablierte Muster; es wird kein neuer Mechanismus eingefuehrt. +- `apps/web/src/lib/proxmox-api.ts`: `listServers(): Promise`, `pollServer(id: string): Promise`, Typ `ProxmoxServer` (inkl. `isActive`, `pollIntervalMin`, `status: ProxmoxServerStatus | null`). +- `ServerCard` wird ausschliesslich in `page.tsx:99` verwendet (per grep geprueft). +- Test-Muster fuer Rollen: `settings-roles.test.tsx` mockt `@/lib/stores/auth-store` mit `useAuthStore: (selector) => mockAuthStore(selector)` und setzt je Fall `mockAuthStore.mockImplementation((sel) => sel({ user }))`, ausserdem `next/link` als `` und `next-intl` mit handgeschriebener Uebersetzungstabelle. +- Nachrichtendateien: nur `apps/web/src/messages/de.json` und `apps/web/src/messages/en.json`. Namensraum `proxmox.card` (de.json ab Zeile 712). `umlaut-guard.spec.ts` prueft de.json auf Ersatzschreibungen (ae/oe/ue/ss) — neue deutsche Texte brauchen echte Umlaute. +- Abfragetakt: `ProxmoxServer.pollIntervalMin` Vorgabe 5, erlaubt 1–1440 (`dto/proxmox-server.dto.ts` `@Min(1) @Max(1440)`). Der Planer laeuft je Mandant im kleinsten Intervall der aktiven Server (`proxmox-scheduler.service.ts`). Inaktive Server (`isActive: false`) werden nicht automatisch abgefragt. + + + + + + + Aufgabe 1: PMG-Summe wird null, sobald eine Haelfte fehlt (sumOrNull) + apps/api/src/proxmox/proxmox-normalize.ts, apps/api/src/proxmox/proxmox-normalize.spec.ts + apps/api/src/proxmox/proxmox-normalize.ts (Zeilen 222-270), apps/api/src/proxmox/proxmox-normalize.spec.ts (Zeilen 185-240) + + - Nur `spamcount_in: 10` vorhanden, `spamcount_out` fehlt -> `spamCount` ist `null` (heute faelschlich 10) + - Nur `spamcount_out: 2` vorhanden, `spamcount_in` fehlt -> `spamCount` ist `null` + - Nur `viruscount_in: 1` vorhanden, `viruscount_out` fehlt -> `virusCount` ist `null` + - Nur `viruscount_out: 3` vorhanden, `viruscount_in` fehlt -> `virusCount` ist `null` + - Eine Haelfte vorhanden, die andere ist nicht lesbar (z. B. `spamcount_out: 'abc'`, `readNumber` liefert null) -> `spamCount` ist `null` + - Unabhaengigkeit der Paare: Spam unvollstaendig, Viren vollstaendig (`viruscount_in: 1, viruscount_out: 0`) -> `spamCount` null, `virusCount` 1; `countIn`/`countOut` bleiben unberuehrt + - Unveraendert gruen: die bestehenden Tests „beide vorhanden -> 12/1“, „beide fehlen -> null“, „HTML -> antwortform“ + + +RED: Im bestehenden `describe('normalizePmg (Aufgabe 3, )', ...)`-Block von `proxmox-normalize.spec.ts` neue Faelle fuer jede Zeile aus `` ergaenzen. Das geht als einzelne `it` oder als `it.each` ueber eine Tabelle {Beschreibung, data, erwartetes spamCount, erwartetes virusCount}. Jeder Testname nennt „nur eine Haelfte vorhanden -> null“ und die Richtung (in bzw. out) sowie Spam bzw. Viren. Vorhandene Tests unveraendert lassen. Der Planer hat geprueft, dass keiner das alte Verhalten festschreibt: Die vorhandenen PMG-Tests decken nur „beide vorhanden“ und „beide fehlen“ ab, und `proxmox.service.spec.ts:350-360` liefert beide Haelften (`spamcount_in: 1, spamcount_out: 0`). Test ausfuehren, die neuen Faelle muessen ROT sein. Commit `test(260923-le6): PMG-Teilsumme ohne Haelfte muss null sein`. + +GREEN: `sumOrNull(a, b)` so aendern, dass es `null` zurueckgibt, sobald `a` ODER `b` `null` ist. Nur wenn beide Zahlen sind, wird ihre Summe zurueckgegeben. Die bisherige Ersatz-durch-Null-Addition entfaellt vollstaendig, ein fehlender Teilwert wird nie mehr als 0 behandelt. Ueber der Funktion einen kurzen deutschen Kommentar ergaenzen (Stil der Datei, ASCII-Umschreibungen wie im Rest der Datei): Eine Tageszahl aus zwei Teilwerten ist nur dann bekannt, wenn beide Teilwerte bekannt sind; eine Teilsumme saehe vollstaendig aus, waere aber still falsch (Abnahmebefund 260923-dhh, Wahrheit 7; PMG-Feldnamen sind nur Annahme A5). `normalizePmg` selbst und die Feldtabelle `PMG_STATS_FIELDS` bleiben unveraendert. Tests muessen GRUEN sein. Commit `fix(260923-le6): PMG-Summe null bei fehlendem Teilwert`. + + + pnpm --filter api exec vitest run src/proxmox + test "$(grep -v '^\s*//' apps/api/src/proxmox/proxmox-normalize.ts | grep -c '?? 0) + (')" -eq 0 + pnpm --filter api type-check + + Alle Tests unter `apps/api/src/proxmox` gruen, darunter mindestens 5 neue Faelle „nur eine Haelfte vorhanden -> null“ (Spam in/out, Viren in/out, nicht lesbare Haelfte). Die Ersatz-durch-Null-Addition steht nicht mehr in `proxmox-normalize.ts`. API-Typpruefung ohne Fehler. + + + + Aufgabe 2: ServerCard waehlt den Hinweistext nach Rolle (isAdmin) und zweiter Text in de/en + apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx, apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx, apps/web/src/messages/de.json, apps/web/src/messages/en.json + apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx (Zeilen 150-215), apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx, apps/web/src/messages/de.json (Zeilen 706-720), apps/web/src/messages/en.json (Zeilen 706-720) + + - `isAdmin` gesetzt, Server nie abgefragt (`status.lastPolledAt === null`) -> Text „Noch keine Abfrage gelaufen. Klicken Sie oben auf „Jetzt aktualisieren“.“ (wie heute) + - `isAdmin={false}`, Server nie abgefragt -> Text „Noch keine Abfrage gelaufen. Die Werte erscheinen nach der nächsten automatischen Abfrage.“, und nirgends in der Karte steht „Jetzt aktualisieren“ + - `isAdmin` weggelassen -> verhaelt sich wie `isAdmin={false}` (sichere Vorgabe) + - Weiterhin in keinem der Faelle die Sammelmeldung „Unerwarteter Fehler.“ + + +Umsetzung der zweiten Korrektur, Teil Karte. + +(a) Nachrichten: In `apps/web/src/messages/de.json` unter `proxmox.card`, direkt nach `notPolledYet`, den neuen Schluessel `notPolledYetAutomatic` mit dem Wert „Noch keine Abfrage gelaufen. Die Werte erscheinen nach der nächsten automatischen Abfrage.“ anlegen. Echtes „ä“ verwenden (umlaut-guard), Sie-Form bzw. unpersoenlich wie die uebrigen App-Texte. In `apps/web/src/messages/en.json` an derselben Stelle `notPolledYetAutomatic`: „No poll has run yet. The values will appear after the next automatic poll.“ Andere Schluessel nicht anfassen; `notPolledYet`, `refresh` und `refreshing` bleiben unveraendert. Begruendung der Wortwahl (Planer-Ermessen, Vorschlag aus dem Auftrag angepasst): Ein „in Kürze“ waere nicht immer wahr, denn das Intervall ist je Server von 1 bis 1440 Minuten einstellbar (`@Max(1440)`). „Nach der nächsten automatischen Abfrage“ stimmt bei jedem Intervall und verweist auf keinen Knopf. + +(b) `ServerCard.tsx`: `ServerCardProps` um die optionale Eigenschaft `isAdmin?: boolean` erweitern und in der Funktionssignatur mit Vorgabe `false` entgegennehmen. Die sichere Vorgabe bedeutet: Wer die Eigenschaft vergisst, zeigt keinen Verweis auf einen Knopf. Im vorhandenen Zweig fuer nie abgefragte Server (`status && !status.lastPolledAt`) den Text nach `isAdmin` waehlen. Ist `isAdmin` wahr, bleibt der heutige Aufruf `t('card.notPolledYet', { refreshLabel: t('card.refresh') })` unveraendert, sonst `t('card.notPolledYetAutomatic')`. Den Kommentar „Nachbesserung Befund 2“ um einen Satz ergaenzen: Nicht-Admins sehen den Knopf nicht (der Poll-Endpunkt verlangt ADMIN/SUPER_ADMIN) und bekommen deshalb den Text ohne Knopfverweis (260923-le6). Sonst NICHTS an der Karte aendern, auch keine Formatierung unbeteiligter Zeilen (Festlegung: kein Umbau). Insbesondere kein `biome format --write` auf die ganze Datei, das wuerde unbeteiligte Zeilen umbrechen. + +(c) `ServerCard.test.tsx`: In die `next-intl`-Mock-Tabelle `'card.notPolledYetAutomatic'` mit dem deutschen Text aus (a) aufnehmen. Den bestehenden Test „Nachbesserung Befund 2: …“ auf `render()` umstellen; seine Erwartungen bleiben. Neue Tests fuer die Faelle aus `` ergaenzen: `isAdmin={false}` sowie weggelassenes `isAdmin` jeweils mit Erwartung des automatischen Textes, `queryByText(/Jetzt aktualisieren/)` ist `null` und `queryByText('Unerwarteter Fehler.')` ist `null`. Zuerst die Tests schreiben und ROT sehen, dann (a)+(b) umsetzen und GRUEN sehen. Ein Commit genuegt: `fix(260923-le6): Proxmox-Karte verweist Nicht-Admins nicht auf den Aktualisieren-Knopf`. + + + pnpm --filter web exec vitest run "src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx" src/messages + node -e "const d=require('./apps/web/src/messages/de.json'),e=require('./apps/web/src/messages/en.json');const a=d.proxmox.card.notPolledYetAutomatic,b=e.proxmox.card.notPolledYetAutomatic;if(!a||!b||/aktualisieren/i.test(a)||/refresh/i.test(b)||!a.includes('nächsten'))process.exit(1);if(d.proxmox.card.notPolledYet!=='Noch keine Abfrage gelaufen. Klicken Sie oben auf „{refreshLabel}“.')process.exit(2)" + + ServerCard-Tests gruen (bestehende und neue Admin-/Nicht-Admin-Faelle); `src/messages`-Tests (umlaut-guard, Paritaet) gruen. `notPolledYetAutomatic` existiert in de und en und erwaehnt keinen Aktualisieren-Knopf. `notPolledYet` ist unveraendert. + + + + Aufgabe 3: Modulseite zeigt „Jetzt aktualisieren“ nur ADMIN/SUPER_ADMIN, mit Seitentest + apps/web/src/app/(portal)/modules/proxmox/page.tsx, apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx + apps/web/src/app/(portal)/modules/proxmox/page.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx (Zeilen 1-100, nur das Mock-Muster) + + - Rolle USER, eine Serverliste mit einem nie abgefragten Server -> kein Knopf mit Namen „Jetzt aktualisieren“; der Karten-Hinweis ist der automatische Text + - Kein Benutzer geladen (`user: null`) -> kein Knopf + - Rolle ADMIN -> Knopf „Jetzt aktualisieren“ sichtbar; der Karten-Hinweis ist der Admin-Text mit Knopfverweis + - Rolle SUPER_ADMIN -> Knopf sichtbar + - Leere Serverliste bei ADMIN -> weiterhin kein Knopf (bestehende Bedingung `servers.length > 0` bleibt) + + +Umsetzung der zweiten Korrektur, Teil Seite. + +(a) Neue Testdatei `apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx` nach dem Muster von `settings-roles.test.tsx` anlegen. Gemockt werden `@/lib/proxmox-api` (`listServers` als `vi.fn()`, der je Fall eine Liste aufloest, und `pollServer` als `vi.fn()`), `@/lib/stores/auth-store` (Selektor-Durchreichung ueber `mockAuthStore`), `next/link` (als ``) und `next-intl`. Die handgeschriebene Uebersetzungstabelle enthaelt mindestens `title`, `description`, `loading`, `loadError`, `emptyState`, `card.refresh`, `card.refreshing`, `card.settingsLink`, `card.unknownValue`, `card.lastPolledLabel`, `card.notPolledYet` (mit `{refreshLabel}`-Ersetzung wie in `ServerCard.test.tsx`) und `card.notPolledYetAutomatic`. Die Seite ueber `import ProxmoxPage from './page'` rendern. Mit `waitFor`/`findByText` auf den Servernamen warten, weil `listServers` asynchron ist. Danach Knopf per `queryByRole('button', { name: 'Jetzt aktualisieren' })` bzw. `getByRole` pruefen. Ein Testfall je Zeile aus ``; der Server im Test ist ein nie abgefragter Server (Status wie im Befund-2-Test von `ServerCard.test.tsx`: `lastPolledAt: null`, `reachable: false`, `errorKind: null`). `afterEach` mit `cleanup()` und `vi.clearAllMocks()`. Test ausfuehren, die USER- und null-Faelle muessen ROT sein. + +(b) `page.tsx`: Die bestehende Bedingung des Aktualisieren-Knopfs (`servers !== null && servers.length > 0`) zusaetzlich an `isAdmin` knuepfen, sodass der Knopf nur fuer ADMIN/SUPER_ADMIN gerendert wird. Die vorhandene Variable `isAdmin` wiederverwenden, keinen neuen Rollen-Mechanismus einfuehren. `handleRefresh` bleibt unveraendert. An der Render-Stelle `` die Eigenschaft `isAdmin={isAdmin}` weiterreichen. Den Kopfkommentar der Komponente um einen Satz ergaenzen: Der Knopf erscheint nur fuer Admins, weil `POST servers/:id/poll` `@Roles(ADMIN, SUPER_ADMIN)` verlangt; fuer andere waere er wirkungslos (260923-le6). Sonst nichts an der Seite aendern: keine neuen Texte, kein Layout, keine Import-Umsortierung. Das vorbestehende organizeImports-Signal von biome in dieser Datei bleibt unangetastet. + +Tests GRUEN sehen. Commit `fix(260923-le6): Aktualisieren-Knopf der Proxmox-Seite nur fuer Admins`. + + + pnpm --filter web exec vitest run "src/app/(portal)/modules/proxmox" + grep -c 'isAdmin={isAdmin}' "apps/web/src/app/(portal)/modules/proxmox/page.tsx" + pnpm --filter web type-check + + Alle Web-Tests im Proxmox-Verzeichnis gruen (ServerCard, ServerForm, neuer Seitentest mit mindestens 5 Faellen: USER, null, ADMIN, SUPER_ADMIN, leere Liste). `page.tsx` reicht `isAdmin={isAdmin}` an `ServerCard` weiter. Web-Typpruefung ohne Fehler. + + + + + +## Trust Boundaries + +| Boundary | Description | +|----------|-------------| +| Browser -> API `POST /proxmox/servers/:id/poll` | Nicht-Admin koennte die Abfrage manuell ausloesen; die Berechtigung prueft ausschliesslich der Server (`@Roles(ADMIN, SUPER_ADMIN)`) | +| PMG-Server -> `normalizePmg` | Fremde, nur angenommene Antwortform (Annahme A5); unvollstaendige Felder duerfen keinen falschen Wert erzeugen | + +## STRIDE Threat Register + +| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan | +|-----------|----------|-----------|----------|-------------|-----------------| +| T-le6-01 | Elevation of Privilege | `POST servers/:id/poll` | low | accept | Das Ausblenden des Knopfes ist reine Oberflaeche, keine Sicherheitsgrenze. Die Durchsetzung bleibt unveraendert serverseitig per `@Roles(Role.ADMIN, Role.SUPER_ADMIN)` in `proxmox.controller.ts:89-90`. Dieser Plan aendert den Controller nicht. | +| T-le6-02 | Tampering (Integritaet der Anzeige) | `sumOrNull` in `normalizePmg` | medium | mitigate | Aufgabe 1: Summe null, sobald ein Teilwert fehlt oder unlesbar ist. Die neuen Tests decken beide Richtungen fuer Spam und Viren ab. | +| T-le6-03 | Information Disclosure | `ServerCard` Hinweistext | low | accept | Der neue Text enthaelt keine Server- oder Zugangsdaten, nur einen statischen Hinweis. | + + + +Nach allen drei Aufgaben (vom Planer an der Ausgangslage 6530ae5 geprueft: alles gruen, `biome lint` sauber): + +- `pnpm --filter api exec vitest run src/proxmox` gruen +- `pnpm --filter web exec vitest run "src/app/(portal)/modules/proxmox" src/messages` gruen +- `pnpm --filter api type-check` und `pnpm --filter web type-check` ohne Fehler +- `pnpm exec biome lint apps/api/src/proxmox/proxmox-normalize.ts apps/api/src/proxmox/proxmox-normalize.spec.ts "apps/web/src/app/(portal)/modules/proxmox/page.tsx" "apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx" "apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx" "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx"` ohne Befund +- `pnpm exec biome check "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx"` ohne Befund (neue Datei, voll konform) +- `biome check` auf den fuenf VORHANDENEN Dateien: vorher 6 Befunde, alle vorbestehend (Formatierung je Datei, dazu organizeImports in `page.tsx`). Deren Anzahl darf nicht steigen. Die vorbestehenden Befunde werden nicht mit behoben, das waere fremder Diff (Festlegung: kein Umbau). +- Keine Container-Neubauten, kein Deploy, keine Browserpruefung in diesem Plan + + + +- Eine PMG-Antwort mit nur einer Haelfte eines Spam- oder Viren-Paares ergibt `null`, die Seite zeigt dort also „unbekannt“ statt einer Teilsumme. +- Auf der Proxmox-Modulseite sehen nur ADMIN und SUPER_ADMIN „Jetzt aktualisieren“. Der Hinweis fuer nie abgefragte Server verweist Nicht-Admins auf die automatische Abfrage statt auf den Knopf. +- Sonst keine sichtbare Aenderung an der Modulseite. +- In der SUMMARY als Beobachtung vermerken, nicht beheben: Inaktive Server (`isActive: false`) werden nicht automatisch abgefragt. Fuer einen inaktiven, nie abgefragten Server stimmt der neue Nicht-Admin-Text deshalb nicht ganz. Das ist ein vorbestehender Randfall, denn auch die Karte fuer Admins beachtet `isActive` heute nicht. Er liegt ausserhalb dieses Auftrags (Festlegung: kein Umbau) und wird dem Nutzer zur Entscheidung vorgelegt. +- In der SUMMARY vermerken, dass damit die offene Luecke (Wahrheit 7) aus `260923-dhh-VERIFICATION.md` geschlossen ist. + + + +Create `.planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-SUMMARY.md` when done + diff --git a/.planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-SUMMARY.md b/.planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-SUMMARY.md new file mode 100644 index 0000000..423c957 --- /dev/null +++ b/.planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-SUMMARY.md @@ -0,0 +1,174 @@ +--- +phase: quick +plan: 260923-le6 +subsystem: proxmox-modul +tags: [nestjs, next-intl, vitest, tdd, proxmox] + +requires: + - phase: quick-260923-dhh + provides: "Proxmox-Modul (PVE/PBS/PMG) inklusive normalizePmg und ServerCard; Abnahmebefund Wahrheit 7 (PMG-Teilsumme) blieb offen" +provides: + - "sumOrNull liefert null, sobald ein Teilwert einer PMG-Summe (Spam/Viren) fehlt oder unlesbar ist — nie mehr eine Teilsumme" + - "ServerCard zeigt Nicht-Admins fuer nie abgefragte Server einen Hinweis ohne Knopfverweis (isAdmin-Eigenschaft, Vorgabe false)" + - "Proxmox-Modulseite zeigt den Knopf 'Jetzt aktualisieren' nur ADMIN/SUPER_ADMIN" +affects: [proxmox-modul, dashboard-kachel-proxmox] + +actuals: + tokens: 4581 + tasks: 3 + commits: 4 +plan_head_before: 6530ae5 + +tech-stack: + added: [] + patterns: + - "isAdmin?: boolean (Vorgabe false) als sichere Eigenschaft fuer UI-Elemente, deren serverseitige Aktion rollenbeschraenkt ist (uebernimmt das bestehende Muster aus page.tsx, kein neuer Mechanismus)" + +key-files: + created: + - apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx + modified: + - apps/api/src/proxmox/proxmox-normalize.ts + - apps/api/src/proxmox/proxmox-normalize.spec.ts + - apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx + - apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx + - apps/web/src/messages/de.json + - apps/web/src/messages/en.json + - apps/web/src/app/(portal)/modules/proxmox/page.tsx + +key-decisions: + - "Wortwahl fuer notPolledYetAutomatic: 'nach der naechsten automatischen Abfrage' statt 'in Kuerze', weil das Poll-Intervall je Server 1-1440 Minuten einstellbar ist und 'in Kuerze' nicht immer zutraefe" + +patterns-established: + - "sumOrNull(a, b): null wenn a ODER b null ist (statt Ersatz-durch-Null) — Muster fuer jede zukuenftige Tageszahl aus zwei Teilwerten" + +requirements-completed: [] + +coverage: + - id: D1 + description: "PMG-Summe (Spam/Viren) ist null, sobald genau eine Haelfte fehlt oder unlesbar ist — in beide Richtungen (in/out), Paare unabhaengig voneinander" + verification: + - kind: unit + ref: "apps/api/src/proxmox/proxmox-normalize.spec.ts#nur eine Haelfte vorhanden -> null (Spam, nur spamcount_in)" + status: pass + - kind: unit + ref: "apps/api/src/proxmox/proxmox-normalize.spec.ts#nur eine Haelfte vorhanden -> null (Spam, nur spamcount_out)" + status: pass + - kind: unit + ref: "apps/api/src/proxmox/proxmox-normalize.spec.ts#nur eine Haelfte vorhanden -> null (Viren, nur viruscount_in)" + status: pass + - kind: unit + ref: "apps/api/src/proxmox/proxmox-normalize.spec.ts#nur eine Haelfte vorhanden -> null (Viren, nur viruscount_out)" + status: pass + - kind: unit + ref: "apps/api/src/proxmox/proxmox-normalize.spec.ts#eine Haelfte ist nicht lesbar -> null (Spam, spamcount_out ist Text)" + status: pass + - kind: unit + ref: "apps/api/src/proxmox/proxmox-normalize.spec.ts#Unabhaengigkeit der Paare: Spam unvollstaendig, Viren vollstaendig" + status: pass + human_judgment: false + - id: D2 + description: "Auf der Proxmox-Modulseite sehen nur ADMIN/SUPER_ADMIN den Knopf 'Jetzt aktualisieren'; USER und ein noch nicht geladener Benutzer sehen ihn nicht; ServerCard verweist Nicht-Admins nicht auf den Knopf" + verification: + - kind: unit + ref: "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx#Rolle USER: kein Knopf" + status: pass + - kind: unit + ref: "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx#kein Benutzer geladen (user: null): kein Knopf" + status: pass + - kind: unit + ref: "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx#Rolle ADMIN: Knopf sichtbar" + status: pass + - kind: unit + ref: "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx#Rolle SUPER_ADMIN: Knopf sichtbar" + status: pass + - kind: unit + ref: "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx#leere Serverliste bei ADMIN: weiterhin kein Knopf" + status: pass + - kind: unit + ref: "apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx#260923-le6: isAdmin={false}, noch nie abgefragt -> automatischer Hinweis ohne Knopfverweis" + status: pass + - kind: unit + ref: "apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx#260923-le6: isAdmin weggelassen -> verhaelt sich wie isAdmin={false}" + status: pass + human_judgment: false + +duration: 21min +completed: 2026-09-23 +status: complete +--- + +# Quick Task 260923-le6: Zwei Abnahmebefunde des Proxmox-Moduls behoben Summary + +**PMG-Teilsumme wird null statt still falsch (sumOrNull), Aktualisieren-Knopf der Proxmox-Modulseite nur noch fuer ADMIN/SUPER_ADMIN sichtbar** + +## Performance + +- **Duration:** 21 min +- **Started:** 2026-09-23T13:12:00Z +- **Completed:** 2026-09-23T13:33:50Z +- **Tasks:** 3 +- **Files modified:** 8 (7 geaendert, 1 neu) + +## Accomplishments +- `sumOrNull(a, b)` in `proxmox-normalize.ts` liefert `null`, sobald ein Teilwert (Spam oder Viren, je Richtung in/out) fehlt oder nicht lesbar ist — die bisherige stille Ersatz-durch-0-Addition ist vollstaendig entfernt. Damit ist Wahrheit 7 (Blocker) aus `260923-dhh-VERIFICATION.md` geschlossen. +- `ServerCard` bekommt die optionale Eigenschaft `isAdmin` (Vorgabe `false`) und zeigt Nicht-Admins fuer einen nie abgefragten Server einen neuen Hinweistext (`proxmox.card.notPolledYetAutomatic`, de/en), der auf keinen Knopf verweist. +- Die Proxmox-Modulseite zeigt den Knopf "Jetzt aktualisieren" nur noch, wenn `isAdmin` wahr ist (bestehende Variable wiederverwendet, kein neuer Rollen-Mechanismus), und reicht `isAdmin` an `ServerCard` weiter. + +## Task Commits + +Alle Aufgaben wurden per TDD (RED -> GREEN) umgesetzt und einzeln committet: + +1. **Aufgabe 1 (RED): PMG-Teilsumme-Tests** - `c13d657` (test) +2. **Aufgabe 1 (GREEN): sumOrNull korrigiert** - `2eb86e1` (fix) +3. **Aufgabe 2: ServerCard mit isAdmin und zweitem Hinweistext** - `2f8dd14` (fix) +4. **Aufgabe 3: Aktualisieren-Knopf nur fuer Admins** - `e1b191b` (fix) + +_Hinweis: Aufgabe 1 hatte planmaessig zwei Commits (RED/GREEN); Aufgaben 2 und 3 wurden je in einem Commit umgesetzt, wie im Plan vorgesehen (Tests zuerst rot gesehen, dann implementiert, ein Commit je Aufgabe)._ + +## Files Created/Modified +- `apps/api/src/proxmox/proxmox-normalize.ts` - `sumOrNull` liefert `null` bei fehlendem Teilwert statt Ersatz-durch-0 +- `apps/api/src/proxmox/proxmox-normalize.spec.ts` - 6 neue Testfaelle fuer beide Richtungen (Spam/Viren), unlesbare Haelfte, Unabhaengigkeit der Paare +- `apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx` - neue `isAdmin`-Eigenschaft (Vorgabe `false`), waehlt den Hinweistext fuer nie abgefragte Server +- `apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx` - bestehenden Test auf `isAdmin` umgestellt, zwei neue Faelle (`isAdmin={false}`, weggelassen) +- `apps/web/src/messages/de.json` / `en.json` - neuer Schluessel `proxmox.card.notPolledYetAutomatic` +- `apps/web/src/app/(portal)/modules/proxmox/page.tsx` - Knopf nur bei `isAdmin`, reicht `isAdmin={isAdmin}` an `ServerCard` weiter +- `apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx` (neu) - Seitentest mit 5 Faellen (USER, `user: null`, ADMIN, SUPER_ADMIN, leere Liste bei ADMIN) + +## Decisions Made +- Formulierung "Noch keine Abfrage gelaufen. Die Werte erscheinen nach der naechsten automatischen Abfrage." statt eines "in Kuerze"-Hinweises, weil das Poll-Intervall je Server zwischen 1 und 1440 Minuten liegen kann (`@Max(1440)`) — die gewaehlte Formulierung stimmt bei jedem Intervall und verweist auf keinen Knopf. +- Keine weiteren Aenderungen an der Modulseite (Festlegung des Nutzers: kein Umbau, keine zusaetzlichen Details oder Statusfarben) — bestaetigt eingehalten. + +## Deviations from Plan + +None - plan genau wie geschrieben ausgefuehrt. + +## Issues Encountered +None. + +## Beobachtungen (nicht behoben, dem Nutzer zur Entscheidung vorgelegt) +- **Inaktive Server:** Ein inaktiver, nie abgefragter Server (`isActive: false`) wird nicht automatisch abgefragt (`proxmox-scheduler.service.ts` fragt nur aktive Server ab). Der neue Nicht-Admin-Hinweistext "...erscheinen nach der naechsten automatischen Abfrage" trifft fuer diesen Randfall nicht ganz zu. Das ist ein vorbestehender Randfall — auch die Admin-Karte beachtet `isActive` heute nicht — und liegt ausserhalb dieses Auftrags (Festlegung: kein Umbau). Wird hier nur vermerkt, nicht behoben. + +## Verifikation (alle gruen, wie im Plan verlangt) +- `pnpm --filter api exec vitest run src/proxmox` - 82 Tests gruen (26 in `proxmox-normalize.spec.ts`, davon 6 neu) +- `pnpm --filter web exec vitest run "src/app/(portal)/modules/proxmox" src/messages` - 32 Tests gruen +- `pnpm --filter api type-check` und `pnpm --filter web type-check` - ohne Fehler +- `biome lint` auf den 6 Plan-Dateien - ohne Befund +- `biome check` auf der neuen Datei `proxmox-page-roles.test.tsx` - ohne Befund (nach `biome check --write` fuer Formatierung) +- `biome check` auf den 5 vorbestehenden Dateien - weiterhin genau 6 Befunde (vorbestehende Formatierung + `organizeImports` in `page.tsx`), keine neuen Befunde — wie im Plan festgelegt nicht behoben (fremder Diff) +- Keine Container-Neubauten, kein Deploy, keine Browserpruefung — wie im Plan vorgesehen + +## User Setup Required +None - keine externe Konfiguration noetig. + +## Next Phase Readiness +- Die offene Luecke (Wahrheit 7) aus `260923-dhh-VERIFICATION.md` ist geschlossen; das Proxmox-Modul hat keine bekannten offenen Abnahmebefunde mehr. +- Offen beim Nutzer (keine Entscheidung noetig, nur zur Kenntnis): der oben vermerkte Randfall bei inaktiven, nie abgefragten Servern. + +## Self-Check: PASSED + +Alle im Plan genannten Dateien wurden gefunden, alle vier Commits sind im Log nachweisbar. + +--- +*Plan: 260923-le6* +*Completed: 2026-09-23*