forked from hiway/freedive
301 lines
8.5 KiB
Elixir
301 lines
8.5 KiB
Elixir
defmodule FreediveWeb.ServiceLive do
|
|
use FreediveWeb.LiliformLive
|
|
alias Freedive.Api.Service
|
|
require Logger
|
|
|
|
def render(assigns) do
|
|
~H"""
|
|
<.page name="Services" filters={@filters} details={@details}>
|
|
<FreediveWeb.ServiceLive.Components.items_block
|
|
items={@items}
|
|
selected_item={@selected_item}
|
|
details={@details}
|
|
/>
|
|
</.page>
|
|
"""
|
|
end
|
|
|
|
def on_mount_connected(socket) do
|
|
Logger.warning("Mounting ServiceLive")
|
|
Service.subscribe()
|
|
{:ok, socket}
|
|
end
|
|
|
|
def items() do
|
|
Service.list()
|
|
end
|
|
|
|
def filters() do
|
|
[
|
|
%{
|
|
title: "Running",
|
|
key: :running,
|
|
icon: "circle-play",
|
|
active: true
|
|
},
|
|
%{
|
|
title: "Enabled",
|
|
key: :enabled,
|
|
icon: "circle-check",
|
|
active: false
|
|
}
|
|
]
|
|
end
|
|
|
|
def search(items, query) do
|
|
items
|
|
|> Enum.filter(fn {name, _item} ->
|
|
String.contains?(String.downcase(name), String.downcase(query))
|
|
end)
|
|
|> Enum.into(%{}, fn {name, item} -> {name, item} end)
|
|
end
|
|
|
|
def handle_event("action:" <> action, %{"name" => service_name}, socket)
|
|
when action in ["start", "stop", "restart"] do
|
|
case action do
|
|
"start" -> Service.start(service_name)
|
|
"stop" -> Service.stop(service_name)
|
|
"restart" -> Service.restart(service_name)
|
|
end
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
def handle_event("command:" <> command, %{"name" => service_name}, socket) do
|
|
Service.command(service_name, command)
|
|
{:noreply, socket}
|
|
end
|
|
|
|
def handle_info({event, payload}, socket) do
|
|
case event do
|
|
:command ->
|
|
# Logger.info("ServiceLive: Command: #{inspect(payload)}")
|
|
|
|
socket =
|
|
if socket.assigns.selected_item && payload.name == socket.assigns.selected_item.name do
|
|
socket |> assign(:selected_item, %{socket.assigns.selected_item | busy: true})
|
|
else
|
|
socket
|
|
end
|
|
|
|
{:noreply, socket}
|
|
|
|
:refreshed ->
|
|
# Logger.info("ServiceLive: Refreshed: #{inspect(payload)}")
|
|
|
|
socket =
|
|
if socket.assigns.selected_item && payload.name == socket.assigns.selected_item.name do
|
|
socket
|
|
|> assign(:selected_item, %{
|
|
socket.assigns.selected_item
|
|
| running: payload.running,
|
|
enabled: payload.enabled,
|
|
busy: false
|
|
})
|
|
|> assign(:items, socket.assigns.items |> Map.put(payload.name, payload))
|
|
else
|
|
socket
|
|
end
|
|
|
|
{:noreply, socket}
|
|
|
|
:stdlog ->
|
|
# Logger.info("ServiceLive: Stdlog: #{inspect(payload)}")
|
|
|
|
socket =
|
|
if socket.assigns.selected_item && payload.name == socket.assigns.selected_item.name do
|
|
socket
|
|
|> assign(:selected_item, %{
|
|
socket.assigns.selected_item
|
|
| log: socket.assigns.selected_item.log ++ payload.log
|
|
})
|
|
else
|
|
socket
|
|
end
|
|
|
|
{:noreply, socket}
|
|
|
|
_ ->
|
|
Logger.warning("ServiceLive: Unknown event: #{event}, payload: #{inspect(payload)}")
|
|
{:noreply, socket}
|
|
end
|
|
end
|
|
end
|
|
|
|
defmodule FreediveWeb.ServiceLive.Components do
|
|
use Liliform.Component
|
|
import Liliform.Icon
|
|
import Liliform.Panel
|
|
import Liliform.Title
|
|
|
|
@doc """
|
|
Returns items as panel-blocks.
|
|
"""
|
|
attr :items, :list, default: [], doc: "items"
|
|
attr :selected_item, :string, default: nil, doc: "selected item"
|
|
attr :details, :string, default: nil, doc: "details"
|
|
|
|
def items_block(%{details: true} = assigns) do
|
|
~H"""
|
|
<.back_link selected_item={@selected_item} />
|
|
<.service_header selected_item={@selected_item} />
|
|
<.std_log selected_item={@selected_item} />
|
|
<.basic_commands selected_item={@selected_item} />
|
|
<.extra_commands selected_item={@selected_item} />
|
|
"""
|
|
end
|
|
|
|
def items_block(assigns) do
|
|
~H"""
|
|
<%= for {_name, item} <- @items do %>
|
|
<.service_preview item={item} selected_item={@selected_item} />
|
|
<% end %>
|
|
"""
|
|
end
|
|
|
|
def back_link(assigns) do
|
|
~H"""
|
|
<.link
|
|
class="panel-block pt-1 has-background-light"
|
|
phx-click="tap"
|
|
phx-value-name={@selected_item.name}
|
|
>
|
|
<span class="panel-icon">
|
|
<.icon for="arrow-left" aria-hidden="true" has-text-dark />
|
|
</span>
|
|
<div class="mt-2 ml-2">Back</div>
|
|
</.link>
|
|
"""
|
|
end
|
|
|
|
def service_header(assigns) do
|
|
~H"""
|
|
<.panel_block>
|
|
<.icon_raw for={@selected_item.icon} color="auto" size="3rem" aria-hidden="true" />
|
|
<div class="column">
|
|
<.title is-4>
|
|
<%= @selected_item.name %>
|
|
<%= if @selected_item.running do %>
|
|
<.icon for="circle-play" color="lightgreen" aria-hidden="true" />
|
|
<% else %>
|
|
<.icon for="circle-stop" color="gray" aria-hidden="true" />
|
|
<% end %>
|
|
<%= if @selected_item.enabled do %>
|
|
<.icon for="circle-check" color="lightgreen" aria-hidden="true" />
|
|
<% else %>
|
|
<.icon for="circle-x" color="gray" aria-hidden="true" />
|
|
<% end %>
|
|
</.title>
|
|
<.subtitle>
|
|
<%= @selected_item.description %>
|
|
</.subtitle>
|
|
</div>
|
|
</.panel_block>
|
|
"""
|
|
end
|
|
|
|
def service_preview(assigns) do
|
|
~H"""
|
|
<.panel_block
|
|
class={"#{if @selected_item != nil and @selected_item.name == @item.name, do: "pt-2 has-background-info-light", else: "pt-2"}"}
|
|
phx-click="tap"
|
|
phx-value-name={@item.name}
|
|
>
|
|
|
|
<.icon_raw for={@item.icon} color="auto" size="1.8rem" aria-hidden="true" />
|
|
|
|
<span class="is-size-5 px-4">
|
|
<%= @item.name %>
|
|
</span>
|
|
|
|
<%= if @item.running do %>
|
|
<.icon for="circle-play" size="1rem" color="lightgreen" aria-hidden="true" />
|
|
<% else %>
|
|
<.icon for="circle-stop" size="1rem" color="gray" aria-hidden="true" />
|
|
<% end %>
|
|
<%= if @item.enabled do %>
|
|
<.icon for="circle-check" size="1rem" color="lightgreen" aria-hidden="true" />
|
|
<% else %>
|
|
<.icon for="circle-x" size="1rem" color="gray" aria-hidden="true" />
|
|
<% end %>
|
|
</.panel_block>
|
|
"""
|
|
end
|
|
|
|
def basic_commands(assigns) do
|
|
~H"""
|
|
<.panel_block_div>
|
|
<%= if @selected_item.running do %>
|
|
<.icon_raw for="power" color="lightgreen" size="2rem" class="ml-2 mr-5" aria-hidden="true" />
|
|
<% else %>
|
|
<.icon_raw for="power" color="gray" size="2rem" class="ml-2 mr-5" aria-hidden="true" />
|
|
<% end %>
|
|
<div class="columns is-fullwidth mr-4">
|
|
<%= if @selected_item.running do %>
|
|
<button
|
|
class="button is-warning column is-2 m-2 is-fullwidth"
|
|
phx-click="action:restart"
|
|
phx-value-name={@selected_item.name}
|
|
{if @selected_item.busy, do: [disabled: true], else: []}
|
|
>
|
|
Restart
|
|
</button>
|
|
<button
|
|
class="button is-danger column is-2 m-2 is-fullwidth"
|
|
phx-click="action:stop"
|
|
phx-value-name={@selected_item.name}
|
|
{if @selected_item.busy, do: [disabled: true], else: []}
|
|
>
|
|
Stop
|
|
</button>
|
|
<% else %>
|
|
<button
|
|
class="button is-success column is-2 m-2 is-fullwidth"
|
|
phx-click="action:start"
|
|
phx-value-name={@selected_item.name}
|
|
{if @selected_item.busy, do: [disabled: true], else: []}
|
|
>
|
|
Start
|
|
</button>
|
|
<% end %>
|
|
</div>
|
|
</.panel_block_div>
|
|
"""
|
|
end
|
|
|
|
def extra_commands(assigns) do
|
|
~H"""
|
|
<%= if @selected_item.commands != [] and @selected_item.commands != nil do %>
|
|
<.panel_block_div>
|
|
<.icon_raw
|
|
for="inspection-panel"
|
|
color="auto"
|
|
size="2rem"
|
|
class="ml-1 mr-5"
|
|
aria-hidden="true"
|
|
/>
|
|
<div class="columns is-fullwidth mr-4">
|
|
<%= for command <- @selected_item.commands do %>
|
|
<button
|
|
class="button is-info column is-2 m-2 is-fullwidth"
|
|
phx-click={"command:#{command}"}
|
|
phx-value-name={@selected_item.name}
|
|
{if @selected_item.busy, do: [disabled: true], else: []}
|
|
>
|
|
<%= command %>
|
|
</button>
|
|
<% end %>
|
|
</div>
|
|
</.panel_block_div>
|
|
<% end %>
|
|
"""
|
|
end
|
|
|
|
def std_log(assigns) do
|
|
~H"""
|
|
<%= if Map.has_key?(@selected_item, :log) and @selected_item.log != [] do %>
|
|
<pre class="panel-block is-fullwidth"><code><%= Enum.join(@selected_item.log, "\n") %></code></pre>
|
|
<% end %>
|
|
"""
|
|
end
|
|
end
|