docs(quick-261008-mzu): Modul Dateien (Nextcloud-Client)
Tessera CI/CD / Lint & Type Check (push) Successful in 50s
Tessera CI/CD / Tests (push) Successful in 1m46s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 20s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m44s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-08 22:59:16 +02:00
parent 49eebde215
commit 71b3f32c98
6 changed files with 1562 additions and 1 deletions
@@ -0,0 +1,136 @@
---
phase: quick-261008-mzu
plan: 01
subsystem: nextcloud-files
tags: [nextcloud, webdav, login-flow-v2, chunked-upload, file-browser, nestjs, nextjs, rls]
requires:
- module-registry (UseModule, ModuleManage, Freigabestufen)
- nextcloud-status (normalizeCloudUrl, parseNextcloudStatus)
- CryptoService (AES-256-GCM)
provides:
- Modul "Dateien" (slug nextcloud-files, Gruppe Infrastruktur): eine Nextcloud je Organisation, ein Konto je Benutzer
- Anmeldung per Passwort (App-Passwort) und per Login Flow v2 (Zwei-Faktor), Abmelden mit Widerruf
- Dateiansicht (Liste/Raster, Vorschau, Quota), Anlegen/Umbenennen/Verschieben/Loeschen in den Papierkorb
- Hochladen als Datenstrom (8-MiB-Stuecke, Chunked Upload v2), Herunterladen, ZIP
- Nextcloud-Kennung (Name, Logo, Farbe) auf dem Anmeldebildschirm
affects: [web-ui, api, docs, changelog]
tech-stack:
added: []
patterns:
- einzige Transportschicht ncRequest (feste Pfadanfaenge, Segmentcodierung, keine Weiterleitungen, keine Cookies)
- prozessweite Aufrufsperre (429 haelt den Ursprung an, erstes 401 toetet den Zugangsschluessel)
- mapNcFailure als einzige Fehlerabbildung, nie 401/403 an den Browser
- RLS auf Mandant UND Benutzer (current_user_id)
key-files:
created:
- apps/api/src/nextcloud-files/ (Transport, Aufrufsperre, Anmelde-Client, Kontodienst, Dateidienst, Transferdienst, Serverkennung)
- apps/api/prisma/migrations/20261008180000_nextcloud_files/migration.sql
- apps/api/prisma/migrations/20261008183000_*/migration.sql
- apps/web/src/app/(portal)/modules/nextcloud-files/ (Seite, Komponenten)
- apps/web/src/lib/nextcloud-files-api.ts, nextcloud-files-upload.ts
- apps/web/src/components/nextcloud-files/ (Dateitypen, Auswahl, Pfade, Formate, Uebertragungen)
- .planning/quick/261008-mzu-modul-nextcloud-dateien-eigenstaendiger-/e2e/ (Testaufbau und vier e2e-Skripte)
modified:
- CHANGELOG.md, docs/anleitung-anwender.md, docs/anleitung-administration.md, docs/anleitung-betrieb.md
- apps/web/src/messages/de.json, en.json, apps/web/src/app/globals.css
decisions:
- "Eine Nextcloud-Adresse je Organisation (Verwalten); interne Adressen erlaubt, dafuer enge Eingrenzung statt Public-URL-Pruefung (SSRF-Schutz ueber feste Pfade, keine Weiterleitungen, nie URL aus Antworten)"
- "App-Passwort gilt nur fuer den Anmeldenamen der Ausstellung (Spalte ncLoginName, gemessen: E-Mail-Anmeldung gegen Nextcloud 34)"
- "401 auf getapppassword ist doppeldeutig (falsches Passwort oder Zwei-Faktor) und heisst credentialsOrTwoFactor mit Weg ueber den Browser"
- "Login Flow: poll.endpoint und login-Ursprung der Antwort werden verworfen; Abfrage immer auf {Basis}/index.php/login/v2/poll, Link aus Basis und Token neu gebaut"
- "Eigene Anmeldebremse 3/15 min je Benutzer und 8/30 min je Server unter Nextclouds 10/30; jedes 429 pausiert alle Aufrufe an den Ursprung"
- "Hochladen in 8-MiB-Stuecken wegen 10-MiB-Klonlimit und 30-s-Proxyzeit von Next.js; Zusammenbau asynchron mit abfragbarem Zustand"
- "Serverkennung: Name aus status.php, Farbe aus anonymen capabilities (nur #rrggbb), Logo nur von zwei festen Pfaden, Bildtyp aus den Bytes, SVG nur mit CSP-Sandbox, 10 Minuten Zwischenspeicher je Organisation und Adresse"
- "Kein Dashboard-Widget (L-08); Etappe 2 bleibt offen"
metrics:
duration: "Task 6: etwa 70 Minuten; gesamt ueber sechs Aufgaben am 2026-10-08"
completed: 2026-10-08
status: complete
commits: 6
plan_head_before: 83b842b72cdd2be7d6e70286708753ab22038271
plan_head_after: 630398fe93596e96a65891381b3844a91c4ed49a
actuals:
tokens: 191000
tasks: 6
commits: 6
---
# Phase quick-261008-mzu Plan 01: Modul Nextcloud-Dateien (Etappe 1) Summary
Neues Modul „Dateien“: Jeder Benutzer arbeitet in seiner eigenen Nextcloud (Konto per Passwort oder Browser-Anmeldung mit Zwei-Faktor), mit Dateiansicht, Hoch- und Herunterladen großer Dateien durch den Next.js-Proxy, Papierkorb-Löschen, und einem gestalteten Anmeldebildschirm mit Name, Logo und Farbe der Nextcloud.
## Commits
| Aufgabe | Commit | Inhalt |
|---|---|---|
| 1 | 9606967 | Modul, Migration (Config + Account mit Zeilenschutz Mandant UND Benutzer), Transportschicht, Aufrufsperre, Einstellungen, Registrierung |
| 2 | d00b6ff | Anmeldung per Passwort und Login Flow v2, Anmeldebremse, Abmelden mit Widerruf, Verbindungsbildschirm, Spalte ncLoginName |
| 3 | 8bee65c | PROPFIND, WebDAV-Schicht, Dateidienst (list, preview, folders, move, delete), mapNcFailure, sendUpstreamStream |
| 4 | 4d4a0b3 | Hochladen als Datenstrom (einzeln und in Stücken), Download mit Range, ZIP, Browser-Uploader |
| 5 | 86fbe8f | Dateiansicht (Liste/Raster, Typkacheln, Auswahl, Tastatur, Menüs, Dialoge, Ziehen und Ablegen, Übertragungsleiste) |
| 6 | 630398f | Serverkennung (API + Web), gestaltete Anmeldekarte, Anleitungen, Changelog, Fokusfang, e2e wiederholbar |
## Aufgabe 6 im Detail
- API: `nextcloud-server-info.ts` (`NextcloudServerInfoService`) mit `GET server` und `GET server/logo` (Benutzen-Ebene, statisch vor dem Parameterblock, Controller- und Manage-Handler-Spec erweitert). 21 neue Tests (fake Transport, echte Aufrufsperre, fake Uhr): feste Pfade, Farbfilter, Logoerkennung nach Bytes, 512-KiB-Grenze, Zwischenspeicher 10 min, Adresswechsel leert, gesperrter Ursprung = keine Anfrage, 429 mitten in der Abfrage wird nicht gemerkt, Antwortköpfe des Logos.
- Web: `ServerIdentity.tsx` (`ServerTile`, `ServerIdentity`), Anmeldekarte neu gestaltet (Kennungskopf mit Akzentlinie in der Themenfarbe, Hinweis zum Passwort am Kartenfuß mit Schloss), kleine Kachel in der Kontoleiste; 6 neue Seitentests; Texte de + en.
- Registrierungen geprüft (app.module, module-loader, module-identity `folder`, nav-store, layouts-Test, Seed); `WIDGET_MODULE_SLUGS` ohne `nextcloud-files`.
- Doku: CHANGELOG (vier Einträge unter „Neu“), Anwenderanleitung „Dateien (Nextcloud)“ mit Tastaturtabelle, Administrationshandbuch „Dateien: Nextcloud anbinden“ (Brute-Force-Ausnahme mit occ-Befehl, Adresswechsel, verwaiste App-Passwörter nach Benutzer-Löschung), Betriebshandbuch „Dateien (Nextcloud)“ in Kapitel 3 (NODE_EXTRA_CA_CERTS, Proxy-Grenzen, Zustand im Arbeitsspeicher) plus Fehlerbild-Zeile.
## Verifikation
- api-Suite 151 Dateien / 2922 Tests grün, web-Suite 143 Dateien / 1633 Tests grün (nach der letzten Änderung erneut), beide `tsc --noEmit` ohne Fehler, `biome lint` ohne Fehler (nur schon vorhandene Warnungen).
- Rollen-Decorator-Grep, de/en-Parität und Wortprüfung, Widget-Prüfung, Changelog-/Anleitungs-Greps grün.
- Stack neu gebaut (`docker compose up -d --build api web`), /health 200, Seed-Zeile „Nextcloud files module seeded in registry“, Routen `server` und `server/logo` vor den Parameterrouten abgebildet.
- Alle e2e-Skripte auf dem neu gebauten Stack grün und zweimal hintereinander wiederholbar: nc-test-setup, e2e-settings (inkl. Serverkennung: Name, Rechnername, Version, Logo mit CSP/nosniff), e2e-connect, e2e-files, e2e-transfer.
- Browserprüfung (playwright-core, Dunkel und Hell, echte Test-Nextcloud): Kennung mit Name „Nextcloud“, Logo und Themenfarbe rgb(0,103,158); 2FA-Wartezustand mit Link auf die Nextcloud; Fehler credentialsOrTwoFactor mit Browser als Hauptknopf; 30-MB-Upload über den Dateiauswahldialog mit Fortschritt und Namenskonflikt-Zeile (Überspringen); Umbenennen per F2, Verschieben nach „Rechnungen & Belege“, Löschen mit Rückfrage (in der Nextcloud-Papierkorb nachgewiesen), Datei-Download (PDF) und Ordner-ZIP („PK“), Enter/Rücktaste/Strg+A.
- Screenshots unter `.playwright-mcp/nextcloud-files/`: dunkel `t6-dark-{connect,connect-waiting,connect-error,list,grid,selection,drop-overlay,transfers,move-dialog,empty,delete-dialog,mobile,mobile-connect,mobile-list}.png`, hell `t6-light-{connect,connect-waiting,list,grid,selection,drop-overlay,transfers,move-dialog,empty}.png`. Gegen L-09 geprüft: ruhige Liste, Streifen nur beim Ziehen, lesbare Typkacheln in beiden Modi, keine Großbuchstaben-Beschriftungen, keine Mittelpunkte.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] Rücktaste ging in leeren Ordnern verloren**
- **Found during:** Aufgabe 6 (Browserprüfung, Screenshot „empty“)
- **Issue:** In einem leeren Ordner gibt es keine Zeile, die den Fokus aufnimmt; der Fokus fiel auf die Seite zurück und die Tastenbelegung der Dateiansicht griff nicht mehr.
- **Fix:** Die Dateikarte ist ein Fokusfang (`tabIndex={-1}`, Klasse `nc-files-root`); in globals.css ohne Rahmen, im dunklen Modus bleibt die Haarlinie erhalten.
- **Files modified:** FileBrowser.tsx, globals.css
- **Commit:** 630398f
**2. [Rule 1 - Bug] e2e-Skripte hingen vom Anfangszustand ab**
- **Found during:** Aufgabe 6 (Wiederholung auf dem neu gebauten Stack)
- **Issue:** `e2e-settings.sh` erwartete eine noch nicht gespeicherte Adresse (ein unveränderter Wert antwortet bewusst ohne Prüfung); `e2e-connect.sh` scheiterte an übrig gebliebenen Tessera-Zugängen in der Nextcloud (ein Adresswechsel lässt Konten ablaufen, ohne zu widerrufen, siehe Administrationshandbuch).
- **Fix:** e2e-settings stellt erst auf eine andere Adresse; e2e-lib bekommt `e2e_nc_purge_tokens`, e2e-connect räumt vor dem Start auf. Dazu Prüfung der Serverkennung in e2e-settings.
- **Commit:** 630398f
**3. [Rule 2 - Missing critical] Doku zum Brute-Force-Befehl vorsichtig formuliert**
- Der occ-Befehl `config:app:set bruteForce whitelist_0` und `security:bruteforce:reset` stehen in den Handbüchern; Aussagen über die Oberfläche der Nextcloud zur Ermittlung der Absenderadresse wurden bewusst weggelassen (nicht gemessen), stattdessen der Hinweis auf das Zugriffsprotokoll des Webservers.
### Hinweise ohne Abweichung
- Bildschirmaufnahmen mit playwright-core statt Playwright MCP (mit Absprache aus der Übergabe von Aufgabe 5); gleiche Prüfungen, gleiche Ablage.
- Das Projekt-`biome format` weist in Dateien aus früheren Aufgaben (module-loader.ts, packages/shared) Formatabweichungen aus; Projektvorgabe ist `biome lint`, daher unverändert gelassen.
- Die RLS-Dokumentation musste in Aufgabe 6 nicht neu gezählt werden (keine neuen Tabellen oder Richtlinien).
## Known Stubs
Keine. (Das Modul hat bewusst kein Dashboard-Widget; Etappe 2 ist offen und nicht Teil dieses Plans.)
## Threat Flags
Keine neuen Flächen gegenüber dem Bedrohungsmodell. Die Serverkennung (T-mzu-01/T-mzu-08) ist enthalten: feste Pfade, keine Weiterleitung, Logo nach Bytes erkannt, SVG nur mit CSP-Sandbox.
## Prüfliste für die echte Umgebung (vom Benutzer)
1. **Desktop-App:** Neue Fassung herunterladen und prüfen, dass der Link „Anmeldung bei Nextcloud öffnen“ in der Desktop-App den System-Browser öffnet (Opener-Skript fängt `target=_blank`), und dass nach „Zugriff gewähren“ die Dateiansicht erscheint.
2. **Nginx Proxy Manager (Tessera-Adresse):** `client_max_body_size` mindestens `10m` (besser `64m`) und Lese-/Sendezeitlimits mindestens 120 s für die Tessera-Adresse (alpha und live) setzen, dann eine Datei über 10 MB hochladen. Ohne das bricht der Upload beim ersten 8-MiB-Stück ab (Betriebshandbuch, Fehlerbild).
3. **Nextcloud Brute-Force-Ausnahme:** Auf der echten Nextcloud die Adresse des Tessera-Servers eintragen: `occ config:app:set bruteForce whitelist_0 --value=<IP des Tessera-Servers>` (bei Proxy dessen Adresse; die tatsächlich ankommende Adresse im Zugriffsprotokoll prüfen). Danach in Tessera unter Dateien → Einstellungen die Adresse eintragen und „Verbindung prüfen“ klicken.
4. **Zertifikat:** Nutzt die echte Nextcloud eine interne Zertifizierungsstelle, `NODE_EXTRA_CA_CERTS` im api-Container setzen (Betriebshandbuch Kapitel 3), sonst meldet die Prüfung ein Zertifikatsproblem.
5. **Zwei-Faktor-Anmeldung:** Mit einem Konto mit Zwei-Faktor-Anmeldung „Im Browser anmelden“ durchspielen; ein Konto ohne Zwei-Faktor per Passwort anmelden; danach „Abmelden“ und in der Nextcloud unter Sicherheit prüfen, dass der Eintrag „Tessera“ verschwunden ist.
6. **Modul freischalten:** Im Marktplatz „Dateien“ aktivieren und die Freigabe erteilen (Benutzen für alle, Verwalten für die, die die Adresse pflegen). Beim Deployen daran denken: `up` baut nicht neu, `--build` bzw. neue Abbilder ziehen; die Migrationen laufen beim Start der API.
7. **Ablage-Kachel mit echtem Logo:** Prüfen, dass Name, Logo und Farbe der echten Nextcloud auf dem Anmeldebildschirm stimmen (bei dunklem Logo auf dunkler Themenfarbe ggf. den Eindruck bewerten).
## Self-Check: PASSED
- Dateien vorhanden: nextcloud-server-info.ts, nextcloud-server-info.spec.ts, ServerIdentity.tsx, Screenshots t6-dark (14) und t6-light (9).
- Commits in der Historie: 9606967, d00b6ff, 8bee65c, 4d4a0b3, 86fbe8f, 630398f.