diff --git a/features/INDEX.md b/features/INDEX.md index 06fa7d6..65bb0bd 100644 --- a/features/INDEX.md +++ b/features/INDEX.md @@ -24,12 +24,23 @@ | PROJ-10 | Deployment (Docker Compose + Synapse Installer) | Deployed | [PROJ-10-deployment.md](PROJ-10-deployment.md) | 2026-09-11 | | PROJ-11 | Chat (Matrix Client-Server API) | Deployed | [PROJ-11-chat.md](PROJ-11-chat.md) | 2026-09-11 | | PROJ-12 | Automatische Löschfrist für ausgetretene Mitglieder (DSGVO) | Planned | [PROJ-12-mitglieder-loeschfrist.md](PROJ-12-mitglieder-loeschfrist.md) | 2026-09-11 | +| PROJ-13 | SYSTEM_ADMIN-Rolle | Planned | [PROJ-13-system-admin-rolle.md](PROJ-13-system-admin-rolle.md) | 2026-09-12 | +| PROJ-14 | Synapse Raum-Übersicht (System-Admin) | Planned | [PROJ-14-synapse-raum-uebersicht.md](PROJ-14-synapse-raum-uebersicht.md) | 2026-09-12 | +| PROJ-15 | Media-Cleanup (System-Admin) | Planned | [PROJ-15-media-cleanup.md](PROJ-15-media-cleanup.md) | 2026-09-12 | +| PROJ-16 | Federation-Status (System-Admin) | Planned | [PROJ-16-federation-status.md](PROJ-16-federation-status.md) | 2026-09-12 | +| PROJ-17 | Nachricht melden (Chat) | Planned | [PROJ-17-nachricht-melden.md](PROJ-17-nachricht-melden.md) | 2026-09-12 | +| PROJ-18 | Reports/Moderation (System-Admin) | Planned | [PROJ-18-reports-moderation.md](PROJ-18-reports-moderation.md) | 2026-09-12 | Noch nicht gestartet (siehe [docs/PRD.md](../docs/PRD.md) Core Features): dedizierte Tenant-Isolation-Tests. P2 (Medien-Anhänge, Push) bewusst aus PROJ-11 ausgeklammert, siehe dessen Non-Goals. +Empfohlene Baureihenfolge PROJ-13 bis PROJ-18: PROJ-13 → PROJ-14 → PROJ-15 → PROJ-16 → PROJ-17 → PROJ-18 (Reports brauchen die Melde-Funktion zuerst). + Nachträge 2026-09-11 (commit bc4d749): Passwort-Reset → [[PROJ-1]], Mitglied hart löschen → [[PROJ-3]]. Nachtrag 2026-09-11 (commit ffe2d45): API-Zugriff bei suspended/left sofort entziehen (nicht nur Matrix) → [[PROJ-3]]. +Nachtrag 2026-09-12: Admin-Direkt-Passwort-Setzen → [[PROJ-3]]. + +Noch nicht gespect't, auf Nutzerwunsch für später vorgemerkt: eigenständiger E-Mail+Passwort-Login der Mitglieder direkt bei Synapse/Element (aktuell nur über TalkTime via Puppet-Login möglich, siehe [[PROJ-11]]). -## Next Available ID: PROJ-13 +## Next Available ID: PROJ-19 diff --git a/features/PROJ-13-system-admin-rolle.md b/features/PROJ-13-system-admin-rolle.md new file mode 100644 index 0000000..d38fae7 --- /dev/null +++ b/features/PROJ-13-system-admin-rolle.md @@ -0,0 +1,36 @@ +# PROJ-13: SYSTEM_ADMIN-Rolle (Grundlage für System-Admin-Panel) + +**Status:** Planned +**Created:** 2026-09-12 + +## Summary +Erste Umsetzung der in PRD/vereinschat-Doku vorgesehenen `SYSTEM_ADMIN`-Rolle: ein plattformweiter Admin, der Zugriff auf vereinsübergreifende Synapse-Verwaltungsfunktionen hat (Media-Cleanup [[PROJ-15]], Raum-Übersicht [[PROJ-14]], Federation-Status [[PROJ-16]], Reports [[PROJ-18]]). Bisher existiert im Code nur organisationsgebundene Rollen (`ORG_ADMIN` etc.) — kein Konzept für "sieht Daten aller Vereine". Diese Rolle ist rein plattformweit, unabhängig von jeder Organisation, und wird bewusst **nicht** über eine UI vergeben (MVP-Scope), sondern manuell in der Datenbank gesetzt. + +## Dependencies +- Requires: [[PROJ-1]] (Auth) — Rolle hängt am `users`-Datensatz, nicht an einer Mitgliedschaft +- Blocks: [[PROJ-14]], [[PROJ-15]], [[PROJ-16]], [[PROJ-18]] — alle vier brauchen diesen Berechtigungscheck + +## User Stories +1. Als Plattformbetreiber will ich einen User als SYSTEM_ADMIN markieren können, um Zugriff auf das System-Admin-Panel zu bekommen. +2. Als SYSTEM_ADMIN will ich beim Login/Laden der App erkannt werden, damit die Admin-Panel-Navigation für mich sichtbar ist. +3. Als normaler Nutzer (auch als ORG_ADMIN eines Vereins) will ich das System-Admin-Panel **nicht** sehen oder aufrufen können. + +## Acceptance Criteria +- [ ] `users`-Tabelle bekommt Feld `isSystemAdmin` (boolean, `not null default false`) +- [ ] Neuer Helper `requesterIsSystemAdmin(userId)` in `backend/src/lib/authz.ts` +- [ ] Alle System-Admin-Routen (PROJ-14/15/16/18) prüfen diesen Helper, `403` sonst +- [ ] `GET /api/auth/me` liefert `isSystemAdmin` mit, damit das Frontend die Navigation entsprechend zeigt/versteckt +- [ ] Kein UI zum Setzen/Entfernen der Rolle — nur per direktem SQL-Update (`UPDATE users SET is_system_admin = true WHERE email = '...'`), dokumentiert in der Spec, nicht im Produkt +- [ ] Frontend: neuer Navigationspunkt "System-Admin" im Dashboard, nur sichtbar wenn `isSystemAdmin` + +## Edge Cases +- SYSTEM_ADMIN ist gleichzeitig auch ORG_ADMIN eines Vereins → beide Rollen unabhängig, kein Konflikt +- Letzter SYSTEM_ADMIN wird versehentlich per SQL entfernt → kein Lockout-Schutz im MVP (bewusst, da eh nur per DB gesetzt/entfernt wird — Betreiber hat DB-Zugriff) +- Kompromittierter SYSTEM_ADMIN-Account → sieht/verwaltet Daten aller Vereine; kein zusätzlicher Schutz (z.B. 2FA) im MVP-Scope + +## Non-Goals +- UI zum Ernennen/Entziehen von SYSTEM_ADMIN (bewusst DB-only für MVP) +- Feingranulare Rechte innerhalb SYSTEM_ADMIN (z.B. "darf nur Media, nicht Reports") — ein SYSTEM_ADMIN darf alles im Panel + +## Notes +Reine Backend-/Grundlagen-Feature ohne eigenständigen sichtbaren Mehrwert — wird zusammen mit dem ersten der vier abhängigen Features (empfohlen: [[PROJ-14]]) implementiert, nicht isoliert deploybar im engeren Sinn (aber testbar: Rolle setzen, `/api/auth/me` prüfen). diff --git a/features/PROJ-14-synapse-raum-uebersicht.md b/features/PROJ-14-synapse-raum-uebersicht.md new file mode 100644 index 0000000..60ecc3b --- /dev/null +++ b/features/PROJ-14-synapse-raum-uebersicht.md @@ -0,0 +1,33 @@ +# PROJ-14: Synapse Raum-Übersicht (System-Admin) + +**Status:** Planned +**Created:** 2026-09-12 + +## Summary +Liste aller Matrix-Räume auf der angebundenen Synapse-Instanz für SYSTEM_ADMIN, unabhängig von TalkTime-Team-/Vereinszuordnung — nachgebaut aus synapse-admin's Raum-Übersicht. Nutzt Synapse Admin API (`GET /_synapse/admin/v1/rooms`), keine neue TalkTime-Datenbanktabelle nötig. + +## Dependencies +- Requires: [[PROJ-13]] (SYSTEM_ADMIN-Rolle) — Zugriffsschutz +- Requires: [[PROJ-9]] (Synapse-Integration) — nutzt bestehenden `services/synapse.ts`-Wrapper + +## User Stories +1. Als SYSTEM_ADMIN will ich alle Matrix-Räume sehen (Name, Mitgliederzahl, Erstellungsdatum), um einen Überblick über die Gesamtnutzung zu haben. +2. Als SYSTEM_ADMIN will ich erkennen, welcher Raum zu welchem TalkTime-Team/Verein gehört (Abgleich mit `chat_rooms`-Tabelle), um Zuordnung nachvollziehen zu können. +3. Als SYSTEM_ADMIN will ich einen verwaisten Raum (keine TalkTime-Zuordnung mehr, z.B. nach Datenbereinigung) direkt löschen/purgen können. + +## Acceptance Criteria +- [ ] `GET /api/system-admin/rooms` — SYSTEM_ADMIN-only, ruft `_synapse/admin/v1/rooms` ab, reichert um TalkTime-Zuordnung an (JOIN gegen lokale `chat_rooms`-Tabelle über `matrix_room_id`) +- [ ] Frontend-Tabelle: Raumname, Mitgliederzahl, TalkTime-Zuordnung (Vereinsname + Team, oder "nicht zugeordnet"), Erstellungsdatum +- [ ] `DELETE /api/system-admin/rooms/:roomId` — purged den Raum bei Synapse; falls in `chat_rooms` verzeichnet, wird der lokale Eintrag ebenfalls gelöscht (kaskadiert nicht automatisch, da externe ID) +- [ ] Bestätigungsdialog vor dem Löschen (destruktive Aktion, analog Mitglied-Löschen aus [[PROJ-3]]) +- [ ] Pagination (Synapse Admin API liefert selbst paginiert, `from`/`limit`-Parameter durchreichen) + +## Edge Cases +- Raum ist ein aktiver Team-Raum (in `chat_rooms` verzeichnet, Team existiert noch) → Löschen weiterhin technisch möglich, aber Warnhinweis im Dialog ("Dieser Raum ist aktiv einem Team zugeordnet — Team-Chat wird dadurch zerstört") +- Synapse nicht erreichbar → Fehleranzeige, keine leere Liste vortäuschen +- Sehr viele Räume (>1000) → Pagination zwingend, nicht alles auf einmal laden +- Raum bereits gelöscht (Race Condition, zwei Admins gleichzeitig) → Synapse liefert Fehler, Frontend zeigt generische Fehlermeldung statt Absturz + +## Non-Goals +- Raum-Inhalte einsehen (Nachrichten lesen) — reine Metadaten-Übersicht, kein Nachrichten-Zugriff für Admin +- Raum-Einstellungen ändern (Name, History-Visibility) über dieses Panel — nur Übersicht + Löschen diff --git a/features/PROJ-15-media-cleanup.md b/features/PROJ-15-media-cleanup.md new file mode 100644 index 0000000..fa10ab0 --- /dev/null +++ b/features/PROJ-15-media-cleanup.md @@ -0,0 +1,35 @@ +# PROJ-15: Media-Cleanup (System-Admin) + +**Status:** Planned +**Created:** 2026-09-12 + +## Summary +Manuelles Aufräumen alter/verwaister Medien-Dateien auf Synapse — nachgebaut aus synapse-admin's Media-Verwaltung. Nutzt Synapse Admin API (`POST /_synapse/admin/v1/purge_media_cache`, `DELETE /_synapse/admin/v1/media//`). Bewusst nur manueller Trigger (kein Zeitplan), da PROJ-11 aktuell keine Medien-Anhänge unterstützt (P2 laut PRD) — betrifft also v.a. Avatare/Profilbilder und den Media-Cache. + +## Dependencies +- Requires: [[PROJ-13]] (SYSTEM_ADMIN-Rolle) +- Requires: [[PROJ-9]] (Synapse-Integration) + +## User Stories +1. Als SYSTEM_ADMIN will ich sehen, wie viel Speicherplatz der Synapse-Media-Store aktuell belegt, um einschätzen zu können ob Aufräumen nötig ist. +2. Als SYSTEM_ADMIN will ich alten, ungenutzten Media-Cache (remote/federierte Medien, die lokal zwischengespeichert wurden) gezielt löschen können. +3. Als SYSTEM_ADMIN will ich vor dem Löschen sehen, wie viel dadurch freigegeben würde, bevor ich bestätige. + +## Acceptance Criteria +- [ ] `GET /api/system-admin/media/summary` — SYSTEM_ADMIN-only, liefert Media-Store-Größe (falls über Synapse Admin API auslesbar) oder Dateisystem-Statistik vom Synapse-Host +- [ ] `POST /api/system-admin/media/purge-cache` — Parameter: `before` (Datum, Standard-Vorschlag: 30 Tage), löst `purge_media_cache` bei Synapse aus +- [ ] Frontend zeigt vor dem Klick auf "Aufräumen" eine Vorschau/Warnung mit dem gewählten Stichtag, kein automatischer Sofort-Trigger ohne Bestätigungsdialog +- [ ] Ergebnis der Aufräum-Aktion (Anzahl gelöschter Medien, falls von Synapse zurückgegeben) wird angezeigt +- [ ] Aktion wird im Audit-Log vermerkt (`action: "system.media_purged"`, kein `organizationId`-Bezug möglich — neues optionales Feld oder System-weite Audit-Kategorie klären in Architektur) + +## Edge Cases +- Synapse Admin API für Media-Größe nicht verfügbar/nicht dokumentiert für die genutzte Synapse-Version → Fallback: nur Purge-Aktion anbieten, keine Vorab-Größenanzeige, das im UI klar kommunizieren statt falsche Zahlen zu zeigen +- `purge_media_cache` betrifft nur den **Remote-Media-Cache** (von föderierten Servern zwischengespeicherte Bilder), nicht lokal hochgeladene Medien — da Federation komplett deaktiviert ist (siehe PRD-Constraint), ist der praktische Nutzen dieser Aktion aktuell gering. Muss im UI-Text ehrlich kommuniziert werden, damit kein falscher Eindruck entsteht ("räumt alles auf") +- Sehr kurzes `before`-Datum (z.B. heute) → Bestätigungsdialog warnt zusätzlich bei ungewöhnlich aggressiven Werten + +## Non-Goals +- Löschen einzelner, konkreter Medien-Dateien nach ID (synapse-admin kann das; hier zunächst nur der pauschale Cache-Purge, da für TalkTime ohne Medien-Feature kaum relevant) +- Automatischer/geplanter Cleanup — nur manueller Trigger im MVP + +## Notes +Praktischer Nutzen ist aktuell begrenzt, solange PROJ-11 keine Medien-Anhänge hat und Federation aus ist — Feature ist v.a. Vorbereitung für P2 (Medien-Anhänge laut PRD-Roadmap). Architektur-Skill sollte das gegenüber dem Nutzer transparent machen. diff --git a/features/PROJ-16-federation-status.md b/features/PROJ-16-federation-status.md new file mode 100644 index 0000000..6d7bd28 --- /dev/null +++ b/features/PROJ-16-federation-status.md @@ -0,0 +1,29 @@ +# PROJ-16: Federation-Status (System-Admin) + +**Status:** Planned +**Created:** 2026-09-12 + +## Summary +Anzeige, welche externen Matrix-Server aktuell föderiert sind — nachgebaut aus synapse-admin's Federation-Ansicht. Da Federation laut Synapse-Config (`federation_domain_whitelist: []`) komplett deaktiviert ist, zeigt diese Ansicht im Normalbetrieb **keine** verbundenen Server. Bewusst trotzdem gebaut (Nutzerentscheidung), als Vorbereitung falls Federation später aktiviert wird. + +## Dependencies +- Requires: [[PROJ-13]] (SYSTEM_ADMIN-Rolle) +- Requires: [[PROJ-9]] (Synapse-Integration) + +## User Stories +1. Als SYSTEM_ADMIN will ich sehen, ob und mit welchen externen Servern föderiert wird, um die Isolation der Plattform zu verifizieren. +2. Als SYSTEM_ADMIN will ich bei aktivierter Federation einen fehlgeschlagenen Verbindungsversuch zu einem Server erkennen können. + +## Acceptance Criteria +- [ ] `GET /api/system-admin/federation` — SYSTEM_ADMIN-only, ruft Synapse Admin API für Federation-Status ab (`GET /_synapse/admin/v1/federation/destinations`) +- [ ] Tabelle: Server-Name, letzter erfolgreicher Kontakt, Retry-Status (falls fehlgeschlagen) +- [ ] Leerer Zustand explizit als "Federation ist deaktiviert — keine externen Server verbunden" dargestellt, nicht als generisches "Keine Daten", damit klar ist dass das der Erwartungswert ist +- [ ] Kein Schreibzugriff (kein "Server blockieren"-Button im MVP) — nur Anzeige + +## Edge Cases +- Federation deaktiviert (aktueller Normalfall) → Synapse Admin API liefert vermutlich leere Liste oder einen Fehler, je nach Synapse-Version — beides sauber als "deaktiviert" behandeln, nicht als Fehlerzustand der Seite +- Federation wird später aktiviert, aber `federation_domain_whitelist` bleibt leer → weiterhin keine echten Verbindungen, gleiche Anzeige + +## Non-Goals +- Federation aktivieren/konfigurieren über dieses Panel — bleibt Server-Konfigurationsaufgabe (`homeserver.yaml`), keine UI dafür +- Server explizit blockieren/whitelisten über UI diff --git a/features/PROJ-17-nachricht-melden.md b/features/PROJ-17-nachricht-melden.md new file mode 100644 index 0000000..234666c --- /dev/null +++ b/features/PROJ-17-nachricht-melden.md @@ -0,0 +1,31 @@ +# PROJ-17: Nachricht melden (Chat) + +**Status:** Planned +**Created:** 2026-09-12 + +## Summary +Mitglieder können eine einzelne Chat-Nachricht im Team-Chat ([[PROJ-11]]) melden. Nutzt Matrix' eingebauten Report-Mechanismus (`POST /_matrix/client/v3/rooms/{roomId}/report/{eventId}`) — direkt vom Browser aus über den bestehenden matrix-js-sdk-Client, kein neuer Backend-Endpunkt nötig. Voraussetzung für [[PROJ-18]] (Admin sieht die Meldungen). + +## Dependencies +- Requires: [[PROJ-11]] (Chat) — Meldung ist eine Aktion auf einer bestehenden Chat-Nachricht + +## User Stories +1. Als Team-Mitglied will ich eine unangemessene Nachricht melden können, ohne den Absender direkt konfrontieren zu müssen. +2. Als Team-Mitglied will ich eine Bestätigung sehen, dass meine Meldung angekommen ist. +3. Als Team-Mitglied will ich nicht sehen können, ob/wie oft eine Nachricht bereits von anderen gemeldet wurde (keine Meldungs-Zähler im Chat selbst — das ist Admin-Sache). + +## Acceptance Criteria +- [ ] Jede fremde Nachricht (nicht die eigenen) bekommt eine "Melden"-Option (z.B. im Kontextmenü/Hover-Aktion in `MessageList.tsx`) +- [ ] Klick öffnet einen kleinen Dialog mit optionalem Freitext-Grund (Matrix' Report-API unterstützt ein `reason`-Feld) +- [ ] Meldung wird über den bestehenden matrix-js-sdk-Client direkt an Synapse gesendet (`client.reportEvent(roomId, eventId, score, reason)`) — kein TalkTime-Backend-Aufruf nötig, da die Nachricht ohnehin nie durchs Backend läuft +- [ ] Erfolgs-/Fehler-Feedback im Dialog (z.B. Toast oder Inline-Meldung) +- [ ] Eigene Nachrichten können nicht gemeldet werden (kein Menüpunkt) + +## Edge Cases +- Mehrfaches Melden derselben Nachricht durch dieselbe Person → Matrix/Synapse lässt das zu (kein clientseitiger Duplikatsschutz im MVP), Admin sieht ggf. mehrere Einträge für dasselbe Event +- Melden schlägt fehl (Netzwerk/Synapse down) → Fehlermeldung, kein stiller Fehlschlag +- Nachricht wurde zwischenzeitlich gelöscht/redacted → Meldung bezieht sich auf die Event-ID, funktioniert auch für redacted Events laut Matrix-Spec (Synapse speichert den Report trotzdem) + +## Non-Goals +- Automatische Konsequenz bei Meldung (z.B. Nachricht automatisch ausblenden) — rein informativ für Admin, keine Auto-Moderation +- Melde-Grund-Kategorien (Spam/Belästigung/etc.) — nur Freitext im MVP diff --git a/features/PROJ-18-reports-moderation.md b/features/PROJ-18-reports-moderation.md new file mode 100644 index 0000000..5f71b0f --- /dev/null +++ b/features/PROJ-18-reports-moderation.md @@ -0,0 +1,33 @@ +# PROJ-18: Reports/Moderation (System-Admin) + +**Status:** Planned +**Created:** 2026-09-12 + +## Summary +SYSTEM_ADMIN sieht alle über [[PROJ-17]] gemeldeten Nachrichten Synapse-weit — nachgebaut aus synapse-admin's Reports-Ansicht. Nutzt Synapse Admin API (`GET /_synapse/admin/v1/event_reports`). + +## Dependencies +- Requires: [[PROJ-13]] (SYSTEM_ADMIN-Rolle) +- Requires: [[PROJ-17]] (Nachricht melden) — ohne Meldungen ist diese Ansicht leer +- Requires: [[PROJ-9]] (Synapse-Integration) + +## User Stories +1. Als SYSTEM_ADMIN will ich alle gemeldeten Nachrichten sehen (Rauminhalt, Melder, Grund, Zeitpunkt), um moderieren zu können. +2. Als SYSTEM_ADMIN will ich erkennen, welchem TalkTime-Verein/Team ein gemeldeter Raum zugeordnet ist, um den zuständigen Vereinsadmin informieren zu können. +3. Als SYSTEM_ADMIN will ich eine gemeldete Nachricht als "bearbeitet"/"erledigt" markieren können, damit die Liste nicht mit alten Meldungen zuwächst. + +## Acceptance Criteria +- [ ] `GET /api/system-admin/reports` — SYSTEM_ADMIN-only, ruft `event_reports` von Synapse ab, reichert um TalkTime-Zuordnung an (Raum → `chat_rooms` → Team/Verein) +- [ ] Tabelle: Zeitpunkt, meldender Nutzer (Matrix-ID), betroffene Nachricht (Textauszug, falls noch verfügbar), Grund, zugeordneter Verein/Team +- [ ] "Als erledigt markieren" — da Synapse selbst keinen "resolved"-Status für Reports kennt, wird das lokal in TalkTime getrackt (neue Tabelle `handled_reports` mit `synapseReportId`, `handledBy`, `handledAt`) +- [ ] Erledigte Meldungen standardmäßig ausgeblendet, mit Filter "auch erledigte anzeigen" +- [ ] Audit-Log-Eintrag beim Markieren als erledigt + +## Edge Cases +- Gemeldete Nachricht wurde inzwischen gelöscht (Absender/Admin hat sie entfernt) → Report bleibt sichtbar, Textauszug fehlt/zeigt "Nachricht nicht mehr verfügbar" +- Gemeldeter Raum hat keine TalkTime-Zuordnung mehr (Team gelöscht) → zeigt "Zuordnung nicht mehr vorhanden", Report bleibt trotzdem einsehbar +- Zwei SYSTEM_ADMINs markieren gleichzeitig denselben Report als erledigt → letzter Schreibzugriff gewinnt, keine Konflikt-Behandlung im MVP + +## Non-Goals +- Direkte Moderationsaktionen aus der Report-Ansicht heraus (Nachricht löschen, Nutzer sperren) — Admin muss dafür in die Raum-Übersicht ([[PROJ-14]]) bzw. bestehende TalkTime-Mitgliederverwaltung ([[PROJ-3]]) wechseln +- Benachrichtigung des zuständigen Vereinsadmins per E-Mail bei neuer Meldung — reine Pull-Ansicht im MVP