feat(admin): "Verlauf löschen" pro Nutzer (Konto bleibt)

Store.delete_user_history (loescht Sessions + Nachrichten eines Nutzers, ohne
Konto/Erinnerungen/Usage) + DELETE /api/admin/users/{id}/history (audit-geloggt)
+ Button im Admin-Nutzer-Bereich.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-30 09:08:43 +02:00
commit 69894b1e7b
3 changed files with 36 additions and 0 deletions

View file

@ -126,6 +126,11 @@ class Store(ABC):
Nutzungsdaten). Anonymer Nutzer kann nicht geloescht werden.
Liefert True, wenn der Nutzer existierte und geloescht wurde."""
@abstractmethod
def delete_user_history(self, user_id: str) -> int:
"""Loescht NUR den Gespraechsverlauf (Sessions + Nachrichten) eines Nutzers;
Konto, Erinnerungen und Nutzungsdaten bleiben. Liefert die Zahl geloeschter Nachrichten."""
@abstractmethod
def reset_token(self, user_id: str) -> tuple[User, str] | None:
"""Generiert einen neuen Token fuer den Nutzer; der alte wird sofort ungueltig.
@ -492,6 +497,17 @@ class SQLiteStore(Store):
conn.execute("DELETE FROM users WHERE id = ?", (user_id,))
return True
def delete_user_history(self, user_id: str) -> int:
with self._connect() as conn:
cur = conn.execute(
"DELETE FROM messages WHERE session_id IN"
" (SELECT id FROM sessions WHERE user_id = ?)",
(user_id,),
)
n = cur.rowcount
conn.execute("DELETE FROM sessions WHERE user_id = ?", (user_id,))
return n
def reset_token(self, user_id: str) -> tuple[User, str] | None:
with self._connect() as conn:
row = conn.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()