mirror of
				https://github.com/livebook-dev/livebook.git
				synced 2025-11-01 00:06:04 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			254 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
| defmodule LivebookWeb.SessionLive.AppInfoComponent do
 | |
|   use LivebookWeb, :live_component
 | |
| 
 | |
|   import LivebookWeb.AppComponents
 | |
| 
 | |
|   @impl true
 | |
|   def render(assigns) do
 | |
|     ~H"""
 | |
|     <div class="flex flex-col">
 | |
|       <div class="flex items-center justify-between">
 | |
|         <h3 class="uppercase text-sm font-semibold text-gray-500">
 | |
|           App
 | |
|         </h3>
 | |
|         <.app_info_icon />
 | |
|       </div>
 | |
|       <%= if @session.mode == :app do %>
 | |
|         <div class="mt-5 flex flex-col">
 | |
|           <.message_box
 | |
|             kind={:info}
 | |
|             message="This session is a running app. To deploy a modified version, you can fork it."
 | |
|           />
 | |
|           <div class="mt-6">
 | |
|             <.button phx-click="fork_session">
 | |
|               <.remix_icon icon="git-branch-line" />
 | |
|               <span>Fork</span>
 | |
|             </.button>
 | |
|           </div>
 | |
|         </div>
 | |
|       <% else %>
 | |
|         <div class="flex flex-col gap-3 mt-2">
 | |
|           <.message_box
 | |
|             :if={@any_session_secrets?}
 | |
|             kind={:warning}
 | |
|             message="The notebook uses session secrets, but those are not available to deployed apps. Convert them to Workspace secrets instead."
 | |
|           />
 | |
| 
 | |
|           <div class="flex flex-col space-y-3">
 | |
|             <.labeled_text label="Slug" one_line>
 | |
|               <%= @settings.slug || "?" %>
 | |
|             </.labeled_text>
 | |
| 
 | |
|             <.labeled_text label="Session type" one_line>
 | |
|               <%= if @settings.multi_session, do: "Multi", else: "Single" %>
 | |
|             </.labeled_text>
 | |
| 
 | |
|             <.labeled_text label="Access" one_line>
 | |
|               <%= if @settings.access_type == :public do %>
 | |
|                 No password <.remix_icon icon="lock-unlock-line" />
 | |
|               <% else %>
 | |
|                 Password protected <.remix_icon icon="lock-password-line" />
 | |
|               <% end %>
 | |
|             </.labeled_text>
 | |
|           </div>
 | |
| 
 | |
|           <.button color="gray" outlined patch={~p"/sessions/#{@session.id}/settings/app"}>
 | |
|             Configure
 | |
|           </.button>
 | |
|         </div>
 | |
| 
 | |
|         <h3 class="mt-12 uppercase text-sm font-semibold text-gray-500">
 | |
|           Remote deployment
 | |
|         </h3>
 | |
| 
 | |
|         <div class="mt-2 flex flex-col gap-2">
 | |
|           <.button
 | |
|             color="blue"
 | |
|             patch={
 | |
|               cond do
 | |
|                 Livebook.Hubs.Provider.type(@hub) != "team" ->
 | |
|                   ~p"/sessions/#{@session.id}/app-teams-hub-info"
 | |
| 
 | |
|                 not Livebook.Notebook.AppSettings.valid?(@settings) ->
 | |
|                   ~p"/sessions/#{@session.id}/settings/app?context=app-teams"
 | |
| 
 | |
|                 true ->
 | |
|                   ~p"/sessions/#{@session.id}/app-teams"
 | |
|               end
 | |
|             }
 | |
|           >
 | |
|             <.remix_icon icon="rocket-line" /> Deploy with Livebook Teams
 | |
|           </.button>
 | |
| 
 | |
|           <.button
 | |
|             color="gray"
 | |
|             outlined
 | |
|             patch={
 | |
|               if Livebook.Notebook.AppSettings.valid?(@settings),
 | |
|                 do: ~p"/sessions/#{@session.id}/app-docker",
 | |
|                 else: ~p"/sessions/#{@session.id}/settings/app?context=app-docker"
 | |
|             }
 | |
|           >
 | |
|             <.remix_icon icon="ship-line" /> Manual Docker deployment
 | |
|           </.button>
 | |
|         </div>
 | |
| 
 | |
|         <h3 class="mt-12 uppercase text-sm font-semibold text-gray-500">
 | |
|           Local preview
 | |
|         </h3>
 | |
| 
 | |
|         <div class="flex flex-col mt-2 space-y-4">
 | |
|           <div :if={@app} class="flex flex-col space-y-3">
 | |
|             <.labeled_text label="URL" one_line>
 | |
|               <a href={~p"/apps/#{@app.slug}"}>
 | |
|                 <%= ~p"/apps/#{@app.slug}" %>
 | |
|               </a>
 | |
|             </.labeled_text>
 | |
| 
 | |
|             <.labeled_text :if={@app.multi_session} label="Latest version" one_line>
 | |
|               v<%= @app.version %>
 | |
|             </.labeled_text>
 | |
| 
 | |
|             <div :if={@app.sessions != []}>
 | |
|               <span class="text-sm text-gray-500">Running sessions</span>
 | |
| 
 | |
|               <div class="mt-2 flex flex-col space-y-4">
 | |
|                 <.app_sessions app={@app} myself={@myself} />
 | |
|               </div>
 | |
|             </div>
 | |
|           </div>
 | |
| 
 | |
|           <div class={["grid gap-2", @app && "grid-cols-2"]}>
 | |
|             <span class="flex flex-col">
 | |
|               <%= if @app do %>
 | |
|                 <.button color="gray" outlined phx-click="deploy_app">
 | |
|                   <.remix_icon icon="slideshow-4-line" /> Relaunch
 | |
|                 </.button>
 | |
|               <% else %>
 | |
|                 <.button color="blue" phx-click="deploy_app">
 | |
|                   <.remix_icon icon="slideshow-4-line" /> Launch preview
 | |
|                 </.button>
 | |
|               <% end %>
 | |
|             </span>
 | |
|             <.button
 | |
|               :if={@app}
 | |
|               color="red"
 | |
|               outlined
 | |
|               type="button"
 | |
|               phx-click="terminate_app"
 | |
|               phx-target={@myself}
 | |
|             >
 | |
|               Terminate
 | |
|             </.button>
 | |
|           </div>
 | |
|         </div>
 | |
|       <% end %>
 | |
|     </div>
 | |
|     """
 | |
