2023-06-09 19:13:27 +00:00
|
|
|
defmodule PrymnWeb.ServerLive.Show do
|
2023-11-23 13:45:33 +00:00
|
|
|
alias DBConnection.App
|
2023-06-09 19:13:27 +00:00
|
|
|
use PrymnWeb, :live_view
|
|
|
|
|
2023-11-14 15:23:50 +00:00
|
|
|
require Logger
|
2023-08-25 21:51:04 +00:00
|
|
|
alias Prymn.{Agents, Servers}
|
2023-06-09 19:13:27 +00:00
|
|
|
|
|
|
|
@impl true
|
|
|
|
def mount(_params, _session, socket) do
|
|
|
|
{:ok, socket}
|
|
|
|
end
|
|
|
|
|
2023-11-23 13:45:33 +00:00
|
|
|
@impl true
|
|
|
|
def render(assigns) do
|
|
|
|
~H"""
|
|
|
|
<div class="mx-auto max-w-2xl">
|
|
|
|
<.header>
|
|
|
|
<span id="server-name" class="relative flex items-center">
|
|
|
|
<button title="Select a different server">
|
|
|
|
Server <%= @server.name %>
|
|
|
|
<.icon name="hero-chevron-down" />
|
|
|
|
</button>
|
|
|
|
<button class="ml-4" title="Edit server name" phx-click={show_edit_server_name()}>
|
|
|
|
<.icon class="h-3 w-3" name="hero-pencil" />
|
|
|
|
</button>
|
|
|
|
<span
|
|
|
|
role="tooltip"
|
|
|
|
class={[
|
|
|
|
"absolute -left-6 inline-flex h-3 w-3 before:-translate-x-1/2 before:-translate-y-full",
|
|
|
|
"before:-top-2 before:left-1/2 before:absolute before:text-sm before:text-white",
|
|
|
|
"before:font-normal before:content-[attr(data-tip)] before:opacity-0",
|
|
|
|
"hover:before:opacity-100 before:py-1 before:px-2 before:bg-black",
|
|
|
|
"before:rounded before:pointer-events-none before:transition-opacity"
|
|
|
|
]}
|
|
|
|
data-tip={@health.message}
|
|
|
|
>
|
|
|
|
<%= case @health.message do %>
|
|
|
|
<% "Connected" -> %>
|
|
|
|
<span class="absolute top-0 left-0 h-full w-full animate-ping rounded-full bg-green-400 opacity-75" />
|
|
|
|
<span class="h-3 w-3 rounded-full bg-green-500" />
|
|
|
|
<% "Disconnected" -> %>
|
|
|
|
<span class="h-3 w-3 rounded-full bg-red-500" />
|
|
|
|
<% _ -> %>
|
|
|
|
<span class="h-3 w-3 rounded-full bg-yellow-500" />
|
|
|
|
<% end %>
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
<form class="hidden" id="server-name-edit" phx-submit={submit_edit_server_name()}>
|
|
|
|
<input
|
|
|
|
class="border-0 border-b border-gray-500"
|
|
|
|
type="text"
|
|
|
|
name="name"
|
|
|
|
value={@server.name}
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
<button type="submit" title="Confirm" class="ml-4">
|
|
|
|
<.icon class="h-4 w-4" name="hero-check" />
|
|
|
|
</button>
|
|
|
|
</form>
|
|
|
|
<:subtitle>
|
|
|
|
<%= @server.public_ip %>
|
|
|
|
</:subtitle>
|
|
|
|
<:actions>
|
|
|
|
<Button.primary size="sm">New App</Button.primary>
|
|
|
|
<Button.secondary size="sm">
|
|
|
|
Quick actions <.icon name="hero-chevron-down" class="ml-1" />
|
|
|
|
</Button.secondary>
|
|
|
|
</:actions>
|
|
|
|
</.header>
|
|
|
|
<div class="my-3 text-sm text-slate-700">
|
|
|
|
<%= for {name, task} <- @health.tasks do %>
|
|
|
|
Background task in progress: <%= name %>
|
|
|
|
<p><%= task.progress %> complete</p>
|
|
|
|
<% end %>
|
|
|
|
</div>
|
|
|
|
<div :if={@server.status == :unregistered} class="my-10">
|
|
|
|
<p class="mb-9">
|
|
|
|
Connect to your server using root credentials and execute the following command:
|
|
|
|
</p>
|
|
|
|
<div class="group inline-flex items-center rounded-lg bg-gray-800 p-4 pl-6 text-white">
|
|
|
|
<code class="flex gap-4">
|
|
|
|
<span class="select-none text-gray-500">#</span>
|
|
|
|
<span class="flex-1">
|
|
|
|
<%= @registration_command %>
|
|
|
|
</span>
|
|
|
|
</code>
|
|
|
|
<button type="button" tabindex="-1">
|
|
|
|
<.icon
|
|
|
|
name="hero-document-duplicate-solid"
|
|
|
|
class="invisible ml-4 animate-bounce text-gray-500 group-hover:visible"
|
|
|
|
/>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div :if={@server.status == :registered} class="my-10">
|
|
|
|
<.live_component
|
|
|
|
id={"system_info-#{@server.name}"}
|
|
|
|
module={PrymnWeb.SystemInfo}
|
|
|
|
ip={@server.public_ip}
|
|
|
|
/>
|
|
|
|
<section class="mt-4">
|
|
|
|
<form phx-change="change_dry_run">
|
|
|
|
<.input type="checkbox" name="dry_run" value={@dry_run} label="Enable dry-run operations" />
|
|
|
|
</form>
|
|
|
|
</section>
|
|
|
|
<section class="mt-4">
|
|
|
|
<h2 class="border-b border-solid border-gray-500 pb-1 text-2xl font-medium">System</h2>
|
|
|
|
<p class="mt-4">
|
|
|
|
Updates: <%= 0 %> pending updates.
|
|
|
|
<Button.primary type="button" class="ml-4" phx-click="system_update">
|
|
|
|
Update now
|
|
|
|
</Button.primary>
|
|
|
|
<p :for={output <- assigns.update_output}>
|
|
|
|
<%= output %>
|
|
|
|
</p>
|
|
|
|
</p>
|
|
|
|
</section>
|
|
|
|
<section class="mt-4">
|
|
|
|
<h2 class="border-b border-solid border-gray-500 pb-1 text-2xl font-medium">
|
|
|
|
Backups
|
|
|
|
</h2>
|
|
|
|
<.table id="backups" rows={[%{date: "2023-10-11"}, %{date: "2023-10-10"}]}>
|
|
|
|
<:col :let={backup} label="Date"><%= backup.date %></:col>
|
|
|
|
<:action>
|
|
|
|
<Button.primary>Restore</Button.primary>
|
|
|
|
</:action>
|
|
|
|
</.table>
|
|
|
|
</section>
|
|
|
|
<section class="mt-4">
|
|
|
|
<h2 class="border-b border-solid border-gray-500 pb-1 text-2xl font-medium">
|
|
|
|
Manage Services
|
|
|
|
</h2>
|
|
|
|
<.table
|
|
|
|
id="services"
|
|
|
|
rows={[%{name: "mariadb", status: "Active"}, %{name: "php8.0", status: "Disabled"}]}
|
|
|
|
>
|
|
|
|
<:col :let={service} label="Service"><%= service.name %></:col>
|
|
|
|
<:col :let={service} label="Status"><%= service.status %></:col>
|
|
|
|
<:action>
|
|
|
|
<Button.primary>Activate</Button.primary>
|
|
|
|
<Button.secondary>Deactivate</Button.secondary>
|
|
|
|
</:action>
|
|
|
|
</.table>
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
<.back navigate={~p"/servers"}>Back to servers</.back>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2023-06-09 19:13:27 +00:00
|
|
|
@impl true
|
|
|
|
def handle_params(%{"id" => id}, _, socket) do
|
|
|
|
server = Servers.get_server!(id)
|
2023-08-25 21:51:04 +00:00
|
|
|
|
2023-08-28 20:32:42 +00:00
|
|
|
if connected?(socket) and server.status == :registered do
|
|
|
|
Agents.subscribe_to_health(server.public_ip)
|
|
|
|
Agents.start_connection(server.public_ip)
|
2023-08-25 21:51:04 +00:00
|
|
|
end
|
2023-06-09 19:13:27 +00:00
|
|
|
|
2023-08-28 20:32:42 +00:00
|
|
|
health = Agents.get_health(server.public_ip)
|
|
|
|
|
2023-06-09 19:13:27 +00:00
|
|
|
{:noreply,
|
|
|
|
socket
|
|
|
|
|> assign(:page_title, server.name)
|
2023-11-19 22:27:36 +00:00
|
|
|
|> assign(:health, health || %{message: "Connecting...", tasks: []})
|
2023-07-09 16:41:41 +00:00
|
|
|
|> assign(:server, server)
|
2023-11-14 15:23:50 +00:00
|
|
|
|> assign(:dry_run, false)
|
|
|
|
|> assign(:update_output, [])
|
|
|
|
# TODO: Do not assign this to the socket - instead generate it in the HTML
|
2023-07-09 16:41:41 +00:00
|
|
|
|> assign(:registration_command, Servers.create_setup_command(server))}
|
2023-06-09 19:13:27 +00:00
|
|
|
end
|
2023-08-25 21:51:04 +00:00
|
|
|
|
2023-11-20 16:50:08 +00:00
|
|
|
@impl true
|
2023-11-14 15:23:50 +00:00
|
|
|
def handle_info(%PrymnProto.Prymn.SysUpdateResponse{} = response, socket) do
|
|
|
|
output = String.split(response.output, "\n")
|
|
|
|
socket = assign(socket, :update_output, output)
|
|
|
|
{:noreply, socket}
|
|
|
|
end
|
|
|
|
|
2023-08-28 20:32:42 +00:00
|
|
|
def handle_info(%Agents.Health{} = health, socket) do
|
|
|
|
{:noreply, assign(socket, :health, health)}
|
|
|
|
end
|
|
|
|
|
2023-11-14 15:23:50 +00:00
|
|
|
@impl true
|
|
|
|
def handle_event("system_update", _params, socket) do
|
|
|
|
host_address = get_in(socket.assigns, [:server, Access.key(:public_ip)])
|
|
|
|
server_name = get_in(socket.assigns, [:server, Access.key(:name)])
|
|
|
|
|
|
|
|
socket =
|
|
|
|
if host_address do
|
|
|
|
Agents.sys_update(host_address, socket.assigns.dry_run)
|
|
|
|
put_flash(socket, :info, "Started a system update on server #{server_name}.")
|
|
|
|
else
|
|
|
|
put_flash(
|
|
|
|
socket,
|
|
|
|
:error,
|
|
|
|
"Could not perform the update. Your server does not seem to have an address"
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
{:noreply, socket}
|
|
|
|
end
|
|
|
|
|
2023-11-23 13:45:33 +00:00
|
|
|
def handle_event("edit_server_name", %{"name" => name}, socket) do
|
|
|
|
server =
|
|
|
|
socket.assigns.server
|
|
|
|
|> Servers.update_server(%{"name" => name})
|
|
|
|
|> case do
|
|
|
|
{:ok, server} -> server
|
|
|
|
{:error, _} -> raise "Oops"
|
|
|
|
end
|
|
|
|
|
|
|
|
{:noreply, assign(socket, :server, server)}
|
|
|
|
end
|
|
|
|
|
2023-11-14 15:23:50 +00:00
|
|
|
def handle_event("change_dry_run", %{"dry_run" => enabled}, socket) do
|
|
|
|
enabled = (enabled == "true" && true) || false
|
|
|
|
{:noreply, assign(socket, :dry_run, enabled)}
|
|
|
|
end
|
2023-11-23 13:45:33 +00:00
|
|
|
|
|
|
|
defp show_edit_server_name() do
|
|
|
|
JS.hide(to: "#server-name")
|
|
|
|
|> JS.show(to: "#server-name-edit")
|
|
|
|
|> JS.focus_first(to: "#server-name-edit")
|
|
|
|
end
|
|
|
|
|
|
|
|
defp submit_edit_server_name() do
|
|
|
|
JS.push("edit_server_name")
|
|
|
|
|> JS.hide()
|
|
|
|
|> JS.show(to: "#server-name", display: "flex")
|
|
|
|
end
|
2023-06-09 19:13:27 +00:00
|
|
|
end
|