defmodule GitGudWeb.SshKeyLive.Index do
use GitGudWeb, :live_view
alias GitGud.SshKeys
alias GitGud.SshKeys.SshKey
@impl true
def mount(_params, _session, socket) do
user = socket.assigns.current_scope.user
{:ok,
socket
|> stream(:keys, SshKeys.list_keys(user))
|> assign_form(SshKey.changeset(%SshKey{}, %{}))
|> assign(:page_title, "SSH keys")}
end
defp assign_form(socket, cs), do: assign(socket, :form, to_form(cs))
@impl true
def handle_event("validate", %{"ssh_key" => attrs}, socket) do
cs = %SshKey{} |> SshKey.changeset(attrs) |> Map.put(:action, :validate)
{:noreply, assign_form(socket, cs)}
end
def handle_event("add", %{"ssh_key" => attrs}, socket) do
case SshKeys.create_key(socket.assigns.current_scope.user, attrs) do
{:ok, key} ->
{:noreply,
socket
|> stream_insert(:keys, key, at: 0)
|> assign_form(SshKey.changeset(%SshKey{}, %{}))
|> put_flash(:info, "Key added.")}
{:error, cs} ->
{:noreply, assign_form(socket, cs)}
end
end
def handle_event("delete", %{"id" => id}, socket) do
key = SshKeys.get_key!(id)
if key.user_id == socket.assigns.current_scope.user.id do
{:ok, _} = SshKeys.delete_key(key)
{:noreply, socket |> stream_delete(:keys, key) |> put_flash(:info, "Key removed.")}
else
{:noreply, put_flash(socket, :error, "Not your key.")}
end
end
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_scope={@current_scope}>
<div class="space-y-6 max-w-2xl">
<h1 class="text-xl font-semibold">SSH keys</h1>
<.form for={@form} id="ssh-key-form" phx-change="validate" phx-submit="add" class="space-y-3">
<.input field={@form[:title]} label="Title" required placeholder="laptop" />
<.input
field={@form[:key]}
label="Public key"
type="textarea"
rows="4"
required
placeholder="ssh-ed25519 AAAA... user@host"
/>
<button type="submit" class="btn btn-primary">Add key</button>
</.form>
<ul id="ssh-keys" phx-update="stream" class="divide-y divide-base-300">
<li class="hidden only:block py-6 opacity-60 text-sm">No keys yet.</li>
<li :for={{id, key} <- @streams.keys} id={id} class="py-3 flex items-start gap-3">
<div class="flex-1">
<p class="font-medium">{key.title}</p>
<p class="text-xs font-mono opacity-60 mt-1 break-all">{key.fingerprint}</p>
<p :if={key.last_used_at} class="text-xs opacity-50">
last used {format_dt(key.last_used_at)}
</p>
</div>
<button
type="button"
class="btn btn-xs btn-ghost"
phx-click="delete"
phx-value-id={key.id}
data-confirm="Delete this key?"
>
<.icon name="hero-trash" class="size-4" />
</button>
</li>
</ul>
</div>
</Layouts.app>
"""
end
defp format_dt(dt), do: Calendar.strftime(dt, "%Y-%m-%d %H:%M")
end
3.1 KiB · text
5af55d9