6829c44464
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
125 lines
11 KiB
Markdown
125 lines
11 KiB
Markdown
---
|
|
phase: quick-261002-k67
|
|
plan: 01
|
|
subsystem: nextcloud-status
|
|
tags: [nextcloud, ampel, modul, dashboard-kachel, scheduler, rls]
|
|
status: complete
|
|
requires:
|
|
- module-registry (ModuleGuard, ModuleManage, seedModule)
|
|
- Freigabestufe Verwalten (quick-261002-icv)
|
|
provides:
|
|
- Modul "nextcloud-status" (Kategorie infrastructure) mit Ampel-Kacheln je Cloud
|
|
- Tabelle NextcloudInstance (RLS, system_read_policy)
|
|
- stündlicher Prüfauftrag nextcloud-status-poll
|
|
- Dashboard-Kachel "nextcloud-status" (zweite modulgebundene Kachel)
|
|
affects:
|
|
- WIDGET_TYPES / WIDGET_MODULE_SLUGS (@tessera/shared)
|
|
- docs/mandantentrennung-zugriffsklassifikation.md, FORSYSTEM_ALLOWED_CALL_SITES
|
|
tech-stack:
|
|
added: []
|
|
patterns:
|
|
- reine Bewertungsfunktion mit eingereichtem Datum
|
|
- Stale-while-revalidate-Zwischenspeicher im Speicher (12 h, 15 min Pause nach Fehlschlag)
|
|
- ein globaler Cron-Auftrag in onApplicationBootstrap ohne Datenbankzugriff bei der Registrierung
|
|
key-files:
|
|
created:
|
|
- apps/api/prisma/migrations/20261002150000_nextcloud_status/migration.sql
|
|
- apps/api/src/nextcloud-status/ (Rating, Fetch, Release, Service, Controller, Scheduler, Logo-Regeln, DTO, Seed, Modul, je mit Spec)
|
|
- apps/web/src/lib/nextcloud-status-api.ts
|
|
- apps/web/src/components/nextcloud-status/ (rating-display, sort-clouds + Test)
|
|
- apps/web/src/app/(portal)/modules/nextcloud-status/ (layout, page, CloudTile, CloudForm + Tests)
|
|
- apps/web/src/components/dashboard/widgets/nextcloud-status-widget.tsx (+ Test)
|
|
modified:
|
|
- apps/api/prisma/schema.prisma, apps/api/src/app.module.ts
|
|
- apps/api/src/prisma/rls-access-inventory.spec.ts, docs/mandantentrennung-zugriffsklassifikation.md
|
|
- apps/api/src/module-registry/module-manage-handlers.spec.ts
|
|
- packages/shared/src/index.ts, apps/api/src/dashboard/widget-module-map(.spec).ts
|
|
- Web-Registrierungen (module-loader, module-identity, nav-store, module-tile, module-layouts.test, widget-registry(+Tests), widget-icon, widget-wrapper, (portal)/page(+Test)), de.json, en.json, umlaut-dictionary.ts
|
|
- CHANGELOG.md, docs/anleitung-anwender.md, docs/anleitung-administration.md
|
|
decisions:
|
|
- "Logo-Bytes als bytea an der Zeile; Listen-/Planerabfragen wählen sie nie aus, nur getLogo (D-A)"
|
|
- "Bewertung beim Lesen in der API; Grundkennung plus Parameter, Übersetzung im Web (D-B)"
|
|
- "Ein globaler Cron-Auftrag, beim Start ohne Datenbankzugriff registriert (D-C)"
|
|
- "Zertifikate der Clouds werden geprüft, Fehler erscheint als Nicht erreichbar mit Fehlercode (D-H)"
|
|
metrics:
|
|
duration: etwa 1 h 15 min
|
|
completed: 2026-10-02
|
|
actuals:
|
|
tokens: 52000
|
|
tasks: 3
|
|
commits: 3
|
|
plan_head_before: 7188733f70f88ce7a61c53f76a614c476864bbfd
|
|
plan_head_after: 87a7b7cbcd86756b1892c14abd77f66744edc969
|
|
---
|
|
|
|
# Phase quick-261002-k67 Plan 01: Modul Nextcloud-Status mit Ampel-Kacheln
|
|
|
|
Neues Modul „Nextcloud-Status“ (Gruppe Infrastruktur): Verwalter tragen Nextcloud-Clouds ihrer Kunden ein, Tessera ruft stündlich `<Adresse>/status.php` ab, bewertet die Version gegen die Daten von endoflife.date (Ampel grün/gelb/rot/grau) und zeigt je Cloud eine Kachel sowie eine Übersichtskachel auf dem Dashboard.
|
|
|
|
## Was gebaut wurde
|
|
|
|
**Aufgabe 1 (Tracer), Commit 5ef7b0c:** Tabelle `NextcloudInstance` samt Zeilenschutz (Migration 20261002150000, lokal angewendet, `migrate status` aktuell, Drift-Prüfung Exit 0). Reine Funktion `rateNextcloud` (alle Regeln aus L-05 einschließlich der Grenzen 90/91 Tage und Support-Ende-Tag gelb, Tag danach rot), `normalizeCloudUrl`, `parseNextcloudStatus`, `fetchNextcloudStatus` (nur GET `<Adresse>/status.php`, 10 s, 3 Weiterleitungen, 64 KiB, keine Zugangsdaten, nur eigene Fehlerkürzel). `NextcloudReleaseService` als Zwischenspeicher (12 h, veraltete Daten sofort, Erneuerung im Hintergrund, 15 min Pause nach Fehlschlag, geteilte Anfrage). Dienst, Controller (`GET instances`, `POST instances`, `POST instances/:id/check`), Seed, Modul, RLS-Inventar. Web: Modulseite mit Kacheln (Ampelleiste, Pille mit Klartext, Logo oder Initialen, Version, letzte Prüfung), Registrierungen in Loader, Identität (Wolkensymbol), Seitenleisten-Titel, Layout-Test, Texte de/en.
|
|
|
|
**Aufgabe 2, Commit 6698ef1:** Ändern, Entfernen, Logo hochladen/abrufen/entfernen, „Jetzt prüfen“ (`POST instances/check`, statisch vor allen `:id`-Routen) und Einzelprüfung, alle mit `@ModuleManage('nextcloud-status')` und ohne Rollen-Decorator; Logo per Magic Bytes (PNG/JPEG/GIF/WebP, kein SVG, 1 MiB, multer plus Zweitprüfung). Stündlicher Auftrag `nextcloud-status-poll` (`0 * * * *`) in `onApplicationBootstrap` ohne Datenbankzugriff; je Durchlauf ein `forSystem`-Aufruf (nur `id`, `tenantId`), danach jede Cloud an ihren Mandanten gebunden, höchstens vier gleichzeitig, Überlappungsschutz. Web: Sortierung (Kundenname, Status, Version, Support-Ende; je Benutzer in localStorage), Formular (Anlegen/Bearbeiten/Löschen mit Bestätigung/Logo), Verwalten-Knöpfe nur mit `useCanManageModule`. `FORSYSTEM_ALLOWED_CALL_SITES` und die Zugriffsklassifikation nachgezogen.
|
|
|
|
**Aufgabe 3, Commit 87a7b7c:** Dashboard-Kachel `nextcloud-status` (geteilte Typliste, Modulzuordnung `nextcloud-status` → `nextcloud-status`, Registry, Symbol, Rahmenkopf, Katalog nur mit Modulzugriff): Zähler grün/gelb/rot (und „ohne Bewertung“ nur wenn > 0), darunter rote, dann gelbe Clouds mit Grund; Klick öffnet das Modul, im Bearbeitungsmodus keine Links, ruft nie eine Prüfung auf. CHANGELOG, Anwender- und Administrationsanleitung.
|
|
|
|
## Testergebnisse (ehrlich)
|
|
|
|
| Prüfung | Ergebnis |
|
|
|---|---|
|
|
| API-Tests gesamt (`pnpm --filter @tessera/api test`) | 118 Dateien, **2030 Tests grün** |
|
|
| Web-Tests gesamt (`pnpm --filter @tessera/web test`) | 119 Dateien, **1276 Tests grün** |
|
|
| `tsc --noEmit` api | grün |
|
|
| `tsc --noEmit` web | grün |
|
|
| Biome lint auf den neuen/geänderten Dateien | keine Meldungen auf neuen Dateien; die zehn verbleibenden Warnungen der Gesamtläufe stammen aus bestehenden Widget-Dateien (z. B. Stoppuhr) und wurden nicht angefasst |
|
|
| Biome check --write | nur auf neuen Dateien angewendet |
|
|
| Schlüsselabgleich de/en (`nextcloudStatus`, `widgets.nextcloudStatus`) und Prüfung auf Mandanten-Wörter | 74 Schlüssel je Sprache, deckungsgleich, kein Treffer |
|
|
| Umlaut-Wächter | grün (Allowlist: Neueste, ausstehend, Statusseite, Bildadresse, aktuell) |
|
|
| rls-coverage, rls-access-inventory | grün (93 Paare, Bereichszeile 0/14/1, Summe 61/264/8, 7 Dateien/8 `forSystem`-Aufrufe) |
|
|
|
|
Neue Tests: Rating (22), Fetch/Parser/URL (35), Release-Cache (10), Service (18), Controller (11), Scheduler (5), Logo-Regeln (6), module-manage-handlers (+9), Web: Seite (13), CloudForm (9), sort-clouds (8), Widget (8) sowie angepasste Aufzählungstests der Widget-Typen.
|
|
|
|
## Lokaler Stand
|
|
|
|
- Migration lokal angewendet (Container-IP 172.19.0.2), `docker compose up -d --build api web` gebaut, api healthy, web läuft.
|
|
- API-Log: „Nextcloud-Status module seeded in registry“, „Nextcloud-Status cron job registered: 0 * * * *“, alle neun Routen gemappt (`instances/check` vor `instances/:id`), „No pending migrations to apply“.
|
|
- Datenbank: `Module`-Zeile `nextcloud-status`, Kategorie `infrastructure`, `isSystem` true; Tabelle `NextcloudInstance` vorhanden.
|
|
- Nicht gepusht. SUMMARY/STATE/PLAN nicht committiert (macht der Orchestrator).
|
|
|
|
## Abweichungen vom Plan
|
|
|
|
Keine funktionalen Abweichungen. Drei kleine Anmerkungen:
|
|
|
|
1. **[Hinweis] Zugriffsklassifikation in zwei Schritten:** Task 1 trug `nextcloud-status.service.ts`/`nextcloudInstance` als `gebunden` ein (0/4/0), Task 2 stellte es wie geplant auf `system-gebunden` (0/14/1) um. Die Zahlen stammen aus der Gate-Schleife (`grep -cE 'tenantPrisma\.[a-zA-Z]*\.'` über die nicht-Spec-Dateien), nicht aus Annahmen.
|
|
2. **[Rule 1 - Fehler] Zählerstand im Registry-Test:** Die Erwartung `counted` (44) im bestehenden Registry-Test musste auf 48 steigen (zwölf Typen mal vier Felder); dazu mussten die Aufzählungen aus dem Plan angepasst werden (Katalog-, Registry-, Modulkarten-Tests). Beim ersten Voll-Lauf war zusätzlich `widget-module-map.spec.ts` noch auf einen Modulbezug eingestellt; im selben Task behoben, danach alle Tests grün.
|
|
3. **[Hinweis] Ledger für `commits:`:** Der Ledger-Eintrag wurde erst nach dem ersten Commit angelegt und aus dessen Elternkommit (7188733) gebildet; die Zählung (3) entspricht `git rev-list --count 7188733..HEAD`.
|
|
|
|
## Bekannte Stubs
|
|
|
|
Keine.
|
|
|
|
## Bedrohungen (Threat Model)
|
|
|
|
Alle als `mitigate` eingestuften Punkte sind umgesetzt und getestet: T-k67-02 (Logo: Magic Bytes, nosniff, Sandbox-CSP, 1 MiB), -03 (`@ModuleManage` ohne Rollen-Decorator, Metadaten-Specs), -04 (`forTenant`, `where { id, tenantId }`, ein einziger `forSystem`-Aufruf mit `select { id, tenantId }`), -05 (nur https, `referrerPolicy="no-referrer"`, API ruft die Logo-Adresse nie ab), -06 (Nebenläufigkeit 4, Überlappungsschutz, Zeitlimits), -07 (`PUBLIC_SELECT` ohne `logoData`, Spec prüft), -08 (statische Route vor `:id`, Reihenfolge-Test), -09 (`normalizeCloudUrl`). T-k67-01 (SSRF auf manuell eingegebene, auch interne Adressen) ist wie geplant akzeptiert und im Code und in der Administrationsanleitung dokumentiert.
|
|
|
|
## Threat Flags
|
|
|
|
Keine neuen Angriffsflächen außerhalb des Plans.
|
|
|
|
## Für die Browser-Prüfung (Orchestrator)
|
|
|
|
1. Als Administrator im Marktplatz „Nextcloud-Status“ aktivieren (falls noch nicht), Freigabe erteilen; die Seite unter `/modules/infrastructure/nextcloud-status` und `/modules/nextcloud-status` öffnen (Seitenleiste, Gruppe Infrastruktur).
|
|
2. „Cloud hinzufügen“: eine öffentlich erreichbare Nextcloud eintragen (Kundenname + Adresse, z. B. eine echte Kunden-Cloud). Erwartung: Kachel mit Version, Ampel und Klartext, „Zuletzt geprüft vor …“; Kopfzeile nennt die neueste Nextcloud-Version. Außerdem eine nicht erreichbare Adresse (rot „Nicht erreichbar“) und eine Adresse ohne Nextcloud (rot „Keine gültige Nextcloud-Antwort“) probieren.
|
|
3. Logo: einmal PNG hochladen (Kachel zeigt es), einmal https-Bildadresse, „Logo entfernen“ (Initialen); eine Nicht-Bilddatei und eine Datei über 1 MB (Fehlermeldung im Formular).
|
|
4. Sortierung: alle vier Optionen, danach neu laden (Auswahl bleibt, im Dunkelmodus ebenfalls prüfen).
|
|
5. Verwalten-Stufe: Benutzer nur mit „Benutzen“ anmelden (sieht Kacheln und Sortierung, aber weder „Cloud hinzufügen“, „Jetzt prüfen“, Kachel-Knöpfe noch Bearbeiten), Benutzer mit „Verwalten“ (sieht alles). „Jetzt prüfen“ und Kachel-Prüfung ausprobieren.
|
|
6. Dashboard: im Bearbeitungsmodus „Widget hinzufügen“ → „Nextcloud-Status“ (nur mit Modulzugriff im Katalog). Zähler, rote/gelbe Liste mit Grund, Klick öffnet das Modul; kleine Kachel zeigt nur die Zähler.
|
|
7. Optional: `docker compose logs api` nach der vollen Stunde — Prüfläufe der Clouds laufen ohne Fehler.
|
|
|
|
## Self-Check: PASSED
|
|
|
|
- Dateien vorhanden: Migration, `apps/api/src/nextcloud-status/*`, `apps/web/src/app/(portal)/modules/nextcloud-status/*`, Widget und Tests (alle per Commit nachgewiesen).
|
|
- Commits vorhanden: 5ef7b0c, 6698ef1, 87a7b7c (`git log`), 3 Commits seit 7188733.
|
|
- Laufender Stand: api healthy, Seed- und Planer-Logzeilen vorhanden, Modul-Zeile in der Datenbank.
|