diff --git a/web/app.py b/web/app.py index c36da2b..92c763c 100644 --- a/web/app.py +++ b/web/app.py @@ -964,6 +964,26 @@ async def action_users_disable( return RedirectResponse("/einstellungen/benutzer", status_code=303) +@app.post("/actions/users/delete") +async def action_users_delete( + request: Request, + target_id: int = Form(...), + csrf: str = Form(...), + admin=Depends(require_admin), +): + require_csrf(admin["id"], csrf) + if target_id == admin["id"]: + raise HTTPException(400, "refusing to delete self") + target = db.get_user(target_id) + if not target: + return RedirectResponse("/einstellungen/benutzer", status_code=303) + db.delete_user(target_id) + db.log_audit(admin["username"], "user.deleted", + f"target={target_id} username={target['username']}", + user_id=admin["id"], ip=client_ip(request)) + return RedirectResponse("/einstellungen/benutzer?deleted=1", status_code=303) + + # --------------------------------------------------------------------------- # Internal endpoints # --------------------------------------------------------------------------- diff --git a/web/db.py b/web/db.py index 449cbe4..b7b1459 100644 --- a/web/db.py +++ b/web/db.py @@ -304,6 +304,14 @@ def set_user_disabled(user_id: int, disabled: bool) -> None: ) +def delete_user(user_id: int) -> None: + """Remove a user and everything that cascades (profile, filters, notifications, + preferences, rejections, applications). Audit/error logs stay (user_id column + is nullable).""" + with _lock: + _conn.execute("DELETE FROM users WHERE id = ?", (user_id,)) + + # --------------------------------------------------------------------------- # User profile / filters / notifications / preferences # --------------------------------------------------------------------------- diff --git a/web/templates/_settings_profil.html b/web/templates/_settings_profil.html index b8527d0..e604feb 100644 --- a/web/templates/_settings_profil.html +++ b/web/templates/_settings_profil.html @@ -3,6 +3,14 @@