defmodule LivebookWeb.Hub.Edit.PersonalComponent do use LivebookWeb, :live_component alias Livebook.Hubs.Personal alias LivebookWeb.LayoutHelpers @impl true def update(assigns, socket) do changeset = Personal.change_hub(assigns.hub) secret_value = if assigns.live_action == :edit_secret do secret = Enum.find(assigns.secrets, &(&1.name == assigns.secret_name)) secret.value end {:ok, socket |> assign(assigns) |> assign(changeset: changeset, stamp_changeset: changeset, secret_value: secret_value)} end @impl true def render(assigns) do ~H"""

Your personal hub. Only you can see and access the data in it.

General

<.form :let={f} id={@id} class="flex flex-col mt-4 space-y-4" for={@changeset} phx-submit="save" phx-change="validate" phx-target={@myself} >
<.text_field field={f[:hub_name]} label="Name" /> <.emoji_field field={f[:hub_emoji]} label="Emoji" />

Secrets

Secrets are a safe way to share credentials and tokens with notebooks. They are often shared with Smart cells and can be read as environment variables using the LB_ prefix.

<.secrets_list id="hub-secrets-list" new_secret_path={~p"/hub/#{@hub.id}/secrets/new"} secrets={@secrets} target={@myself} />

Stamping

Notebooks may be stamped using your secret key. A stamp allows to securely store information such as the names of the secrets that you granted access to. You must not share your secret key with others. But you may copy the secret key between different machines you own.

If you change the secret key, you will need to grant access to secrets once again in previously stamped notebooks.

<.form :let={f} id={"#{@id}-stamp"} class="flex flex-col mt-4 space-y-4" for={@stamp_changeset} phx-submit="stamp_save" phx-change="stamp_validate" phx-target={@myself} >
<.password_field field={f[:secret_key]} label="Secret key" />
<.modal :if={@live_action in [:new_secret, :edit_secret]} id="secrets-modal" show width={:big} patch={~p"/hub/#{@hub.id}"} > <.live_component module={LivebookWeb.Hub.SecretFormComponent} id="secrets" hub={@hub} secret_name={@secret_name} secret_value={@secret_value} return_to={~p"/hub/#{@hub.id}"} />
""" end defp secrets_list(assigns) do ~H"""
<.no_entries :if={@secrets == []}> No secrets in this Hub yet.
<.secret_info secret={secret} target={@target} />
<.link patch={@new_secret_path} class="button-base button-blue" id="add-secret"> Add secret
""" end defp secret_info(assigns) do ~H"""
<.labeled_text label="Name"> <%= @secret.name %>
<.menu id={"hub-secret-#{@secret.name}-menu"}> <:toggle> <.menu_item> <.link id={"hub-secret-#{@secret.name}-edit"} patch={~p"/hub/#{@secret.hub_id}/secrets/edit/#{@secret.name}"} type="button" role="menuitem" > <.remix_icon icon="file-edit-line" /> Edit <.menu_item variant={:danger}>
""" end @impl true def handle_event("save", %{"personal" => params}, socket) do {:noreply, save(params, :changeset, socket)} end def handle_event("validate", %{"personal" => params}, socket) do {:noreply, validate(params, :changeset, socket)} end def handle_event("stamp_save", %{"personal" => params}, socket) do {:noreply, save(params, :stamp_changeset, socket)} end def handle_event("stamp_validate", %{"personal" => params}, socket) do {:noreply, validate(params, :stamp_changeset, socket)} end def handle_event("generate_secret_key", %{}, socket) do params = %{"secret_key" => Personal.generate_secret_key()} {:noreply, validate(params, :stamp_changeset, socket)} end def handle_event("delete_hub_secret", attrs, socket) do {:ok, secret} = Livebook.Secrets.update_secret(%Livebook.Secrets.Secret{}, attrs) :ok = Livebook.Hubs.delete_secret(socket.assigns.hub, secret) {:noreply, socket} end defp save(params, changeset_name, socket) do case Personal.update_hub(socket.assigns.hub, params) do {:ok, hub} -> socket |> put_flash(:success, "Hub updated successfully") |> push_navigate(to: ~p"/hub/#{hub.id}") {:error, changeset} -> assign(socket, changeset_name, changeset) end end defp validate(params, changeset_name, socket) do assign(socket, changeset_name, Personal.validate_hub(socket.assigns.hub, params)) end end