Files
talktime/features/PROJ-14-synapse-raum-uebersicht.md
T
patrickandClaude Sonnet 5 f81d2d7906 feat(PROJ-14): backend for Synapse room overview (SYSTEM_ADMIN)
GET /api/system-admin/rooms and DELETE /api/system-admin/rooms/:roomId,
both gated on requireSystemAdmin from PROJ-13. No new DB table — reads
Synapse's room list directly, cross-references the existing chat_rooms
table for org/team attribution via one inArray join.

deleteRoom() uses the v2 async purge endpoint (not v1, which blocks
until the purge finishes and would time out on a large room).
normalizeCreationTs() handles Synapse versions that return seconds
instead of documented milliseconds — moot on this deployment's Synapse
version, which omits creation_ts from the rooms list entirely; verified
live, code already treats a missing value as null rather than guessing.

Known gap: audit_logs.organization_id is NOT NULL, so deleting an
unassigned room can't get a proper audit entry (falls back to a server
log warning). Flagged rather than silently worked around.

Verified end-to-end on talkhub/matrix: 403 without the SYSTEM_ADMIN
flag, 200 with it, and used the new delete endpoint to clean up a
genuinely orphaned test room left over from earlier QA.

Frontend (/system-admin route, rooms table, delete dialog) still
outstanding — the nav button added in PROJ-13 stays a dead link until
that lands.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
2026-09-12 21:06:46 +02:00

6.1 KiB

PROJ-14: Synapse Raum-Übersicht (System-Admin)

Status: In Progress Created: 2026-09-12

Implementation Notes (Backend, 2026-09-12)

Backend umgesetzt, Frontend-Seite (/system-admin Route + Tabelle + Lösch-Dialog) steht noch aus.

  • backend/src/services/synapse.ts: listRooms() (GET /_synapse/admin/v1/rooms, from/limit/search_term durchgereicht), deleteRoom() (v2 async Delete mit purge: true — v1 blockiert bei großen Räumen bis zum HTTP-Timeout), normalizeCreationTs() (Synapse liefert je nach Version Sekunden statt Millisekunden).
  • backend/src/routes/system-admin.ts (neu, in server.ts registriert): GET /api/system-admin/rooms + DELETE /api/system-admin/rooms/:roomId, beide preHandler: [app.authenticate, requireSystemAdmin].
  • Keine neue Tabelle, keine Migration: chat_rooms.matrix_room_id ist bereits unique (Index vorhanden), Zuordnung via einem inArray-JOIN gegen organizations/teams (kein N+1).
  • Abweichung/Einschränkung: audit_logs.organization_id ist NOT NULL — das Löschen eines nicht zugeordneten Raums kann daher nicht ins Audit-Log, nur ins Server-Log (app.log.warn). Zugeordnete Räume werden regulär als system_admin.room.deleted auditiert.
  • Edge Cases: Synapse nicht erreichbar → 502 mit Fehlertext (nie leere Liste); Raum bereits gelöscht (404 von Synapse) → lokale Zuordnung wird trotzdem aufgeräumt, dann 404.

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

Tech Design (Solution Architect)

A) Komponenten-Struktur

System-Admin-Bereich (NEU, "/system-admin")
└── Seite "Räume"
    ├── Tabelle: Raumname | Mitglieder | Zuordnung (Verein/Team oder "nicht zugeordnet") | Erstellt am | Aktion
    ├── "Weiter"/"Zurück"-Paginierung (analog Mitgliederliste aus PROJ-3)
    └── Löschen-Bestätigungsdialog (analog Mitglied-Löschen aus PROJ-3)

Erste Unterseite im neuen System-Admin-Bereich — die Navigation dorthin (aktuell ein Button, der ins Leere zeigt, siehe PROJ-13) bekommt jetzt ein echtes Ziel.

B) Datenfluss (in einfachen Worten)

Es wird keine neue Tabelle in der TalkTime-Datenbank angelegt. Der Ablauf:

  1. Backend fragt Synapse nach der Liste aller Räume (Name, Mitgliederzahl, Erstellungsdatum) — das ist reine Server-Information, die Synapse selbst schon führt.
  2. Backend gleicht diese Liste gegen die bereits vorhandene TalkTime-Tabelle ab, in der steht, welcher Matrix-Raum zu welchem Team/Verein gehört (dieselbe Tabelle, die auch der Chat aus PROJ-11 nutzt).
  3. Ergebnis: eine kombinierte Liste, die sowohl "reine Synapse-Info" als auch "TalkTime-Zuordnung, falls vorhanden" zeigt.
  4. Löschen eines Raums geht direkt an Synapse; falls der Raum in der TalkTime-Zuordnung stand, wird dieser Eintrag ebenfalls entfernt, damit keine verwaiste Zuordnung auf einen nicht mehr existierenden Raum zurückbleibt.

C) Tech-Entscheidungen (Begründung)

  • Keine eigene Datenspeicherung der Raumliste: Synapse ist bereits die "Quelle der Wahrheit" für Räume — eine Kopie in TalkTime pflegen hieße, zwei Datenstände synchron halten zu müssen, ohne echten Nutzen.
  • Zugriffsschutz über die in PROJ-13 gebaute Prüfung: Kein neuer Berechtigungsmechanismus, konsistent mit allen anderen System-Admin-Funktionen.
  • Löschen bricht Chat für aktive Teams bewusst nicht automatisch ab: Ein Löschen-Button, der aktive Team-Chats stillschweigend verschont, wäre für einen Admin verwirrend ("warum lässt sich das nicht löschen?"). Stattdessen: löschen ist möglich, aber mit deutlicher Warnung vorher.

D) Abhängigkeiten (neue Pakete)

Keine — nutzt die bestehende Synapse-Anbindung aus PROJ-9/PROJ-11.

Offene Voraussetzung

Erfordert PROJ-13 (Berechtigungsprüfung) als Grundlage — bereits umgesetzt.