defmodule LivebookWeb.SessionLive.SecretsComponent do use LivebookWeb, :live_component alias Livebook.Hubs alias Livebook.Secrets alias Livebook.Secrets.Secret alias Livebook.Session @impl true def mount(socket) do {:ok, assign(socket, title: title(socket))} end @impl true def update(assigns, socket) do socket = assign(socket, assigns) secret_name = socket.assigns[:prefill_secret_name] socket = socket |> assign_new(:changeset, fn -> attrs = %{name: secret_name, value: nil, hub_id: nil, readonly: false} Secrets.change_secret(%Secret{}, attrs) end) |> assign_new(:grant_access_secret, fn -> Enum.find( socket.assigns.hub_secrets, &(&1.name == secret_name and not is_map_key(socket.assigns.secrets, secret_name)) ) end) {:ok, socket} end @impl true def render(assigns) do ~H"""

<%= @title %>

<.grant_access_message :if={@grant_access_secret} secret={@grant_access_secret} target={@myself} hub={@hub} />

Choose a secret

<.secret_with_badge :for={ secret <- @secrets |> Session.Data.session_secrets(@hub.id) |> Enum.sort_by(& &1.name) } secret_name={secret.name} hub?={false} stored="Session" active={secret.name == @prefill_secret_name} target={@myself} /> <.secret_with_badge :for={secret <- Enum.sort_by(@hub_secrets, & &1.name)} secret_name={secret.name} hub?={true} stored={hub_label(@hub)} active={ secret.name == @prefill_secret_name and Session.Data.secret_toggled?(secret, @secrets) } target={@myself} />
<.remix_icon icon="folder-lock-line" class="align-middle text-2xl" /> Secrets not found.
Add to see them here.
<.form :let={f} for={@changeset} phx-target={@myself} phx-change="validate" phx-submit="save" autocomplete="off" class="basis-1/2 grow" >

Add new secret

<.text_field field={f[:name]} label="Name (alphanumeric and underscore)" autofocus={@prefill_secret_name == nil} spellcheck="false" autocomplete="off" phx-debounce class="uppercase" /> <.password_field field={f[:value]} label="Value" autofocus={@prefill_secret_name != nil} spellcheck="false" autocomplete="off" phx-debounce /> <.radio_field field={f[:hub_id]} options={[ {"", "only this session"}, {@hub.id, "in #{@hub.hub_emoji} #{@hub.hub_name}"} ]} />
<.link patch={@return_to} class="button-base button-outlined-gray"> Cancel
""" end defp secret_with_badge(assigns) do ~H"""
<%= @secret_name %> <%= @stored %>
""" end defp grant_access_message(assigns) do ~H"""
<.remix_icon icon="error-warning-fill" class="align-middle text-2xl flex text-gray-100 rounded-lg py-2" /> There is a secret named <%= @secret.name %> in <%= hub_label(@hub) %>. Allow this session to access it?
""" end @impl true def handle_event("save", %{"secret" => attrs}, socket) do with {:ok, secret} <- Secrets.update_secret(%Secret{}, attrs), :ok <- set_secret(socket, secret) do {:noreply, socket |> push_patch(to: socket.assigns.return_to) |> push_secret_selected(secret.name)} else {:error, changeset} -> {:noreply, assign(socket, changeset: changeset)} end end def handle_event("validate", %{"secret" => attrs}, socket) do changeset = %Secret{} |> Secrets.change_secret(attrs) |> Map.put(:action, :validate) {:noreply, assign(socket, changeset: changeset)} end def handle_event("select_secret", %{"name" => secret_name} = attrs, socket) do if attrs["hub"] do secret = Enum.find(socket.assigns.hub_secrets, &(&1.name == secret_name)) unless Session.Data.secret_toggled?(secret, socket.assigns.secrets) do Session.set_secret(socket.assigns.session.pid, secret) end end {:noreply, socket |> push_patch(to: socket.assigns.return_to) |> push_secret_selected(secret_name)} end defp push_secret_selected(%{assigns: %{select_secret_ref: nil}} = socket, _), do: socket defp push_secret_selected(%{assigns: %{select_secret_ref: ref}} = socket, secret_name) do push_event(socket, "secret_selected", %{select_secret_ref: ref, secret_name: secret_name}) end defp title(%{assigns: %{select_secret_ref: nil}}), do: "Add secret" defp title(%{assigns: %{select_secret_options: %{"title" => title}}}), do: title defp title(_), do: "Select secret" defp set_secret(socket, %Secret{hub_id: nil} = secret) do Session.set_secret(socket.assigns.session.pid, secret) end defp set_secret(socket, %Secret{} = secret) do with :ok <- Hubs.create_secret(socket.assigns.hub, secret) do Session.set_secret(socket.assigns.session.pid, secret) end end defp hub_label(hub), do: "#{hub.hub_emoji} #{hub.hub_name}" end