- Add CASCADE DELETE to MagicLinkToken and AdminTakeoverSession models - Update admin_api.py to support multi-container deletion - Add DSGVO-compliant user deletion (removes tokens and sessions) - Integrate Sonner toast system in frontend - Add bulk-operations UI (select, block, unblock, delete users) - Implement two-step confirmation for critical actions - Update TypeScript config for Set iteration - Add comprehensive documentation in docs/guides/
10 KiB
10 KiB
Admin-Dashboard: Verbesserte Container- und User-Löschung
Datum: 02.02.2026 Version: 2.0 Status: ✅ Vollständig implementiert
📋 Übersicht
Diese Dokumentation beschreibt die Verbesserungen des Admin-Dashboards:
- Multi-Container-Deletion - Alle Container eines Users löschen (nicht nur Primary)
- Toast-Benachrichtigungen - Modernes UI statt primitiver Alerts
- Bulk-Operations - Mehrere User gleichzeitig verwalten (Sperren, Löschen, etc.)
- DSGVO-Compliance - Vollständige Datenlöschung (MagicLinkToken, AdminTakeoverSession)
🔧 Technische Änderungen
1. Backend - Multi-Container & DSGVO
Datei: admin_api.py
DELETE /api/admin/users/<id>/container (aktualisiert)
Löscht alle Docker-Container eines Users (nicht nur Primary Container):
# Vorher (begrenzt auf Primary):
if user.container_id:
container_mgr.stop_container(user.container_id)
container_mgr.remove_container(user.container_id)
# Nachher (alle Container):
for container in user.containers:
if container.container_id:
container_mgr.stop_container(container.container_id)
container_mgr.remove_container(container.container_id)
db.session.delete(container)
Response:
{
"message": "Alle 3 Container von user@example.com wurden gelöscht",
"deleted": 3,
"failed": []
}
DELETE /api/admin/users/<id> (aktualisiert - DSGVO)
Löscht einen User komplett mit allen Daten:
# 1. Docker-Container löschen
# 2. MagicLinkToken löschen (DSGVO: IP-Adressen)
# 3. AdminTakeoverSession löschen (als Target-User)
# 4. User-Account löschen (CASCADE löscht UserContainer)
Response mit DSGVO-Summary:
{
"message": "User test@example.com wurde vollständig gelöscht",
"summary": {
"containers_deleted": 3,
"containers_failed": [],
"magic_tokens_deleted": 5,
"takeover_sessions_deleted": 0
}
}
2. Datenbank - CASCADE DELETE
Datei: models.py
MagicLinkToken (Zeile 110-118):
user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=False)
user = db.relationship('User', backref=db.backref('magic_tokens', lazy=True, cascade='all, delete-orphan'))
AdminTakeoverSession (Zeile 171-180):
admin_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='SET NULL'), nullable=True)
target_user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=False)
admin = db.relationship('User', foreign_keys=[admin_id],
backref=db.backref('takeover_sessions_as_admin', lazy=True))
target_user = db.relationship('User', foreign_keys=[target_user_id],
backref=db.backref('takeover_sessions_as_target', lazy=True, cascade='all, delete-orphan'))
Warum:
admin_id: SET NULL- Erhält Audit-Log auch wenn Admin gelöscht wirdtarget_user_id: CASCADE- Session wird gelöscht wenn User gelöscht wird- Verhindert Foreign-Key-Constraint-Fehler
3. Frontend - Toast-System & Bulk-Operations
Datei: frontend/package.json
{
"dependencies": {
"sonner": "^1.7.2"
}
}
Datei: frontend/src/app/layout.tsx
import { Toaster } from "sonner";
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Toaster position="top-right" richColors />
</body>
</html>
);
}
Datei: frontend/src/app/admin/page.tsx - Neue Features:
Toast-Nachrichten statt primitiver Alerts:
// Vorher
setSuccessMessage(message);
setTimeout(() => setSuccessMessage(""), 3000);
// Nachher
toast.success(message); // Modern, stackbar, dunkler
toast.error(`Fehler: ${error}`);
toast.loading("Lösche User...", { id: "delete" });
Bulk-Selection UI:
- User-Checkboxen pro Zeile (nicht Admin/CurrentUser)
- "Select All" Checkbox für gefilterte User
- Bulk-Action-Bar mit 4 Aktionen:
- Sperren / Entsperren
- Container löschen
- User löschen (mit Zwei-Schritt-Bestätigung)
Zwei-Schritt-Bestätigung bei kritischen Aktionen:
// Schritt 1: Vorschau mit Warnung
if (!confirm(`⚠️ WARNUNG: 3 User löschen?\n\nDies löscht:\n- User-Accounts\n- Alle Container\n- Alle Tokens`)) {
return;
}
// Schritt 2: Numerische Bestätigung
const confirmation = prompt(`Geben Sie die Anzahl ein (3):`);
if (confirmation !== "3") {
toast.error("Abgebrochen");
return;
}
🚀 Deployment
Vorbereitungen:
-
Backend:
# Syntax-Check python -m py_compile admin_api.py models.py -
Frontend:
cd frontend npm install # Installiert sonner npm run build npx tsc --noEmit # TypeScript-Check
Deployment-Befehle:
# Lokal/Entwicklung
cd /volume1/docker/spawner
git pull
docker-compose up -d --build
Nach Deployment:
# Logs checken
docker-compose logs -f spawner
# Admin-Dashboard testen
# 1. Einen User mit Containern erstellen
# 2. Container löschen → Toast sollte erscheinen
# 3. User löschen → Toast mit Summary
🧪 Test-Szenarien
Test 1: Multi-Container-Deletion
# Voraussetzung: User mit 3 Containern (template-01, template-02, template-next)
# 1. Admin-Dashboard öffnen
# 2. Container-Icon klicken
# 3. Toast: "3 Container gelöscht"
# 4. Verify: docker ps | grep user- → Keine Container
Test 2: DSGVO-Compliance
# 1. User mit Magic Links erstellen
# 2. Admin: User löschen → Zwei-Schritt-Bestätigung
# 3. Toast mit Summary:
# - 3 Container deleted
# - 5 Magic Tokens deleted
# - 0 Takeover Sessions deleted
# 4. Verify in DB:
docker exec spawner python3 -c "
from app import app, db
from models import MagicLinkToken
with app.app_context():
tokens = MagicLinkToken.query.filter_by(user_id=123).count()
print(f'Tokens für User 123: {tokens}')
"
# Expected: 0
Test 3: Toast-Benachrichtigungen
1. Admin-Dashboard öffnen
2. Mehrere Aktionen schnell:
- Container löschen
- User sperren
- User entsperren
3. Erwartung: Toasts stacken oben-rechts, jeder mit X zum Schließen
Test 4: Bulk-Operations
1. 3 User mit Checkboxen auswählen
2. Bulk-Action-Bar erscheint
3. "Sperren" Button → Confirm → Toast "3 User gesperrt"
4. "Select All" Checkbox → Alle (außer Admin) ausgewählt
5. "User löschen" → Zwei-Schritt-Bestätigung → Toast mit Summary
📊 API-Response-Format
Single Container-Deletion:
curl -X DELETE \
http://localhost:5000/api/admin/users/123/container \
-H "Authorization: Bearer $TOKEN"
Response (Success):
{
"message": "Alle 3 Container von user@example.com wurden gelöscht",
"deleted": 3,
"failed": []
}
Response (Partial Failure - Status 207):
{
"message": "2 Container gelöscht, 1 fehlgeschlagen",
"deleted": 2,
"failed": ["a1b2c3d4"]
}
Single User-Deletion:
curl -X DELETE \
http://localhost:5000/api/admin/users/123 \
-H "Authorization: Bearer $TOKEN"
Response:
{
"message": "User user@example.com wurde vollständig gelöscht",
"summary": {
"containers_deleted": 3,
"containers_failed": [],
"magic_tokens_deleted": 5,
"takeover_sessions_deleted": 1
}
}
⚠️ Wichtige Hinweise
Breaking Change: CASCADE DELETE
- Foreign Key Constraints wurden aktualisiert
- DB-Migration erforderlich (siehe unten)
- Alte Constraints verursachen Fehler
Datenbank-Migration:
Option 1: Mit Alembic (falls installiert)
cd backend
flask db migrate -m "Add CASCADE DELETE for DSGVO"
flask db upgrade
Option 2: Manuell für SQLite
-- Backup zuerst machen!
.backup /app/spawner.db.backup
-- MagicLinkToken
ALTER TABLE magic_link_token
DROP CONSTRAINT IF EXISTS magic_link_token_user_id_fkey;
ALTER TABLE magic_link_token
ADD CONSTRAINT magic_link_token_user_id_fkey
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE;
-- AdminTakeoverSession
ALTER TABLE admin_takeover_session
DROP CONSTRAINT IF EXISTS admin_takeover_session_admin_id_fkey;
ALTER TABLE admin_takeover_session
DROP CONSTRAINT IF EXISTS admin_takeover_session_target_user_id_fkey;
ALTER TABLE admin_takeover_session
ADD CONSTRAINT admin_takeover_session_admin_id_fkey
FOREIGN KEY (admin_id) REFERENCES user(id) ON DELETE SET NULL;
ALTER TABLE admin_takeover_session
ADD CONSTRAINT admin_takeover_session_target_user_id_fkey
FOREIGN KEY (target_user_id) REFERENCES user(id) ON DELETE CASCADE;
Backwards Compatibility:
- ✅ Alte API-Clients funktionieren (neue Felder sind optional)
- ✅ Bestehende User-Daten bleiben erhalten
- ⚠️ Nur neue Deletes sind DSGVO-konform
🔍 Troubleshooting
Problem: Toasts erscheinen nicht
# 1. Prüfe ob sonner installiert ist
cd frontend
npm list sonner
# 2. Browser-Console (F12) auf Fehler prüfen
# 3. Cache leeren: Ctrl+Shift+Del
Problem: Container-Löschung funktioniert nicht
# Logs prüfen
docker-compose logs spawner 2>&1 | tail -100
# Docker-Socket-Permissions
docker exec spawner ls -la /var/run/docker.sock
# Container manuell löschen
docker ps -a | grep user-
docker rm -f user-xyz-123
Problem: Multi-Container nicht sichtbar
# DB-Abfrage
docker exec spawner python3 -c "
from app import app, db
from models import User
with app.app_context():
user = User.query.filter_by(email='test@example.com').first()
print(f'User {user.email} hat {len(user.containers)} Container')
for c in user.containers:
print(f' - Type: {c.container_type}, ID: {c.container_id}')
"
📚 Weitere Ressourcen
📝 Änderungshistorie
| Version | Datum | Änderungen |
|---|---|---|
| 2.0 | 02.02.2026 | Multi-Container, Toast-System, Bulk-Operations, DSGVO |
| 1.0 | ≤01.02.2026 | Ursprüngliches Admin-Dashboard |
Fragen? Siehe Troubleshooting oder Logs prüfen: docker-compose logs spawner