lib/lamina/dashboard.ex

if Code.ensure_loaded?(Phoenix.LiveDashboard.PageBuilder) do
  defmodule Lamina.Dashboard do
    use Phoenix.LiveDashboard.PageBuilder
    alias Lamina.Dashboard.Remote
    alias Lamina.Registry.ServerRegistry
    alias Phoenix.LiveView.Socket

    @moduledoc """
    A LiveDashboard.PageBuilder which shows information about currently running
    Lamina configuration servers.

    ## Usage:

    Add the following to your LiveDashboard entry in your Phoenix router:

    ```elixir
    live_dashboard "/dashboard",
      additional_pages: [lamina: Lamina.Dashboard]
    ```
    """

    @doc false
    @impl true
    @spec mount(map, any, Socket.t()) :: {:ok, Socket.t()}
    def mount(params, _session, socket) do
      node = socket.assigns.page.node
      servers = Remote.list_servers(node)
      socket = assign(socket, servers: servers)
      nav = params["nav"]
      server = find_server(nav)
      first_server = hd(servers)

      cond do
        is_nil(server) && first_server ->
          to = live_dashboard_path(socket, socket.assigns.page, nav: server_id(first_server))
          {:ok, push_redirect(socket, to: to)}

        server ->
          config_keys = Remote.list_config_keys(node, server)
          providers = Remote.list_providers(node, server)

          {:ok, assign(socket, providers: providers, config_keys: config_keys, server: server)}

        true ->
          {:ok, socket}
      end
    end

    @doc false
    @impl true
    def menu_link(_, _), do: {:ok, "Lamina"}

    @doc false
    @impl true
    def render_page(%{servers: []} = assigns) do
      fn ->
        ~H"""
        No Lamina servers running on #{@page.node}.
        """
      end
    end

    def render_page(assigns) do
      items =
        for server <- assigns.servers do
          {server_id(server),
           name: inspect(server), render: render_server(assigns), method: :redirect}
        end

      nav_bar(items: items)
    end

    defp render_server(assigns) do
      fn ->
        table(
          columns: table_columns(),
          id: assigns.server,
          row_attrs: &row_attrs/1,
          row_fetcher: &fetch_table(assigns.server, &1, &2),
          title: "configuration settings"
        )
      end
    end

    defp server_id(server) when is_atom(server) do
      server
      |> Atom.to_string()
      |> Base.encode64(padding: false)
    end

    defp find_server(server_id) do
      ServerRegistry.all_servers()
      |> Enum.find(&(server_id(&1) == server_id))
    end

    defp table_columns,
      do: [
        %{
          field: :name,
          header: "Config key",
          header_attrs: [class: "p1-4"],
          cell_attrs: [class: "tabular-column-name p1-4"],
          sortable: :asc
        },
        %{
          field: :value,
          format: &format_value/1
        }
      ]

    defp format_value(value), do: inspect(value)

    defp row_attrs(_table), do: []

    defp fetch_table(server, params, node) do
      Remote.get_configs(node, server, params)
    end
  end
end