forked from hiway/freedive
Add file monitor to reload services, improve icons.
This commit is contained in:
parent
65aa88e926
commit
2c9fab28a4
6 changed files with 141 additions and 68 deletions
|
@ -134,11 +134,11 @@ defmodule Freedive.Api.Service.Cli do
|
|||
defp service(name, command, args) do
|
||||
case execute(@service_bin, [name, command] ++ args, doas: true) do
|
||||
{:ok, stdout} ->
|
||||
Logger.debug("service #{name} #{command} #{args}: #{inspect(stdout)}")
|
||||
# Logger.debug("service #{name} #{command} #{args}: #{inspect(stdout)}")
|
||||
{:ok, stdout}
|
||||
|
||||
{:error, {stderr, code}} ->
|
||||
Logger.debug("service #{name} #{command} #{args}: (#{code}) #{String.trim(stderr)}")
|
||||
# Logger.debug("service #{name} #{command} #{args}: (#{code}) #{String.trim(stderr)}")
|
||||
{:error, {stderr, code}}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,6 +10,7 @@ defmodule Freedive.Api.Service do
|
|||
@events [
|
||||
:command,
|
||||
:refreshed,
|
||||
:refreshed_services,
|
||||
:stdlog
|
||||
]
|
||||
|
||||
|
@ -53,12 +54,25 @@ defmodule Freedive.Api.Service do
|
|||
def init(opts) do
|
||||
state = %{opts: opts, services: []}
|
||||
Logger.info("Starting Service.Server with opts: #{inspect(opts)}")
|
||||
|
||||
{:ok, state, {:continue, opts}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_continue(_opts, state) do
|
||||
state = %{state | services: list_services!()}
|
||||
{:ok, watcher_pid} =
|
||||
FileSystem.start_link(
|
||||
dirs: ["/etc/", "/usr/local/etc/rc.d"],
|
||||
name: :rc_dirs_changed
|
||||
)
|
||||
|
||||
FileSystem.subscribe(:rc_dirs_changed)
|
||||
|
||||
state =
|
||||
state
|
||||
|> Map.put(:services, list_services!())
|
||||
|> Map.put(:watcher_pid, watcher_pid)
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
|
@ -88,16 +102,40 @@ defmodule Freedive.Api.Service do
|
|||
{:noreply, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(
|
||||
{:file_event, watcher_pid, {path, _events}},
|
||||
%{watcher_pid: watcher_pid} = state
|
||||
) do
|
||||
name = Path.basename(path)
|
||||
Logger.debug("Service.Server: handle_info file_event name: #{name}")
|
||||
|
||||
state =
|
||||
if name in state[:services] do
|
||||
curr_service = state[:services][name]
|
||||
refreshed_service = refresh_service!(curr_service)
|
||||
IO.inspect(refreshed_service, label: "refreshed_service")
|
||||
broadcast(:refreshed, refreshed_service)
|
||||
%{state | services: Map.put(state[:services], name, refreshed_service)}
|
||||
else
|
||||
if name == "rc.conf" do
|
||||
Logger.info("Service.Server: rc.conf changed, refreshing enabled services.")
|
||||
services = list_services!()
|
||||
broadcast(:refreshed_services, services)
|
||||
|
||||
state
|
||||
|> Map.put(:services, services)
|
||||
else
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def broadcast(event, payload) do
|
||||
if event in @events do
|
||||
Phoenix.PubSub.broadcast(
|
||||
Freedive.PubSub,
|
||||
@topic,
|
||||
{event,
|
||||
Map.merge(payload, %{
|
||||
hostname: "unknown"
|
||||
})}
|
||||
)
|
||||
Phoenix.PubSub.broadcast(Freedive.PubSub, @topic, {event, payload})
|
||||
else
|
||||
Logger.error("Service.Server broadcast: unknown event: #{event}")
|
||||
end
|
||||
|
|
|
@ -99,6 +99,26 @@ defmodule FreediveWeb.ServiceLive do
|
|||
|
||||
{:noreply, socket}
|
||||
|
||||
:refreshed_services ->
|
||||
Logger.info("ServiceLive: Refreshed services.")
|
||||
|
||||
searched_and_filtered_items =
|
||||
payload
|
||||
|> search(socket.assigns.query)
|
||||
|> FreediveWeb.LiliformLive.filter(
|
||||
FreediveWeb.LiliformLive.get_active_filter(socket.assigns.filters)
|
||||
)
|
||||
|
||||
socket =
|
||||
if socket.assigns.selected_item do
|
||||
selected_item = payload[socket.assigns.selected_item.name]
|
||||
assign(socket, :selected_item, selected_item)
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
{:noreply, assign(socket, %{items_all: payload, items: searched_and_filtered_items})}
|
||||
|
||||
:stdlog ->
|
||||
# Logger.info("ServiceLive: Stdlog: #{inspect(payload)}")
|
||||
|
||||
|
@ -171,20 +191,15 @@ defmodule FreediveWeb.ServiceLive.Components do
|
|||
def service_header(assigns) do
|
||||
~H"""
|
||||
<.panel_block>
|
||||
<.icon_raw for={@selected_item.icon} color="auto" size="3rem" aria-hidden="true" />
|
||||
<.icon_raw for={@selected_item.icon} color="auto" size="2.5rem" 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 %>
|
||||
|
||||
<span class="ml-1">
|
||||
<.icon_running item={@selected_item} size="1.2rem" />
|
||||
<.icon_enabled item={@selected_item} size="1.2rem" />
|
||||
</span>
|
||||
</.title>
|
||||
<.subtitle>
|
||||
<%= @selected_item.description %>
|
||||
|
@ -201,23 +216,14 @@ defmodule FreediveWeb.ServiceLive.Components do
|
|||
phx-click="tap"
|
||||
phx-value-name={@item.name}
|
||||
>
|
||||
|
||||
<.icon_raw for={@item.icon} color="auto" size="1.5rem" aria-hidden="true" />
|
||||
|
||||
<span class="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 %>
|
||||
<.icon_running item={@item} size="1rem" />
|
||||
<.icon_enabled item={@item} size="1rem" />
|
||||
</.panel_block>
|
||||
"""
|
||||
end
|
||||
|
@ -298,4 +304,24 @@ defmodule FreediveWeb.ServiceLive.Components do
|
|||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
||||
def icon_running(assigns) do
|
||||
~H"""
|
||||
<%= if @item.running do %>
|
||||
<.icon for="running" title="Running" size={@size} color="auto" aria-hidden="true" />
|
||||
<% else %>
|
||||
<.icon for="stopped" title="Stopped" size={@size} color="auto" aria-hidden="true" />
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
||||
def icon_enabled(assigns) do
|
||||
~H"""
|
||||
<%= if @item.enabled do %>
|
||||
<.icon for="enabled" title="Enabled" size={@size} color="auto" aria-hidden="true" />
|
||||
<% else %>
|
||||
<.icon for="disabled" title="Disabled" size={@size} color="auto" aria-hidden="true" />
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,6 +50,7 @@ defmodule Liliform.Icon do
|
|||
<.icon for="info" size="2rem" />
|
||||
"""
|
||||
attr :for, :string, required: true, doc: "icon name"
|
||||
attr :title, :string, default: nil, doc: "icon title"
|
||||
attr :size, :string, default: nil, doc: "size of icon"
|
||||
attr :color, :string, default: nil, doc: "color of icon"
|
||||
attr :class, :string, default: "", doc: "additional classes"
|
||||
|
@ -72,7 +73,14 @@ defmodule Liliform.Icon do
|
|||
~H"""
|
||||
<span class="icon-text">
|
||||
<span class={["icon", @class]} {@rest}>
|
||||
<.icon_svg for={@for} height={@size} width={@size} {icon_color(assigns)} />
|
||||
<.icon_svg
|
||||
for={@for}
|
||||
title={@title}
|
||||
height={@size}
|
||||
width={@size}
|
||||
{icon_color(assigns)}
|
||||
{@rest}
|
||||
/>
|
||||
</span>
|
||||
<%= if @inner_block != [] do %>
|
||||
<span>
|
||||
|
@ -87,6 +95,7 @@ defmodule Liliform.Icon do
|
|||
Renders an icon without the icon-text wrapper.
|
||||
"""
|
||||
attr :for, :string, required: true, doc: "icon name"
|
||||
attr :title, :string, default: nil, doc: "icon title"
|
||||
attr :size, :string, default: nil, doc: "size of icon"
|
||||
attr :color, :string, default: nil, doc: "color of icon"
|
||||
attr :rest, :global
|
||||
|
@ -97,7 +106,7 @@ defmodule Liliform.Icon do
|
|||
|> set_bulma_classes()
|
||||
|
||||
~H"""
|
||||
<.icon_svg for={@for} height={@size} width={@size} {icon_color(assigns)} {@rest} />
|
||||
<.icon_svg for={@for} title={@title} height={@size} width={@size} {icon_color(assigns)} {@rest} />
|
||||
"""
|
||||
end
|
||||
|
||||
|
@ -112,38 +121,35 @@ defmodule Liliform.Icon do
|
|||
|
||||
defp icon_color(assigns) do
|
||||
if Features.enabled?(:colorhash) do
|
||||
color =
|
||||
case assigns.color do
|
||||
nil -> ""
|
||||
"auto" -> Colorhash.hsl(assigns.for)
|
||||
color -> color
|
||||
end
|
||||
|
||||
auto_color =
|
||||
case assigns.for do
|
||||
"alert" -> [class: "has-text-danger"]
|
||||
"info" -> [class: "has-text-info"]
|
||||
"success" -> [class: "has-text-success"]
|
||||
"warning" -> [class: "has-text-warning"]
|
||||
"error" -> [class: "has-text-danger"]
|
||||
"all" -> [color: "magenta"]
|
||||
"compute" -> [color: "blue"]
|
||||
"storage" -> [color: "green"]
|
||||
"network" -> [color: "orange"]
|
||||
"system" -> [color: "purple"]
|
||||
"account" -> [color: "darkblue"]
|
||||
_ -> [color: color]
|
||||
end
|
||||
|
||||
case color do
|
||||
"auto" ->
|
||||
auto_color
|
||||
|
||||
"" ->
|
||||
case assigns.color do
|
||||
nil ->
|
||||
[]
|
||||
|
||||
_ ->
|
||||
[color: color]
|
||||
clr ->
|
||||
case String.downcase(clr) do
|
||||
"auto" ->
|
||||
case String.downcase(assigns.for) do
|
||||
"alert" -> [class: "has-text-danger"]
|
||||
"info" -> [class: "has-text-info"]
|
||||
"success" -> [class: "has-text-success"]
|
||||
"warning" -> [class: "has-text-warning"]
|
||||
"error" -> [class: "has-text-danger"]
|
||||
"all" -> [color: "magenta"]
|
||||
"compute" -> [color: "blue"]
|
||||
"storage" -> [color: "green"]
|
||||
"network" -> [color: "orange"]
|
||||
"system" -> [color: "purple"]
|
||||
"account" -> [color: "darkblue"]
|
||||
"running" -> [color: "lime"]
|
||||
"stopped" -> [color: "gray"]
|
||||
"enabled" -> [color: "blue"]
|
||||
"disabled" -> [color: "gray"]
|
||||
_ -> [color: Colorhash.hsl(assigns.for)]
|
||||
end
|
||||
|
||||
_ ->
|
||||
[color: assigns.color]
|
||||
end
|
||||
end
|
||||
else
|
||||
[class: "has-text-dark"]
|
||||
|
@ -170,7 +176,9 @@ defmodule Liliform.Icon do
|
|||
"ntpdate" -> :clock
|
||||
"httpd" -> :globe
|
||||
"running" -> :circle_play
|
||||
"stopped" -> :circle_stop
|
||||
"enabled" -> :circle_check
|
||||
"disabled" -> :circle_x
|
||||
lucide_name -> String.to_atom(lucide_name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -53,7 +53,7 @@ defmodule Liliform.Page do
|
|||
phx-value-key={filter.key}
|
||||
>
|
||||
<.icon_raw
|
||||
for={filter.icon}
|
||||
for={filter.title}
|
||||
size="2rem"
|
||||
color={if filter.active, do: "gray", else: "auto"}
|
||||
/>
|
||||
|
|
5
mix.exs
5
mix.exs
|
@ -19,7 +19,7 @@ defmodule Freedive.MixProject do
|
|||
|
||||
defp freebsd_pkg do
|
||||
[
|
||||
service_commands: ["init", "account_register", "password_reset"],
|
||||
service_commands: ["init", "account_register", "password_reset"]
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -73,7 +73,8 @@ defmodule Freedive.MixProject do
|
|||
{:lucide_icons, "~> 1.1"},
|
||||
{:phx_component_helpers, "~> 1.4"},
|
||||
{:mix_freebsd_pkg, github: "hiway/mix_freebsd_pkg", runtime: Mix.env() == :dev},
|
||||
{:phx_config_util, "~> 0.1.0"}
|
||||
{:phx_config_util, "~> 0.1.0"},
|
||||
{:file_system, "~> 1.0"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue