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 @@
+ {# Honeypot: Chrome/Firefox password managers ignore autocomplete="off" but + autofill the *first* email+password pair they find. These hidden fields + absorb that autofill so the visible E-Mail/Immomio-Passwort stay clean. + The server ignores unknown form fields. #} +
@@ -25,7 +33,8 @@
- +
@@ -95,7 +104,8 @@
- +
diff --git a/web/templates/_settings_users.html b/web/templates/_settings_users.html index 6e18f44..ac261f5 100644 --- a/web/templates/_settings_users.html +++ b/web/templates/_settings_users.html @@ -1,39 +1,53 @@

Benutzer verwalten

{% if request.query_params.get('ok') %}
Benutzer angelegt.
{% endif %} +{% if request.query_params.get('deleted') %}
Benutzer gelöscht.
{% endif %} {% if request.query_params.get('err') == 'exists' %}
Benutzername existiert bereits.
{% endif %} -
-
-

Neuen Benutzer anlegen

- - -
- - -
-
- - -
+
+

Neuen Benutzer anlegen

+ + +
+ + +
+
+ + +
+
- - -
+ +
+ + -
-

Alle Benutzer

-
- {% for u in users %} -
- {{ u.username }} +
+
+

Alle Benutzer

+ {{ users|length }} +
+
+ {% for u in users %} +
+
+ {{ u.username }} + {% if u.id == user.id %}du{% endif %} +
+
{% if u.is_admin %}Administrator{% endif %} {% if u.disabled %}deaktiviert {% else %}aktiv{% endif %} - {% if u.id != user.id %} +
+ {% if u.id != user.id %} +
@@ -42,9 +56,15 @@ {% if u.disabled %}aktivieren{% else %}deaktivieren{% endif %}
- {% endif %} +
+ + + +
- {% endfor %} + {% endif %}
+ {% endfor %}
-
+ diff --git a/web/templates/bewerbung_detail.html b/web/templates/bewerbung_detail.html index 9977409..eec37e9 100644 --- a/web/templates/bewerbung_detail.html +++ b/web/templates/bewerbung_detail.html @@ -11,7 +11,7 @@ {% if application.triggered_by == 'auto' %}automatisch{% else %}manuell{% endif %} {% if application.provider %}{{ application.provider }}{% endif %} {% if application.submit_forms_used %}echt gesendet - {% else %}Trockenmodus{% endif %} + {% else %}nicht abgeschickt{% endif %} {% if application.success == 0 %} {% if a.triggered_by == 'auto' %}automatisch{% else %}manuell{% endif %} {% if a.provider %}{{ a.provider }}{% endif %} {% if a.submit_forms_used %}echt gesendet - {% else %}Trockenmodus{% endif %} + {% else %}nicht abgeschickt{% endif %} {{ a.started_at|de_dt }}