Uebergabe-Notizen der pausierten Sitzung entfernt, die Arbeit ist wieder aufgenommen. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
23 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, estimate, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | estimate | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick | 260923-le6 | execute | 1 |
|
true |
|
|
- PMG-Teilsumme (API):
sumOrNull(a, b)inapps/api/src/proxmox/proxmox-normalize.tsaddiert heute einen fehlenden Teilwert als 0, sobald nur EINE Haelfte null ist. Dadurch zeigt die Seite z. B. „Spam: 10“ als vollstaendige Tageszahl, obwohlspamcount_outfehlte. 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. - 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/pollverlangt 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.
<execution_context>
@/.claude/gsd-core/workflows/execute-plan.md
@/.claude/gsd-core/templates/summary.md
</execution_context>
@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.roleist'SUPER_ADMIN' | 'ADMIN' | 'USER'.page.tsxberechnet BEREITSconst 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<ProxmoxServer[]>,pollServer(id: string): Promise<ProxmoxTestResult>, TypProxmoxServer(inkl.isActive,pollIntervalMin,status: ProxmoxServerStatus | null).ServerCardwird ausschliesslich inpage.tsx:99verwendet (per grep geprueft).- Test-Muster fuer Rollen:
settings-roles.test.tsxmockt@/lib/stores/auth-storemituseAuthStore: (selector) => mockAuthStore(selector)und setzt je FallmockAuthStore.mockImplementation((sel) => sel({ user })), ausserdemnext/linkals<a>undnext-intlmit handgeschriebener Uebersetzungstabelle. - Nachrichtendateien: nur
apps/web/src/messages/de.jsonundapps/web/src/messages/en.json. Namensraumproxmox.card(de.json ab Zeile 712).umlaut-guard.spec.tsprueft de.json auf Ersatzschreibungen (ae/oe/ue/ss) — neue deutsche Texte brauchen echte Umlaute. - Abfragetakt:
ProxmoxServer.pollIntervalMinVorgabe 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.
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.
(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(<ServerCard server={server} isAdmin />) umstellen; seine Erwartungen bleiben. Neue Tests fuer die Faelle aus <behavior> 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.
(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 <a>) 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 <behavior>; 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 <ServerCard server={server} /> 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.
<threat_model>
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. |
| </threat_model> |
6530ae5 geprueft: alles gruen, `biome lint` sauber):
pnpm --filter api exec vitest run src/proxmoxgruenpnpm --filter web exec vitest run "src/app/(portal)/modules/proxmox" src/messagesgruenpnpm --filter api type-checkundpnpm --filter web type-checkohne Fehlerpnpm 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 Befundpnpm exec biome check "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx"ohne Befund (neue Datei, voll konform)biome checkauf den fuenf VORHANDENEN Dateien: vorher 6 Befunde, alle vorbestehend (Formatierung je Datei, dazu organizeImports inpage.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
<success_criteria>
- 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 beachtetisActiveheute 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.mdgeschlossen ist. </success_criteria>