Add permanent user deletion endpoint with cascade cleanup and trash icon button in admin panel

Implemented DELETE `/admin/users/{user_id}/permanent` endpoint with safeguards preventing self-deletion and blocking deletion of game hosts. Added cascade deletion of user's game memberships, chips, sheet states, and invite tokens, with winner references nullified. Added trash icon button (🗑) to admin user rows with bilingual confirmation dialog and error handling for game ownership conflicts. Adjusted
This commit is contained in:
2026-08-02 10:44:40 +02:00
parent 45fc8e4c9e
commit 565d538c35
4 changed files with 44 additions and 11 deletions
+23 -1
View File
@@ -6,7 +6,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from ..db import get_db
from ..mailer import send_html_email, send_user_invite
from ..models import AppSettings, InviteToken, User, Role
from ..models import AppSettings, Game, GameChip, GameMember, InviteToken, SheetState, User, Role
from ..security import hash_password, get_session_user_id
router = APIRouter(prefix="/admin", tags=["admin"])
@@ -84,6 +84,28 @@ def create_user(req: Request, data: dict, db: Session = Depends(get_db)):
db.commit()
return {"ok": True, "id": u.id, "invite_sent": invite_sent}
@router.delete("/users/{user_id}/permanent")
def permanently_delete_user(req: Request, user_id: str, db: Session = Depends(get_db)):
admin = require_admin(req, db)
if admin.id == user_id:
raise HTTPException(400, "cannot delete yourself")
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(404, "not found")
if db.query(Game).filter(Game.host_user_id == user_id).first():
raise HTTPException(409, "cannot delete a user who owns games")
db.query(Game).filter(Game.winner_user_id == user_id).update({Game.winner_user_id: None}, synchronize_session=False)
db.query(GameMember).filter(GameMember.user_id == user_id).delete(synchronize_session=False)
db.query(GameChip).filter(GameChip.user_id == user_id).delete(synchronize_session=False)
db.query(SheetState).filter(SheetState.owner_user_id == user_id).delete(synchronize_session=False)
db.query(InviteToken).filter(InviteToken.user_id == user_id).delete(synchronize_session=False)
db.delete(user)
db.commit()
return {"ok": True, "deleted": True}
@router.delete("/users/{user_id}")
def delete_user(req: Request, user_id: str, db: Session = Depends(get_db)):
admin = require_admin(req, db)