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: "session", readonly: false} Secrets.change_secret(%Secret{}, attrs) end) |> assign_new(:grant_access_secret, fn -> Enum.find( socket.assigns.saved_secrets, &(&1.name == secret_name and secret_name not in Map.keys(socket.assigns.secrets)) ) 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_name, _} <- Enum.sort(@secrets)} secret_name={secret_name} secret_origin={:session} stored="Session" active={secret_name == @prefill_secret_name} target={@myself} /> <.secret_with_badge :for={secret <- @saved_secrets} :if={!is_map_key(@secrets, secret.name) and @secrets[secret.name] != secret.value} secret_name={secret.name} stored={hub_label(@hub)} active={false} 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" /> <.text_field field={f[:value]} label="Value" autofocus={@prefill_secret_name != nil} spellcheck="false" autocomplete="off" phx-debounce /> <.radio_field field={f[:hub_id]} label="Storage" options={[ {"session", "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 assigns = assign_new(assigns, :secret_origin, fn -> nil end) ~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("grant_access", %{"name" => secret_name} = attrs, socket) do cond do attrs["origin"] == "session" and is_map_key(socket.assigns.secrets, secret_name) -> Session.set_secret(socket.assigns.session.pid, %{ name: secret_name, value: socket.assigns.secrets[secret_name] }) secret = Enum.find(socket.assigns.saved_secrets, &(&1.name == secret_name)) -> Session.set_secret(socket.assigns.session.pid, secret) 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: "session"} = 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