|   end
 | |
| 
 | |
|   defp app_sessions(assigns) do
 | |
|     ~H"""
 | |
|     <div :for={app_session <- @app.sessions} class="w-full border border-gray-200 rounded-lg">
 | |
|       <div class="px-4 py-3 flex gap-3">
 | |
|         <.labeled_text label="Status" class="grow">
 | |
|           <a
 | |
|             class="inline-block"
 | |
|             aria-label="debug app"
 | |
|             href={app_session.app_status == :error && ~p"/sessions/#{app_session.id}"}
 | |
|             target="_blank"
 | |
|           >
 | |
|             <.app_status status={app_session.app_status} />
 | |
|           </a>
 | |
|         </.labeled_text>
 | |
|         <.labeled_text label="Version" class="grow">
 | |
|           v<%= app_session.version %>
 | |
|         </.labeled_text>
 | |
|       </div>
 | |
|       <div class="border-t border-gray-200 px-3 py-1 flex space-x-2">
 | |
|         <div class="grow" />
 | |
|         <span class="tooltip top" data-tooltip="Open">
 | |
|           <.icon_button
 | |
|             disabled={app_session.app_status.lifecycle != :active}
 | |
|             aria-label="open app"
 | |
|             href={~p"/apps/#{@app.slug}/sessions/#{app_session.id}"}
 | |
|           >
 | |
|             <.remix_icon icon="link" />
 | |
|           </.icon_button>
 | |
|         </span>
 | |
|         <span class="tooltip top" data-tooltip="Debug">
 | |
|           <.icon_button aria-label="debug app" href={~p"/sessions/#{app_session.id}"}>
 | |
|             <.remix_icon icon="terminal-line" />
 | |
|           </.icon_button>
 | |
|         </span>
 | |
|         <%= if app_session.app_status.lifecycle == :active do %>
 | |
|           <span class="tooltip top" data-tooltip="Deactivate">
 | |
|             <.icon_button
 | |
|               aria-label="deactivate app session"
 | |
|               phx-click={
 | |
|                 JS.push("deactivate_app_session",
 | |
|                   value: %{session_id: app_session.id},
 | |
|                   target: @myself
 | |
|                 )
 | |
|               }
 | |
|             >
 | |
|               <.remix_icon icon="stop-circle-line" />
 | |
|             </.icon_button>
 | |
|           </span>
 | |
|         <% else %>
 | |
|           <span class="tooltip top" data-tooltip="Terminate">
 | |
|             <.icon_button
 | |
|               aria-label="terminate app session"
 | |
|               phx-click={
 | |
|                 JS.push("terminate_app_session",
 | |
|                   value: %{session_id: app_session.id},
 | |
|                   target: @myself
 | |
|                 )
 | |
|               }
 | |
|             >
 | |
|               <.remix_icon icon="delete-bin-6-line" />
 | |
|             </.icon_button>
 | |
|           </span>
 | |
|         <% end %>
 | |
|       </div>
 | |
|     </div>
 | |
|     """
 | |
|   end
 | |
| 
 | |
|   defp app_info_icon(assigns) do
 | |
|     ~H"""
 | |
|     <span
 | |
|       class="tooltip bottom-left"
 | |
|       data-tooltip={
 | |
|         ~S'''
 | |
|         App deployment is a way to share your
 | |
|         notebook for people to interact with. Use
 | |
|         inputs and controls to build interactive
 | |
|         UIs, perfect for demos and tasks.
 | |
|         '''
 | |
|       }
 | |
|     >
 | |
|       <.icon_button>
 | |
|         <.remix_icon icon="question-line" />
 | |
|       </.icon_button>
 | |
|     </span>
 | |
|     """
 | |
|   end
 | |
| 
 | |
|   @impl true
 | |
|   def handle_event("terminate_app", %{}, socket) do
 | |
|     {:noreply, confirm_app_termination(socket, socket.assigns.app.pid, "preview")}
 | |
|   end
 | |
| 
 | |
|   def handle_event("terminate_app_session", %{"session_id" => session_id}, socket) do
 | |
|     app_session = Enum.find(socket.assigns.app.sessions, &(&1.id == session_id))
 | |
|     Livebook.Session.close(app_session.pid)
 | |
|     {:noreply, socket}
 | |
|   end
 | |
| 
 | |
|   def handle_event("deactivate_app_session", %{"session_id" => session_id}, socket) do
 | |
|     app_session = Enum.find(socket.assigns.app.sessions, &(&1.id == session_id))
 | |
|     Livebook.Session.app_deactivate(app_session.pid)
 | |
|     {:noreply, socket}
 | |
|   end
 | |
| end
 |