defmodule LivebookWeb.Hub.Edit.PersonalComponent do use LivebookWeb, :live_component alias Livebook.Hubs alias Livebook.Hubs.Personal alias LivebookWeb.LayoutComponents alias LivebookWeb.NotFoundError @impl true def update(assigns, socket) do socket = assign(socket, assigns) changeset = Personal.change_hub(assigns.hub) secrets = Hubs.get_secrets(assigns.hub) file_systems = Hubs.get_file_systems(assigns.hub, hub_only: true) secret_name = assigns.params["secret_name"] file_system_id = assigns.params["file_system_id"] secret_value = if assigns.live_action == :edit_secret do Enum.find_value(secrets, &(&1.name == secret_name and &1.value)) || raise(NotFoundError, "could not find secret matching #{inspect(secret_name)}") end file_system = if assigns.live_action == :edit_file_system do Enum.find_value(file_systems, &(&1.id == file_system_id && &1)) || raise(NotFoundError, "could not find file system matching #{inspect(file_system_id)}") end {:ok, assign(socket, secrets: secrets, file_system: file_system, file_system_id: file_system_id, file_systems: file_systems, changeset: changeset, stamp_changeset: changeset, secret_name: secret_name, secret_value: secret_value )} end @impl true def render(assigns) do ~H"""

Your personal workspace. All data is stored on your machine and only you can access it.

General

<.form :let={f} id={@id} class="flex flex-col md:flex-row mt-4 space-y-4 md:space-x-2 md:space-y-0" 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" />
<.button type="submit" phx-disable-with="Updating..." disabled={not @changeset.valid?}> Save

Secrets

Secrets are a safe way to allow notebooks to access credentials and tokens.

<.live_component module={LivebookWeb.Hub.SecretListComponent} id="hub-secrets-list" hub={@hub} secrets={@secrets} edit_path={"hub/#{@hub.id}/secrets/edit"} return_to={~p"/hub/#{@hub.id}"} />
<.button patch={~p"/hub/#{@hub.id}/secrets/new"} id="add-secret"> Add secret

File Storages

File storages are used to store notebooks and their files.

<.live_component module={LivebookWeb.Hub.FileSystemListComponent} id="hub-file-systems-list" hub_id={@hub.id} file_systems={@file_systems} disabled={false} />

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 mt-4 space-x-2" for={@stamp_changeset} phx-submit="stamp_save" phx-change="stamp_validate" phx-target={@myself} >
<.password_field field={f[:secret_key]} label="Secret key" />
<.button color="gray" small type="button" phx-click="generate_secret_key" phx-target={@myself} > <.remix_icon icon="refresh-line" class="text-xl leading-none py-1" />
<.button type="submit" phx-disable-with="Updating..." disabled={not @stamp_changeset.valid?} > Save
<.modal :if={@live_action in [:new_secret, :edit_secret]} id="secrets-modal" show width="medium" patch={~p"/hub/#{@hub.id}"} > <.live_component module={LivebookWeb.Hub.SecretFormComponent} id="secrets" hub={@hub} secret_name={@secret_name} secret_value={@secret_value} disabled={false} return_to={~p"/hub/#{@hub.id}"} /> <.modal :if={@live_action in [:new_file_system, :edit_file_system]} id="file-systems-modal" show width="medium" patch={~p"/hub/#{@hub.id}"} > <.live_component module={LivebookWeb.Hub.FileSystemFormComponent} id="file-systems" hub={@hub} disabled={false} file_system={@file_system} file_system_id={@file_system_id} return_to={~p"/hub/#{@hub.id}"} />
""" 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 defp save(params, changeset_name, socket) do case Personal.update_hub(socket.assigns.hub, params) do {:ok, hub} -> socket |> put_flash(:success, "Workspace 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