defmodule LivebookWeb.Hub.Edit.TeamComponent do use LivebookWeb, :live_component alias Livebook.Hubs alias Livebook.Hubs.{Provider, Team} alias Livebook.Teams alias LivebookWeb.LayoutHelpers alias LivebookWeb.NotFoundError @impl true def update(assigns, socket) do socket = assign(socket, assigns) changeset = Team.change_hub(assigns.hub) show_key? = assigns.params["show-key"] == "true" 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"] is_default? = is_default?(assigns.hub) 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, socket |> assign( secrets: secrets, file_system: file_system, file_system_id: file_system_id, file_systems: file_systems, show_key: show_key?, secret_name: secret_name, secret_value: secret_value, hub_metadata: Provider.to_metadata(assigns.hub), is_default: is_default? ) |> assign_new(:config_changeset, fn -> Hubs.Dockerfile.config_changeset() end) |> update_dockerfile() |> assign_form(changeset)} end @impl true def render(assigns) do ~H"""
<%= Provider.connection_error(@hub) %>

General

<.form :let={f} id={@id} class="flex flex-col mt-4 space-y-4" for={@form} phx-submit="save" phx-change="validate" phx-target={@myself} >
<.text_field field={f[:hub_name]} label="Name" disabled help="Name cannot be changed" class="bg-gray-200/50 border-200/80 cursor-not-allowed" /> <.emoji_field field={f[:hub_emoji]} label="Emoji" />

Secrets

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

<.live_component module={LivebookWeb.Hub.SecretListComponent} id="hub-secrets-list" hub={@hub} secrets={@secrets} target={@myself} />

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} target={@myself} />

Airgapped deployment

It is possible to deploy notebooks that belong to this Hub in an airgapped deployment, without connecting back to Livebook Teams server. Configure the deployment below and use the generated Dockerfile in a directory with notebooks that belong to your Organization.

<.form :let={f} for={@config_changeset} as={:data} phx-change="validate_dockerfile" phx-target={@myself} >

Danger zone

Delete this hub

This only removes the hub from this machine. You must rejoin to access its features once again.

<.modal show={@show_key} id="show-key-modal" width={:medium} patch={~p"/hub/#{@hub.id}"}> <.teams_key_modal teams_key={@hub.teams_key} /> <.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} 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} file_system={@file_system} file_system_id={@file_system_id} return_to={~p"/hub/#{@hub.id}"} />
""" end defp org_url(hub, path) do Livebook.Config.teams_url() <> "/orgs/#{hub.org_id}" <> path end defp teams_key_modal(assigns) do ~H"""

Teams key

This is your Teams key. This key encrypts your data before it is sent to Livebook Teams servers. This key is required for you and invited users to join this organization. We recommend storing it somewhere safe:
""" end @impl true def handle_event("save", %{"team" => params}, socket) do case Teams.update_hub(socket.assigns.hub, params) do {:ok, hub} -> {:noreply, socket |> put_flash(:success, "Hub updated successfully") |> push_navigate(to: ~p"/hub/#{hub.id}")} {:error, changeset} -> {:noreply, assign_form(socket, changeset)} end end def handle_event("validate", %{"team" => attrs}, socket) do changeset = socket.assigns.hub |> Team.change_hub(attrs) |> Map.replace!(:action, :validate) {:noreply, assign_form(socket, changeset)} end def handle_event("delete_hub_secret", attrs, socket) do %{hub: hub} = socket.assigns on_confirm = fn socket -> {:ok, secret} = Livebook.Secrets.update_secret(%Livebook.Secrets.Secret{}, attrs) case Livebook.Hubs.delete_secret(hub, secret) do :ok -> socket {:transport_error, reason} -> put_flash(socket, :error, reason) end end {:noreply, confirm(socket, on_confirm, title: "Delete hub secret - #{attrs["name"]}", description: "Are you sure you want to delete this hub secret?", confirm_text: "Delete", confirm_icon: "delete-bin-6-line" )} end def handle_event("validate_dockerfile", %{"data" => data}, socket) do changeset = data |> Hubs.Dockerfile.config_changeset() |> Map.replace!(:action, :validate) {:noreply, socket |> assign(config_changeset: changeset) |> update_dockerfile()} end def handle_event("mark_as_default", _, socket) do Hubs.set_default_hub(socket.assigns.hub.id) {:noreply, push_navigate(socket, to: ~p"/hub/#{socket.assigns.hub.id}")} end def handle_event("remove_as_default", _, socket) do Hubs.unset_default_hub(socket.assigns.hub.id) {:noreply, push_navigate(socket, to: ~p"/hub/#{socket.assigns.hub.id}")} end defp is_default?(hub) do Hubs.get_default_hub().id == hub.id end defp assign_form(socket, %Ecto.Changeset{} = changeset) do assign(socket, form: to_form(changeset)) end defp update_dockerfile(socket) do config = socket.assigns.config_changeset |> Ecto.Changeset.apply_changes() |> Map.replace!(:deploy_all, true) %{hub: hub, secrets: hub_secrets, file_systems: hub_file_systems} = socket.assigns dockerfile = Hubs.Dockerfile.build_dockerfile(config, hub, hub_secrets, hub_file_systems, nil, [], %{}) assign(socket, :dockerfile, dockerfile) end end