From a324d5a68d443d81d68dcb6eab917f6b72581213 Mon Sep 17 00:00:00 2001 From: domverse Date: Wed, 17 Jun 2026 00:02:59 +0200 Subject: [PATCH] feat: bulk unban via checkboxes Adds per-row checkboxes, a select-all toggle, a live "N selected" counter, and an /unban-bulk endpoint that DELETEs each chosen decision id. Single-row Unban buttons still work via hx-vals so they don't accidentally submit the bulk form. Co-Authored-By: Claude Opus 4.7 --- app/app.py | 19 ++++++++++ app/templates/_decisions.html | 69 +++++++++++++++++++++++------------ 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/app/app.py b/app/app.py index acb7c92..b0c38d5 100644 --- a/app/app.py +++ b/app/app.py @@ -122,6 +122,25 @@ def unban(): return list_decisions() +@app.post("/unban-bulk") +def unban_bulk(): + ids = [i for i in request.form.getlist("id") if i.isdigit()] + if not ids: + return list_decisions() + deleted = 0 + errors = [] + for did in ids: + r = _machine("DELETE", f"/v1/decisions/{did}") + if r.status_code in (200, 204): + deleted += 1 + else: + errors.append(f"{did}:{r.status_code}") + log.info("unban-bulk by=%s count=%d errors=%s", caller_ip(), deleted, errors) + if errors: + return f"deleted {deleted}/{len(ids)}; errors: {', '.join(errors[:5])}", 502 + return list_decisions() + + @app.post("/unban-me") def unban_me(): ip = caller_ip() diff --git a/app/templates/_decisions.html b/app/templates/_decisions.html index 6c03385..21d6a22 100644 --- a/app/templates/_decisions.html +++ b/app/templates/_decisions.html @@ -2,30 +2,51 @@

{{ error }}

{% endif %} {% if decisions %} - - - - - - {% for d in decisions %} - - - - - - - - - - - {% endfor %} - -
IDIP / valueScopeTypeReasonUntilOrigin
{{ d.id }}{{ d.value }}{{ d.scope }}{{ d.type }}{{ d.scenario }}{{ d.until }}{{ d.origin }} -
- - -
-
+
+
+ + 0 selected of {{ decisions|length }} +
+ + + + + + + + + {% for d in decisions %} + + + + + + + + + + + + {% endfor %} + +
IDIP / valueScopeTypeReasonUntilOrigin
{{ d.id }}{{ d.value }}{{ d.scope }}{{ d.type }}{{ d.scenario }}{{ d.until }}{{ d.origin }} + +
+
+ {% else %} {% if not error %}

No active decisions.

{% endif %} {% endif %}