From 7160bf95d66cf1ae71c5d3edb54b5e5b3cf4827d Mon Sep 17 00:00:00 2001 From: Graham McIntie Date: Mon, 16 Feb 2026 16:50:17 -0600 Subject: [PATCH] Add admin endpoint to flush Oban jobs by state --- .../controllers/admin_controller.ex | 23 +++++++++++++++++++ lib/towerops_web/router.ex | 1 + 2 files changed, 24 insertions(+) diff --git a/lib/towerops_web/controllers/admin_controller.ex b/lib/towerops_web/controllers/admin_controller.ex index 31a8d36d..bb886914 100644 --- a/lib/towerops_web/controllers/admin_controller.ex +++ b/lib/towerops_web/controllers/admin_controller.ex @@ -19,4 +19,27 @@ defmodule ToweropsWeb.AdminController do def stop_impersonate(conn, _params) do UserAuth.stop_impersonation(conn) end + + @doc """ + Flush Oban jobs by state. Cancels all jobs in the given state(s). + POST /admin/oban/flush?states=scheduled,retryable + """ + def flush_oban(conn, %{"states" => states_param}) do + import Ecto.Query + + valid_states = ~w(scheduled retryable available) + requested = String.split(states_param, ",") |> Enum.map(&String.trim/1) + states = Enum.filter(requested, &(&1 in valid_states)) + + if states == [] do + conn |> put_status(400) |> json(%{error: "No valid states. Use: scheduled, retryable, available"}) + else + {count, _} = + Oban.Job + |> where([j], j.state in ^states) + |> Towerops.Repo.delete_all() + + conn |> json(%{ok: true, deleted: count, states: states}) + end + end end diff --git a/lib/towerops_web/router.ex b/lib/towerops_web/router.ex index 5f966fa0..3de48bad 100644 --- a/lib/towerops_web/router.ex +++ b/lib/towerops_web/router.ex @@ -277,6 +277,7 @@ defmodule ToweropsWeb.Router do post "/admin/impersonate/:user_id", AdminController, :start_impersonate delete "/admin/impersonate", AdminController, :stop_impersonate + post "/admin/oban/flush", AdminController, :flush_oban end live_session :require_superuser,