156 lines
13 KiB
Markdown
156 lines
13 KiB
Markdown
---
|
||
phase: quick-261002-kxc
|
||
plan: 01
|
||
subsystem: nextcloud-status
|
||
tags: [benachrichtigung, glocke, mail, rls, scheduler, in-app]
|
||
status: complete
|
||
requires:
|
||
- quick-261002-k67 (Modul Nextcloud-Status)
|
||
provides:
|
||
- persönliche Glocke je Kachel (Tabelle NextcloudAlertSubscription, RLS mit Benutzerdimension)
|
||
- Zwei-Fehlschläge-Regel, Wiederholung nach 5 Minuten, gemeldeter Zustand auf der Zeile
|
||
- E-Mail (nur Deutsch) und Meldung in Tessera bei Störung und Wiederherstellung
|
||
- Fehlercodes als lesbarer Hinweis auf Kachel und in der Mail
|
||
affects:
|
||
- apps/api/src/nextcloud-status/
|
||
- apps/api/src/mail/mail.service.ts
|
||
- apps/web (Kachel, Seite, AppShell)
|
||
key-files:
|
||
created:
|
||
- apps/api/prisma/migrations/20261002170000_nextcloud_alerts/migration.sql
|
||
- apps/api/src/nextcloud-status/nextcloud-alert-rules.ts
|
||
- apps/api/src/nextcloud-status/nextcloud-alert-mail.ts
|
||
- apps/api/src/nextcloud-status/nextcloud-alert.service.ts
|
||
- apps/web/src/components/nextcloud-status/nextcloud-alert-notifier.tsx
|
||
- apps/web/src/components/nextcloud-status/error-hint.ts
|
||
modified:
|
||
- apps/api/prisma/schema.prisma
|
||
- apps/api/src/nextcloud-status/nextcloud-status.service.ts
|
||
- apps/api/src/nextcloud-status/nextcloud-status.controller.ts
|
||
- apps/api/src/nextcloud-status/nextcloud-status-scheduler.service.ts
|
||
- apps/api/src/nextcloud-status/nextcloud-status.module.ts
|
||
- apps/api/src/mail/mail.service.ts
|
||
- apps/web/src/app/(portal)/modules/nextcloud-status/components/CloudTile.tsx
|
||
- apps/web/src/app/(portal)/modules/nextcloud-status/page.tsx
|
||
- apps/web/src/components/layout/app-shell.tsx
|
||
- docs/mandantentrennung-zugriffsklassifikation.md
|
||
- CHANGELOG.md
|
||
- docs/anleitung-anwender.md
|
||
- docs/anleitung-administration.md
|
||
decisions:
|
||
- "Zwei-Fehlschläge-Regel für jeden fehlgeschlagenen Abruf (D-K1); der erste Fehlschlag schreibt nur Zähler und Zeitpunkt, die Kachel behält den guten Stand (D-K2)"
|
||
- "Der Zähler einer dauerhaft roten Cloud wird bei 2 gedeckelt (sonst wüchse er stündlich weiter)"
|
||
- "Wiederholung als zweiter Cron-Auftrag (jede Minute) über denselben einen Systemlesezugriff (D-K3)"
|
||
- "Anspruch vor dem Senden per updateMany auf alertState; Versand im Hintergrund, bis zu 3 Versuche im Abstand von 60 s (D-K5)"
|
||
- "Fehler-Hinweise: gleiche Zuordnung in API (Mail) und Web (Kachel); Rohkennung bleibt als Tooltip"
|
||
metrics:
|
||
duration: "ca. 20 Minuten"
|
||
completed: 2026-10-02
|
||
actuals:
|
||
tokens: 32500
|
||
tasks: 3
|
||
commits: 3
|
||
plan_head_before: 6829c44464ef3d68711117af656d4c6db79892ec
|
||
plan_head_after: 6c4bff6f6cf85867cdea54c1dbcc91d2c7fff028
|
||
---
|
||
|
||
# Quick 261002-kxc: Nextcloud-Status – Benachrichtigung bei Rot und Wiederherstellung
|
||
|
||
Persönliche Glocke je Kachel: Wer eingeschaltet hat, bekommt bei Störung und bei „wieder in Ordnung“ je Änderung genau eine E-Mail und eine Meldung in Tessera (Desktop-App: Windows-Benachrichtigung). Ein einzelner Fehlschlag löst nichts aus; Tessera prüft nach etwa fünf Minuten erneut.
|
||
|
||
## Commits
|
||
|
||
| Aufgabe | Commit | Inhalt |
|
||
|---|---|---|
|
||
| 1 (Tracer) | faed0d7 | Migration, reine Regeln, Mailbaustein, Alert-Dienst, Glocke in API und Kachel, RLS-Dokument |
|
||
| 2 | 11a70c9 | Wiederholungsauftrag, Adresswechsel, Hinweis „Prüfung fehlgeschlagen“, Fehlercodes in Klartext |
|
||
| 3 | 6c4bff6 | Endpunkt `GET alerts`, globaler Melder im AppShell, Anleitung, Changelog |
|
||
|
||
Nicht gepusht. Die Commit-Zeile trägt „Claude Sonnet 5.5“ (die Vorgabe der Umgebung für Commit-Anhänge), nicht den im Auftrag genannten Opus-Text, weil ich Sonnet 5.5 bin und die Angabe sonst falsch wäre.
|
||
|
||
## Was gebaut wurde
|
||
|
||
- **Datenbank:** Migration `20261002170000_nextcloud_alerts` (lokal angewendet, `migrate status` aktuell, Drift-Prüfung `--exit-code` = 0). Neue Tabelle `NextcloudAlertSubscription` mit Mandant-und-Benutzer-Regel wie „Reminder“, ohne `system_read_policy`, Cascade bei Cloud und Benutzer. Neue Spalten an `NextcloudInstance`: `consecutiveFailures`, `firstFailureAt`, `alertState` ('ok'|'red'), `alertReason`, `alertChangedAt`.
|
||
- **Reine Regeln** (`nextcloud-alert-rules.ts`): `decideAlert`, `planStatusWrite`, `FAILURES_FOR_RED = 2`, `RETRY_DELAY_MS = 5 min`.
|
||
- **Mail** (`nextcloud-alert-mail.ts`, `MailService.sendNextcloudAlertEmail`): Betreff „Nextcloud <Kundenname>: nicht erreichbar“ bzw. „… wieder in Ordnung“ wortgleich, eigene Betreffe für die anderen roten Gründe, Text mit Grund, Adresse, Zeit (Europe/Berlin), Link zum Modul; CR/LF und 150 Zeichen wie bei Erinnerungen.
|
||
- **Alert-Dienst:** Abonnieren/Abbestellen (idempotent, 404 für fremde Clouds), Anspruch vor dem Senden (`updateMany where alertState = bisher`, nur `count === 1` meldet), Versand im Hintergrund, Empfänger beim Senden neu geprüft (aktiv, Adresse, Modulzugriff über `getModuleAccessLevels`, SMTP), bis zu 3 Versuche, `listRecentAlerts` für die Meldung in Tessera.
|
||
- **Status-Dienst/Controller:** `checkInstance` ist weiter der einzige Schreibweg und läuft jetzt über `planStatusWrite` und `evaluateAfterCheck`; Listen tragen `subscribed`; `POST/DELETE instances/:id/subscription` und `GET alerts` nur mit Modul-Guard (kein Verwalten, keine Rolle).
|
||
- **Planer:** zweiter Auftrag `nextcloud-status-retry` (jede Minute), filtert dieselbe `forSystem`-Abfrage auf genau einen Fehlschlag älter als 5 Minuten. Weiterhin genau ein `forSystem`-Aufruf im Dienst.
|
||
- **Web:** Glocke an jeder Kachel (auch für „Benutzen“), optimistisches Umschalten mit Rücknahme bei Fehler, Browser-Erlaubnis einmalig beim ersten Einschalten; Hinweis „Prüfung fehlgeschlagen, wird in wenigen Minuten wiederholt“; `NextcloudAlertNotifier` im AppShell (nutzt die Helfer der Erinnerungen, daher Windows-Benachrichtigung in der Desktop-App; fragt ohne Modulzugriff nie ab; 401/403 pausiert bis Fokus).
|
||
- **Zusatzwunsch Fehlertexte:** Kachel und Mail zeigen statt `ERR_TLS_CERT_ALTNAME_INVALID` & Co. Klartext: „Zertifikat passt nicht zur Adresse“, „Zertifikat abgelaufen“, „Zertifikat nicht vertrauenswürdig“, „Adresse nicht gefunden“ (ENOTFOUND/EAI_AGAIN), „Verbindung abgelehnt“, „Zeitüberschreitung“, „Server antwortet mit Fehler <Code>“, sonst „Verbindungsfehler“. Auf der Kachel bleibt die Rohkennung als `title`-Tooltip. In der Mail steht nur der Klartext. Zuordnung als reine Funktionen `describeCheckError` (API) und `errorHint` (Web), je mit Tests; de/en-Texte unter `nextcloudStatus.errorHint`.
|
||
|
||
## Tests und Prüfungen (gemessen)
|
||
|
||
| Prüfung | Ergebnis |
|
||
|---|---|
|
||
| API komplett (`pnpm --filter @tessera/api test`) | 121 Dateien, 2120 Tests, alle grün |
|
||
| Web komplett (`pnpm --filter @tessera/web test`) | 121 Dateien, 1321 Tests, alle grün |
|
||
| `tsc --noEmit` API / Web | beide fehlerfrei |
|
||
| Biome `lint` auf allen berührten Dateien der drei Aufgaben (25 Dateien) | keine Fehler, 2 Warnungen in `mail.service.spec.ts` (Zeilen 130 und 150, Non-Null-Zusicherungen, vor dieser Arbeit vorhanden, nicht angefasst) |
|
||
| Biome `check` auf den neuen Dateien und dem AppShell | sauber |
|
||
| RLS-Specs (`rls-coverage`, `rls-access-inventory`) | grün, Dokument nachgeführt |
|
||
| Migration | angewendet, Drift 0 |
|
||
|
||
Neu hinzugekommen: Regeln 19 Tests, Mailbaustein 27, Alert-Dienst 22, Web-Melder 11, Fehlerhinweis 20 u. a. sowie Erweiterungen in Status-Dienst, Controller, Scheduler, MailService und Seitentest.
|
||
|
||
Hinweis zur Reihenfolge: Bei den reinen Regeln habe ich Spec und Implementierung gemeinsam geschrieben und nicht ausdrücklich zuerst den roten Lauf beobachtet; die Fälle aus dem Plan sind vollständig abgedeckt.
|
||
|
||
## RLS-Dokument (`docs/mandantentrennung-zugriffsklassifikation.md`)
|
||
|
||
- Bereichszeile `nextcloud-status`: 0/23/1 (gemessen mit der Gate-Schleife über `nextcloud-status/`; Dienst 14, Alert-Dienst 9 gebundene Rohtreffer).
|
||
- Neue Fundstellen: drei Paare `nextcloud-alert.service.ts` (`nextcloudAlertSubscription`, `nextcloudInstance`, `user`), Paarzahl 96 (53→56 `muss-mandantengebunden`).
|
||
- Summenzeile von mir nur um die eigenen +9 fortgeschrieben (61/273/8). **Auffälligkeit:** Eine frische Messung über alle Bereiche ergibt 61/280/8; die Differenz von 7 stammt aus älteren, nicht nachgeführten Zeilen (`dashboard` 30 statt 29, `groups` 33 statt 31, `reminders` 13 statt 12, weitere Bereiche ohne eigene Zeile). Das habe ich nicht stillschweigend „repariert“, sondern in der Summenzeile vermerkt.
|
||
- `FORSYSTEM_ALLOWED_CALL_SITES` unverändert; genau ein `forSystem(this.prisma)` in `nextcloud-status.service.ts`.
|
||
|
||
## Abweichungen vom Plan
|
||
|
||
**1. [Rule 3 - Blockierend] Umlaut-Wächter**
|
||
- Gefunden bei Aufgabe 2: `umlaut-guard.spec.ts` meldete „passt“ und „vertrauenswürdig“ als neue Wörter.
|
||
- Behoben: beide in `UMLAUT_ALLOWLIST` (`apps/web/src/messages/umlaut-dictionary.ts`) ergänzt (korrektes Deutsch).
|
||
|
||
**2. [Rule 2 - Korrektheit] Zähler gedeckelt**
|
||
- `consecutiveFailures` einer dauerhaft roten Cloud würde sonst bei jeder stündlichen Prüfung weiterzählen; gedeckelt bei 2 (`FAILURES_FOR_RED`). Mit Test.
|
||
|
||
**3. Zusatzwunsch Fehlertexte** (Auftrag, nicht im Plan): zusätzlich neue Dateien `error-hint.ts`/`.test.ts` und `describeCheckError` in `nextcloud-alert-mail.ts`; Doku-Satz „Fehlercode steht klein darunter“ in der Administrationsanleitung angepasst.
|
||
|
||
**4. Commit-Anhang:** siehe oben (Sonnet 5.5 statt Opus-Text).
|
||
|
||
Sonst: Plan wie geschrieben ausgeführt. Keine Authentifizierungs-Hürden, keine Paketinstallationen.
|
||
|
||
## Lokaler Neubau
|
||
|
||
`docker compose up -d --build api web` ausgeführt; api, web und db laufen. Im API-Protokoll: „Nextcloud-Status module seeded in registry“, „Nextcloud-Status retry job registered: * * * * *“, Routen `/modules/nextcloud-status/instances/:id/subscription` (POST, DELETE) und `/modules/nextcloud-status/alerts` (GET) gemappt, „No pending migrations to apply“, keine Fehler.
|
||
|
||
## Für die Browser-Prüfung (Orchestrator)
|
||
|
||
**Lokaler SMTP-Stand:** Ja, eingerichtet. Genau eine `SmtpConfig`-Zeile: Host `mailhog`, Port 1025, Absender `tessera@tessera.local`. Die Mails landen also in MailHog (nicht in einem echten Postfach). Empfangsadressen der lokalen Konten (nur nachgesehen, nichts geändert): `admin` (SUPER_ADMIN) `admin@tessera.local`, `nutzer1` `nutzer1@tessera.local`, `nutzer2` `nutzer2@tessera.local`, `testuser` `testuser@example.com`; alle aktiv. Lokal gibt es bereits 5 Clouds. Ich habe keinen Versand ausgelöst (nur Unit-Tests mit gemocktem Transport).
|
||
|
||
**Klickweg, beide Übergänge zu erzwingen** (als Admin; ein Benutzer mit nur „Benutzen“ sieht die Glocke ebenfalls, kann aber keine Clouds anlegen oder prüfen):
|
||
|
||
1. „Cloud hinzufügen“ mit einer nicht erreichbaren Adresse, z. B. `https://127.0.0.1:9`. Die Kachel bleibt grau „Noch nicht geprüft“ und zeigt den Hinweis „Prüfung fehlgeschlagen, wird in wenigen Minuten wiederholt“.
|
||
2. Glocke auf dieser Kachel einschalten (Browser fragt einmalig nach der Erlaubnis für Benachrichtigungen).
|
||
3. „Jetzt prüfen“ oder den Prüfknopf der Kachel noch einmal drücken: zweiter Fehlschlag, Kachel wird rot „Nicht erreichbar“ mit Klartext-Grund (bei 127.0.0.1:9 „Verbindung abgelehnt“). Es kommt eine Mail „Nextcloud <Name>: nicht erreichbar“ (in MailHog) und die Meldung in Tessera. Ohne Knopfdruck passiert dasselbe automatisch nach etwa 5 Minuten durch den Wiederholungsauftrag. Weitere Prüfungen, solange die Cloud rot bleibt, senden nichts mehr.
|
||
4. Adresse der Cloud bearbeiten auf eine erreichbare öffentliche Nextcloud: sie wird sofort neu geprüft, die Kachel wird grün oder gelb, es kommt „Nextcloud <Name>: wieder in Ordnung“ (Mail und Meldung).
|
||
|
||
Sofort rot ohne Wartezeit: eine Cloud, die im Wartungsmodus steht oder auf eine Datenbank-Aktualisierung wartet, wird beim ersten Abruf gemeldet.
|
||
|
||
## Known Stubs
|
||
|
||
Keine.
|
||
|
||
## Threat Flags
|
||
|
||
Keine neuen Angriffsflächen außerhalb des Plan-Bedrohungsmodells (T-kxc-01 bis -08 umgesetzt: Benutzer nur aus dem Token, 404 für fremde Clouds, Empfänger beim Senden neu geprüft, Anspruch vor dem Senden, CR/LF-Schutz im Betreff, Versand im Hintergrund, keine Verwalten-/Rollen-Decorators an den neuen Handlern, durch Tests festgeschrieben).
|
||
|
||
## Self-Check: PASSED
|
||
|
||
- Dateien vorhanden: Migration, `nextcloud-alert-rules.ts`, `nextcloud-alert-mail.ts`, `nextcloud-alert.service.ts`, `nextcloud-alert-notifier.tsx`, `error-hint.ts` – gefunden.
|
||
- Commits vorhanden: faed0d7, 11a70c9, 6c4bff6 (gemessen mit `git rev-list --count 6829c44..HEAD` = 3).
|
||
|
||
## Browser-Prüfung (Orchestrator, 02.10., lokal, dunkel, MailHog)
|
||
|
||
- Grundmodul (k67): 5 echte öffentliche Clouds + 1 kaputte; Ampel korrekt (35.0.1/34.0.4 grün, 33.0.5/33.0.8 Enterprise gelb „Update auf 33.0.9“, Zertifikatsfehler rot), Sortierung Status, Logo per Upload und per URL, Dashboard-Kachel mit Zählern 2/2/1.
|
||
- Glocke an „Ausfall AG“ (https://127.0.0.1:9): erster Abruf grau + Wiederholungshinweis, zweiter rot „Nicht erreichbar“ (Port 9 ist von fetch gesperrt → „Verbindungsfehler“ korrekt; normaler Port liefert ECONNREFUSED → „Verbindung abgelehnt“).
|
||
- Mail „Nextcloud Ausfall AG: nicht erreichbar“ in MailHog, Text verständlich; Browser-Benachrichtigung erschienen.
|
||
- Adresse auf erreichbare Cloud geändert → grün, Mail + Benachrichtigung „wieder in Ordnung“.
|