Vendor oban_web 2.12.1 and oban_met 1.1.0 (taken from the towerops-web2 vendor tree), bump oban to ~> 2.21, and mount the Oban dashboard at /admin/oban behind the existing require_admin on_mount hook. Adds an admin-only "Oban" nav link and extends the Dockerfile to copy vendor/ before deps.get so production builds pick up the path dependencies.
252 lines
6.7 KiB
Elixir
252 lines
6.7 KiB
Elixir
defmodule Oban.Web.DashboardLive do
|
|
use Oban.Web, :live_view
|
|
|
|
alias Oban.Web.{CronsPage, JobsPage, QueuesPage, WorkflowsPage}
|
|
|
|
@impl Phoenix.LiveView
|
|
def mount(params, session, socket) do
|
|
%{"prefix" => prefix, "resolver" => resolver} = session
|
|
%{"live_path" => live_path, "live_transport" => live_transport} = session
|
|
%{"user" => user, "access" => access, "csp_nonces" => csp_nonces} = session
|
|
%{"refresh" => refresh, "logo_path" => logo_path} = session
|
|
|
|
refresh = restore_state(socket, "refresh", refresh)
|
|
theme = restore_state(socket, "theme", "system")
|
|
sidebar_width = restore_state(socket, "sidebar_width", 320)
|
|
oban = current_oban_instance(session, socket)
|
|
|
|
conf = await_init([oban])
|
|
_met = await_init([oban, Oban.Met])
|
|
page = resolve_page(params)
|
|
|
|
Process.put(:routing, {socket, prefix})
|
|
|
|
socket =
|
|
socket
|
|
|> assign(conf: conf, params: params, page: page, init_state: init_state(socket))
|
|
|> assign(live_path: live_path, live_transport: live_transport, logo_path: logo_path)
|
|
|> assign(access: access, csp_nonces: csp_nonces, resolver: resolver, user: user)
|
|
|> assign(original_refresh: nil, refresh: refresh, timer: nil, theme: theme)
|
|
|> assign(sidebar_width: sidebar_width)
|
|
|> init_schedule_refresh()
|
|
|> page.comp.handle_mount()
|
|
|
|
{:ok, socket}
|
|
end
|
|
|
|
defp current_oban_instance(session, socket) do
|
|
stashed = restore_state(socket, "instance")
|
|
default = List.first(oban_instances())
|
|
|
|
case stashed || session["oban"] || default || Oban do
|
|
name when is_binary(name) -> name |> String.split(".") |> Module.safe_concat()
|
|
name -> name
|
|
end
|
|
end
|
|
|
|
defp init_state(socket) do
|
|
case get_connect_params(socket) do
|
|
%{"init_state" => state} -> state
|
|
_ -> %{}
|
|
end
|
|
end
|
|
|
|
defp restore_state(socket, key, default \\ nil) do
|
|
socket
|
|
|> init_state()
|
|
|> Map.get("oban:" <> key, default)
|
|
end
|
|
|
|
@impl Phoenix.LiveView
|
|
def render(assigns) do
|
|
assigns =
|
|
assigns
|
|
|> Map.put(:id, "page")
|
|
|> Map.drop(~w(flash live_path live_transport refresh socket timer)a)
|
|
|
|
~H"""
|
|
<.live_component id="page" module={@page.comp} {assigns} />
|
|
"""
|
|
end
|
|
|
|
@impl Phoenix.LiveView
|
|
def terminate(_reason, %{assigns: %{timer: timer}}) do
|
|
if is_reference(timer), do: Process.cancel_timer(timer)
|
|
|
|
:ok
|
|
end
|
|
|
|
def terminate(_reason, _socket), do: :ok
|
|
|
|
@impl Phoenix.LiveView
|
|
def handle_params(params, uri, socket) do
|
|
page = resolve_page(params)
|
|
|
|
if page == socket.assigns.page do
|
|
page.comp.handle_params(params, uri, socket)
|
|
else
|
|
socket =
|
|
socket
|
|
|> assign(params: params, page: page)
|
|
|> page.comp.handle_mount()
|
|
|
|
page.comp.handle_params(params, uri, socket)
|
|
end
|
|
end
|
|
|
|
@impl Phoenix.LiveView
|
|
def handle_info(:clear_flash, socket) do
|
|
{:noreply, clear_flash(socket)}
|
|
end
|
|
|
|
def handle_info(:pause_refresh, socket) do
|
|
socket =
|
|
if socket.assigns.refresh > 0 do
|
|
cancel_timer(socket)
|
|
|
|
assign(socket, refresh: -1, original_refresh: socket.assigns.refresh, timer: nil)
|
|
else
|
|
socket
|
|
end
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
def handle_info(:resume_refresh, socket) do
|
|
if original = socket.assigns.original_refresh do
|
|
handle_info({:update_refresh, original}, socket)
|
|
else
|
|
{:noreply, socket}
|
|
end
|
|
end
|
|
|
|
def handle_info({:select_instance, name}, socket) do
|
|
name = name |> String.split(".") |> Module.safe_concat()
|
|
conf = Oban.config(name)
|
|
name = inspect(name)
|
|
page = socket.assigns.page
|
|
|
|
socket =
|
|
socket
|
|
|> assign(oban: name, conf: conf)
|
|
|> push_event("select-instance", %{name: name})
|
|
|> put_flash_with_clear(:info, "Switched instance to #{name}")
|
|
|> page.comp.handle_refresh()
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
def handle_info(:toggle_refresh, socket) do
|
|
%{original_refresh: original, refresh: refresh} = socket.assigns
|
|
|
|
cond do
|
|
refresh > 0 ->
|
|
socket =
|
|
socket
|
|
|> assign(refresh: -1, original_refresh: refresh)
|
|
|> push_event("update-refresh", %{refresh: -1})
|
|
|
|
{:noreply, socket}
|
|
|
|
is_integer(original) ->
|
|
handle_info({:update_refresh, original}, socket)
|
|
|
|
true ->
|
|
handle_info({:update_refresh, 1}, socket)
|
|
end
|
|
end
|
|
|
|
def handle_info({:update_refresh, refresh}, socket) do
|
|
socket =
|
|
socket
|
|
|> assign(refresh: refresh, original_refresh: nil)
|
|
|> schedule_refresh()
|
|
|
|
{:noreply, push_event(socket, "update-refresh", %{refresh: refresh})}
|
|
end
|
|
|
|
def handle_info({:update_theme, theme}, socket) do
|
|
{:noreply,
|
|
socket
|
|
|> assign(theme: theme)
|
|
|> push_event("update-theme", %{theme: theme})}
|
|
end
|
|
|
|
def handle_info(:refresh, socket) do
|
|
socket =
|
|
socket
|
|
|> socket.assigns.page.comp.handle_refresh()
|
|
|> schedule_refresh()
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
def handle_info(message, socket) do
|
|
socket.assigns.page.comp.handle_info(message, socket)
|
|
end
|
|
|
|
@impl Phoenix.LiveView
|
|
def handle_event("sidebar_resize", %{"width" => width}, socket) do
|
|
{:noreply, assign(socket, sidebar_width: width)}
|
|
end
|
|
|
|
def handle_event(event, params, socket) do
|
|
socket.assigns.page.comp.handle_event(event, params, socket)
|
|
end
|
|
|
|
## Mount Helpers
|
|
|
|
defp await_init(args, opts \\ [])
|
|
|
|
defp await_init([oban_name | _] = args, opts) do
|
|
retries = Keyword.get(opts, :retries, 150)
|
|
timeout = Keyword.get(opts, :timeout, 100)
|
|
|
|
case apply(Oban.Registry, :whereis, args) do
|
|
nil when retries > 0 ->
|
|
Process.sleep(timeout)
|
|
|
|
await_init(args, Keyword.put(opts, :retries, retries - 1))
|
|
|
|
nil ->
|
|
raise RuntimeError, "no config registered for #{inspect(args)} instance"
|
|
|
|
pid when is_pid(pid) ->
|
|
Oban.config(oban_name)
|
|
end
|
|
end
|
|
|
|
## Render Helpers
|
|
|
|
defp resolve_page(%{"page" => "jobs"}), do: %{name: :jobs, comp: JobsPage}
|
|
defp resolve_page(%{"page" => "queues"}), do: %{name: :queues, comp: QueuesPage}
|
|
defp resolve_page(%{"page" => "crons"}), do: %{name: :crons, comp: CronsPage}
|
|
defp resolve_page(%{"page" => "workflows"}), do: %{name: :workflows, comp: WorkflowsPage}
|
|
defp resolve_page(_params), do: %{name: :jobs, comp: JobsPage}
|
|
|
|
## Refresh Helpers
|
|
|
|
defp init_schedule_refresh(socket) do
|
|
if connected?(socket) do
|
|
schedule_refresh(socket)
|
|
else
|
|
assign(socket, timer: nil)
|
|
end
|
|
end
|
|
|
|
defp schedule_refresh(socket) do
|
|
cancel_timer(socket)
|
|
|
|
if socket.assigns.refresh > 0 do
|
|
interval = :timer.seconds(socket.assigns.refresh) - 50
|
|
|
|
assign(socket, timer: Process.send_after(self(), :refresh, interval))
|
|
else
|
|
assign(socket, timer: nil)
|
|
end
|
|
end
|
|
|
|
defp cancel_timer(socket) do
|
|
if is_reference(socket.assigns.timer), do: Process.cancel_timer(socket.assigns.timer)
|
|
end
|
|
end
|