From ce641a8974e3d4c795ce316c3d86d8c1954f24bd Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Tue, 21 Oct 2025 15:51:18 -0700 Subject: [PATCH] terminal page --- bin/cli/src/command/ssh.rs | 8 +- bin/core/src/periphery/terminal.rs | 96 ++++----- bin/core/src/ws/mod.rs | 120 +++++------ bin/core/src/ws/terminal.rs | 15 +- bin/periphery/src/api/terminal.rs | 5 +- bin/periphery/src/terminal.rs | 8 +- client/core/rs/src/entities/server.rs | 8 +- client/core/ts/src/types.ts | 8 +- client/periphery/rs/src/api/terminal.rs | 4 +- frontend/public/client/types.d.ts | 8 +- frontend/src/components/layouts.tsx | 57 +++-- .../src/components/resources/build/index.tsx | 3 + frontend/src/components/resources/common.tsx | 79 +++++-- .../components/resources/deployment/index.tsx | 1 + .../src/components/resources/repo/index.tsx | 12 +- .../src/components/resources/stack/index.tsx | 8 +- frontend/src/components/terminal/server.tsx | 2 +- .../src/pages/server-info/container/index.tsx | 2 +- frontend/src/pages/server-info/image.tsx | 2 +- frontend/src/pages/server-info/network.tsx | 2 +- frontend/src/pages/server-info/volume.tsx | 2 +- frontend/src/pages/terminal.tsx | 75 +++++++ frontend/src/pages/terminals.tsx | 196 +++++++++++++++--- frontend/src/router.tsx | 9 +- frontend/src/types.d.ts | 2 +- 25 files changed, 502 insertions(+), 230 deletions(-) create mode 100644 frontend/src/pages/terminal.tsx diff --git a/bin/cli/src/command/ssh.rs b/bin/cli/src/command/ssh.rs index 64a7cdac6..0b6cf76c3 100644 --- a/bin/cli/src/command/ssh.rs +++ b/bin/cli/src/command/ssh.rs @@ -36,10 +36,10 @@ pub async fn handle( let forward_resize = async { while sigwinch.recv().await.is_some() { - if let Ok(resize_message) = resize_message() { - if write_tx.send(resize_message).await.is_err() { - break; - }; + if let Ok(resize_message) = resize_message() + && write_tx.send(resize_message).await.is_err() + { + break; } } }; diff --git a/bin/core/src/periphery/terminal.rs b/bin/core/src/periphery/terminal.rs index b15ba3a80..28d6370ac 100644 --- a/bin/core/src/periphery/terminal.rs +++ b/bin/core/src/periphery/terminal.rs @@ -15,23 +15,25 @@ use periphery_client::{ }, transport::EncodedTransportMessage, }; -use transport::channel::{Receiver, Sender, channel}; +use transport::channel::{Receiver, Sender}; use uuid::Uuid; use crate::{ periphery::PeripheryClient, state::periphery_connections, }; +pub struct ConnectTerminalResponse { + pub channel: Uuid, + pub sender: Sender, + pub receiver: Receiver>>, +} + impl PeripheryClient { #[instrument("ConnectTerminal", skip(self), fields(server_id = self.id))] pub async fn connect_terminal( &self, terminal: String, - ) -> anyhow::Result<( - Uuid, - Sender, - Receiver>>, - )> { + ) -> anyhow::Result { tracing::trace!( "request | type: ConnectTerminal | terminal name: {terminal}", ); @@ -41,23 +43,27 @@ impl PeripheryClient { || format!("No connection found for server {}", self.id), )?; - let channel_id = self + let channel = self .request(ConnectTerminal { terminal }) .await .context("Failed to create terminal connection")?; - let (sender, receiever) = channel(); - connection.terminals.insert(channel_id, sender).await; + let (sender, receiver) = transport::channel::channel(); + connection.terminals.insert(channel, sender).await; connection .sender - .send_terminal(channel_id, Ok(Vec::with_capacity(17))) // 16 bytes uuid + 1 EncodedResponse + .send_terminal(channel, Ok(Vec::with_capacity(17))) // 16 bytes uuid + 1 EncodedResponse .await .context( "Failed to send TerminalTrigger to begin forwarding.", )?; - Ok((channel_id, connection.sender.clone(), receiever)) + Ok(ConnectTerminalResponse { + channel, + sender: connection.sender.clone(), + receiver, + }) } #[instrument("ConnectContainerExec", skip(self), fields(server_id = self.id))] @@ -66,11 +72,7 @@ impl PeripheryClient { container: String, shell: String, recreate: TerminalRecreateMode, - ) -> anyhow::Result<( - Uuid, - Sender, - Receiver>>, - )> { + ) -> anyhow::Result { tracing::trace!( "request | type: ConnectContainerExec | container name: {container} | shell: {shell}", ); @@ -80,7 +82,7 @@ impl PeripheryClient { || format!("No connection found for server {}", self.id), )?; - let channel_id = self + let channel = self .request(ConnectContainerExec { container, shell, @@ -89,18 +91,22 @@ impl PeripheryClient { .await .context("Failed to create container exec connection")?; - let (sender, receiever) = channel(); - connection.terminals.insert(channel_id, sender).await; + let (sender, receiver) = transport::channel::channel(); + connection.terminals.insert(channel, sender).await; connection .sender - .send_terminal(channel_id, Ok(Vec::with_capacity(17))) + .send_terminal(channel, Ok(Vec::with_capacity(17))) .await .context( "Failed to send TerminalTrigger to begin forwarding.", )?; - Ok((channel_id, connection.sender.clone(), receiever)) + Ok(ConnectTerminalResponse { + channel, + sender: connection.sender.clone(), + receiver, + }) } #[instrument("ConnectContainerAttach", skip(self), fields(server_id = self.id))] @@ -108,11 +114,7 @@ impl PeripheryClient { &self, container: String, recreate: TerminalRecreateMode, - ) -> anyhow::Result<( - Uuid, - Sender, - Receiver>>, - )> { + ) -> anyhow::Result { tracing::trace!( "request | type: ConnectContainerAttach | container name: {container}", ); @@ -130,7 +132,7 @@ impl PeripheryClient { .await .context("Failed to create container attach connection")?; - let (sender, receiever) = transport::channel::channel(); + let (sender, receiver) = transport::channel::channel(); connection.terminals.insert(channel, sender).await; connection @@ -141,7 +143,11 @@ impl PeripheryClient { "Failed to send TerminalTrigger to begin forwarding.", )?; - Ok((channel, connection.sender.clone(), receiever)) + Ok(ConnectTerminalResponse { + channel, + sender: connection.sender.clone(), + receiver, + }) } /// Executes command on specified terminal, @@ -174,27 +180,25 @@ impl PeripheryClient { || format!("No connection found for server {}", self.id), )?; - let channel_id = self + let channel = self .request(ExecuteTerminal { terminal, command }) .await .context("Failed to create execute terminal connection")?; - let (terminal_sender, terminal_receiver) = channel(); - connection - .terminals - .insert(channel_id, terminal_sender) - .await; + let (terminal_sender, terminal_receiver) = + transport::channel::channel(); + connection.terminals.insert(channel, terminal_sender).await; connection .sender - .send_terminal(channel_id, Ok(Vec::with_capacity(17))) + .send_terminal(channel, Ok(Vec::with_capacity(17))) .await .context( "Failed to send TerminalTrigger to begin forwarding.", )?; Ok(ReceiverStream { - channel_id, + channel, receiver: terminal_receiver, channels: connection.terminals.clone(), }) @@ -230,7 +234,7 @@ impl PeripheryClient { || format!("No connection found for server {}", self.id), )?; - let channel_id = self + let channel = self .request(ExecuteContainerExec { container, shell, @@ -240,21 +244,19 @@ impl PeripheryClient { .await .context("Failed to create execute terminal connection")?; - let (terminal_sender, terminal_receiver) = channel(); - connection - .terminals - .insert(channel_id, terminal_sender) - .await; + let (terminal_sender, terminal_receiver) = + transport::channel::channel(); + connection.terminals.insert(channel, terminal_sender).await; // Trigger forwarding to begin now that forwarding channel is ready. // This is required to not miss messages. connection .sender - .send_terminal(channel_id, Ok(Vec::with_capacity(17))) + .send_terminal(channel, Ok(Vec::with_capacity(17))) .await?; Ok(ReceiverStream { - channel_id, + channel, receiver: terminal_receiver, channels: connection.terminals.clone(), }) @@ -262,7 +264,7 @@ impl PeripheryClient { } pub struct ReceiverStream { - channel_id: Uuid, + channel: Uuid, channels: Arc>>>>, receiver: Receiver>>, } @@ -295,9 +297,9 @@ impl ReceiverStream { fn cleanup(&self) { // Not the prettiest but it should be fine let channels = self.channels.clone(); - let id = self.channel_id; + let channel = self.channel; tokio::spawn(async move { - channels.remove(&id).await; + channels.remove(&channel).await; }); } } diff --git a/bin/core/src/ws/mod.rs b/bin/core/src/ws/mod.rs index a99d94fa2..da9c356df 100644 --- a/bin/core/src/ws/mod.rs +++ b/bin/core/src/ws/mod.rs @@ -1,7 +1,7 @@ use crate::{ auth::{auth_api_key_check_enabled, auth_jwt_check_enabled}, helpers::query::get_user, - periphery::PeripheryClient, + periphery::{PeripheryClient, terminal::ConnectTerminalResponse}, state::periphery_connections, }; use anyhow::anyhow; @@ -17,13 +17,8 @@ use komodo_client::{ entities::{server::Server, user::User}, ws::WsLoginMessage, }; -use periphery_client::{ - api::terminal::DisconnectTerminal, - transport::EncodedTransportMessage, -}; +use periphery_client::api::terminal::DisconnectTerminal; use tokio_util::sync::CancellationToken; -use transport::channel::{Receiver, Sender}; -use uuid::Uuid; mod container; mod deployment; @@ -157,34 +152,26 @@ async fn handle_container_exec_terminal( trace!("connecting to periphery container exec websocket"); - let (periphery_connection_id, periphery_sender, periphery_receiver) = - match periphery - .connect_container_exec(container, shell, recreate) - .await - { - Ok(ws) => ws, - Err(e) => { - debug!( - "Failed connect to periphery container exec websocket | {e:#}" - ); - let _ = client_socket - .send(ws::Message::text(format!("ERROR: {e:#}"))) - .await; - let _ = client_socket.close().await; - return; - } - }; + let response = match periphery + .connect_container_exec(container, shell, recreate) + .await + { + Ok(ws) => ws, + Err(e) => { + debug!( + "Failed connect to periphery container exec websocket | {e:#}" + ); + let _ = client_socket + .send(ws::Message::text(format!("ERROR: {e:#}"))) + .await; + let _ = client_socket.close().await; + return; + } + }; trace!("connected to periphery container exec websocket"); - forward_ws_channel( - periphery, - client_socket, - periphery_connection_id, - periphery_sender, - periphery_receiver, - ) - .await + forward_ws_channel(periphery, client_socket, response).await } async fn handle_container_attach_terminal( @@ -208,42 +195,36 @@ async fn handle_container_attach_terminal( trace!("connecting to periphery container exec websocket"); - let (periphery_connection_id, periphery_sender, periphery_receiver) = - match periphery - .connect_container_attach(container, recreate) - .await - { - Ok(ws) => ws, - Err(e) => { - debug!( - "Failed connect to periphery container attach websocket | {e:#}" - ); - let _ = client_socket - .send(ws::Message::text(format!("ERROR: {e:#}"))) - .await; - let _ = client_socket.close().await; - return; - } - }; + let response = match periphery + .connect_container_attach(container, recreate) + .await + { + Ok(ws) => ws, + Err(e) => { + debug!( + "Failed connect to periphery container attach websocket | {e:#}" + ); + let _ = client_socket + .send(ws::Message::text(format!("ERROR: {e:#}"))) + .await; + let _ = client_socket.close().await; + return; + } + }; trace!("connected to periphery container attach websocket"); - forward_ws_channel( - periphery, - client_socket, - periphery_connection_id, - periphery_sender, - periphery_receiver, - ) - .await + forward_ws_channel(periphery, client_socket, response).await } async fn forward_ws_channel( periphery: PeripheryClient, client_socket: axum::extract::ws::WebSocket, - periphery_connection_id: Uuid, - periphery_sender: Sender, - mut periphery_receiver: Receiver>>, + ConnectTerminalResponse { + channel, + sender: periphery_sender, + receiver: mut periphery_receiver, + }: ConnectTerminalResponse, ) { let (mut client_send, mut client_receive) = client_socket.split(); let cancel = CancellationToken::new(); @@ -264,7 +245,7 @@ async fn forward_ws_channel( match client_recv_res { Some(Ok(ws::Message::Binary(bytes))) => { if let Err(e) = periphery_sender - .send_terminal(periphery_connection_id, Ok(bytes.into())) + .send_terminal(channel, Ok(bytes.into())) .await { debug!("Failed to send terminal message | {e:?}",); @@ -275,7 +256,7 @@ async fn forward_ws_channel( Some(Ok(ws::Message::Text(text))) => { let bytes: Bytes = text.into(); if let Err(e) = periphery_sender - .send_terminal(periphery_connection_id, Ok(bytes.into())) + .send_terminal(channel, Ok(bytes.into())) .await { debug!("Failed to send terminal message | {e:?}",); @@ -286,7 +267,7 @@ async fn forward_ws_channel( Some(Ok(ws::Message::Close(_frame))) => { let _ = periphery_sender .send_terminal( - periphery_connection_id, + channel, Err(anyhow!("Client disconnected")), ) .await; @@ -296,7 +277,7 @@ async fn forward_ws_channel( Some(Err(_e)) => { let _ = periphery_sender .send_terminal( - periphery_connection_id, + channel, Err(anyhow!("Client disconnected")), ) .await; @@ -306,7 +287,7 @@ async fn forward_ws_channel( None => { let _ = periphery_sender .send_terminal( - periphery_connection_id, + channel, Err(anyhow!("Client disconnected")), ) .await; @@ -353,11 +334,8 @@ async fn forward_ws_channel( tokio::join!(core_to_periphery, periphery_to_core); // Cleanup - if let Err(e) = periphery - .request(DisconnectTerminal { - id: periphery_connection_id, - }) - .await + if let Err(e) = + periphery.request(DisconnectTerminal { channel }).await { warn!( "Failed to disconnect Periphery terminal forwarding | {e:#}", @@ -366,6 +344,6 @@ async fn forward_ws_channel( if let Some(connection) = periphery_connections().get(&periphery.id).await { - connection.terminals.remove(&periphery_connection_id).await; + connection.terminals.remove(&channel).await; } } diff --git a/bin/core/src/ws/terminal.rs b/bin/core/src/ws/terminal.rs index 85760f922..650844174 100644 --- a/bin/core/src/ws/terminal.rs +++ b/bin/core/src/ws/terminal.rs @@ -59,11 +59,7 @@ pub async fn handler( trace!("connecting to periphery terminal websocket"); - let ( - periphery_connection_id, - periphery_sender, - periphery_receiver, - ) = match periphery.connect_terminal(terminal).await { + let response = match periphery.connect_terminal(terminal).await { Ok(ws) => ws, Err(e) => { debug!("Failed connect to periphery terminal | {e:#}"); @@ -77,13 +73,6 @@ pub async fn handler( trace!("connected to periphery terminal websocket"); - forward_ws_channel( - periphery, - client_socket, - periphery_connection_id, - periphery_sender, - periphery_receiver, - ) - .await + forward_ws_channel(periphery, client_socket, response).await }) } diff --git a/bin/periphery/src/api/terminal.rs b/bin/periphery/src/api/terminal.rs index 124694be1..ab9881ce9 100644 --- a/bin/periphery/src/api/terminal.rs +++ b/bin/periphery/src/api/terminal.rs @@ -258,14 +258,15 @@ impl Resolve for DisconnectTerminal { fields( id = args.id.to_string(), core = args.core, - channel_id = self.id.to_string(), + channel_id = self.channel.to_string(), ) )] async fn resolve( self, args: &super::Args, ) -> anyhow::Result { - if let Some(channel) = terminal_channels().remove(&self.id).await + if let Some(channel) = + terminal_channels().remove(&self.channel).await { channel.cancel.cancel(); } diff --git a/bin/periphery/src/terminal.rs b/bin/periphery/src/terminal.rs index f2eda2ddf..7ff7c5d22 100644 --- a/bin/periphery/src/terminal.rs +++ b/bin/periphery/src/terminal.rs @@ -5,7 +5,9 @@ use bytes::Bytes; use encoding::{Decode as _, WithChannel}; use komodo_client::{ api::write::TerminalRecreateMode, - entities::{ContainerTerminalMode, server::TerminalInfo}, + entities::{ + ContainerTerminalMode, komodo_timestamp, server::TerminalInfo, + }, }; use periphery_client::transport::EncodedTerminalMessage; use portable_pty::{CommandBuilder, PtySize, native_pty_system}; @@ -138,6 +140,7 @@ pub async fn list_terminals( name: name.to_string(), command: terminal.command.clone(), stored_size_kb: terminal.history.size_kb(), + created_at: terminal.created_at, }) .collect::>(); terminals.sort_by(|a, b| a.name.cmp(&b.name)); @@ -191,6 +194,8 @@ pub type StdoutReceiver = broadcast::Receiver; pub struct Terminal { /// The command that was used as the root command, eg `shell` command: String, + /// Created timestamp milliseconds + created_at: i64, pub cancel: CancellationToken, @@ -374,6 +379,7 @@ impl Terminal { stdout, history, container, + created_at: komodo_timestamp(), }) } diff --git a/client/core/rs/src/entities/server.rs b/client/core/rs/src/entities/server.rs index 11234158a..60850faaa 100644 --- a/client/core/rs/src/entities/server.rs +++ b/client/core/rs/src/entities/server.rs @@ -10,7 +10,7 @@ use crate::{ deserializers::{ option_string_list_deserializer, string_list_deserializer, }, - entities::{MaintenanceWindow, Timelength}, + entities::{MaintenanceWindow, Timelength, I64}, }; use super::{ @@ -375,6 +375,8 @@ pub struct TerminalInfo { pub command: String, /// The size of the terminal history in memory. pub stored_size_kb: f64, + /// When the Terminal was created. + pub created_at: I64, } /// Info about an active terminal on a server. @@ -390,6 +392,8 @@ pub struct TerminalInfoWithServer { pub command: String, /// The size of the terminal history in memory. pub stored_size_kb: f64, + /// When the Terminal was created in unix milliseconds. + pub created_at: I64, } impl TerminalInfoWithServer { @@ -399,6 +403,7 @@ impl TerminalInfoWithServer { name, command, stored_size_kb, + created_at, }: TerminalInfo, ) -> Self { Self { @@ -406,6 +411,7 @@ impl TerminalInfoWithServer { name, command, stored_size_kb, + created_at } } } diff --git a/client/core/ts/src/types.ts b/client/core/ts/src/types.ts index 824863484..f43629f65 100644 --- a/client/core/ts/src/types.ts +++ b/client/core/ts/src/types.ts @@ -3567,6 +3567,8 @@ export interface TerminalInfoWithServer { command: string; /** The size of the terminal history in memory. */ stored_size_kb: number; + /** When the Terminal was created in unix milliseconds. */ + created_at: I64; } export type ListAllTerminalsResponse = TerminalInfoWithServer[]; @@ -4117,6 +4119,8 @@ export interface TerminalInfo { command: string; /** The size of the terminal history in memory. */ stored_size_kb: number; + /** When the Terminal was created. */ + created_at: I64; } export type ListTerminalsResponse = TerminalInfo[]; @@ -5338,9 +5342,9 @@ export interface CreateTerminal { * This can also include args: * `docker exec -it container sh` * - * Default: `bash` + * Default: Configured on each Periphery */ - command: string; + command?: string; /** Default: `Never` */ recreate?: TerminalRecreateMode; } diff --git a/client/periphery/rs/src/api/terminal.rs b/client/periphery/rs/src/api/terminal.rs index b93cfbb1b..95932b957 100644 --- a/client/periphery/rs/src/api/terminal.rs +++ b/client/periphery/rs/src/api/terminal.rs @@ -86,8 +86,8 @@ pub struct ConnectContainerAttach { #[response(NoData)] #[error(anyhow::Error)] pub struct DisconnectTerminal { - /// The connection id of the terminal to disconnect from - pub id: Uuid, + /// The channel id of the terminal to disconnect from + pub channel: Uuid, } // diff --git a/frontend/public/client/types.d.ts b/frontend/public/client/types.d.ts index 04c6b18b2..ebdc386bb 100644 --- a/frontend/public/client/types.d.ts +++ b/frontend/public/client/types.d.ts @@ -3606,6 +3606,8 @@ export interface TerminalInfoWithServer { command: string; /** The size of the terminal history in memory. */ stored_size_kb: number; + /** When the Terminal was created in unix milliseconds. */ + created_at: I64; } export type ListAllTerminalsResponse = TerminalInfoWithServer[]; /** An api key used to authenticate requests via request headers. */ @@ -4087,6 +4089,8 @@ export interface TerminalInfo { command: string; /** The size of the terminal history in memory. */ stored_size_kb: number; + /** When the Terminal was created. */ + created_at: I64; } export type ListTerminalsResponse = TerminalInfo[]; export type ListUserGroupsResponse = UserGroup[]; @@ -5182,9 +5186,9 @@ export interface CreateTerminal { * This can also include args: * `docker exec -it container sh` * - * Default: `bash` + * Default: Configured on each Periphery */ - command: string; + command?: string; /** Default: `Never` */ recreate?: TerminalRecreateMode; } diff --git a/frontend/src/components/layouts.tsx b/frontend/src/components/layouts.tsx index 51808387d..ce5e94b82 100644 --- a/frontend/src/components/layouts.tsx +++ b/frontend/src/components/layouts.tsx @@ -63,6 +63,7 @@ interface PageProps { subtitle?: ReactNode; actions?: ReactNode; superHeader?: ReactNode; + className?: string; } export const Page = ({ @@ -74,33 +75,15 @@ export const Page = ({ subtitle, actions, children, -}: PageProps) => ( -
- {superHeader ? ( -
- {superHeader} - {(title || icon || subtitle || actions) && ( -
-
-
- {icon} -

{title}

- {titleRight} -
-
{subtitle}
-
- {actions} -
- )} -
- ) : ( - (title || icon || subtitle || actions) && ( + className, +}: PageProps) => { + const Header = ( + <> + {(title || icon || subtitle || actions) && (
-
+
{icon}

{title}

@@ -110,12 +93,24 @@ export const Page = ({
{actions}
- ) - )} - {titleOther} - {children} -
-); + )} + + ); + return ( +
+ {superHeader ? ( +
+ {superHeader} + {Header} +
+ ) : ( + Header + )} + {titleOther} + {children} +
+ ); +}; export const PageXlRow = ({ superHeader, @@ -253,7 +248,9 @@ export const NewLayout = ({ Enter {configureLabel} for the new {entityType}. +
{children}
+ + + + + New Terminal + + Choose the Server and Command for the new Terminal. + + + +
+ Server + setRequest((req) => ({ ...req, server }))} + align="end" + /> + Terminal Name + + setRequest((req) => ({ ...req, name: e.target.value })) + } + onKeyDown={(e) => { + if (e.key === "Enter") { + onConfirm(); + } + }} + /> + Command + + setRequest((req) => ({ ...req, command: e.target.value })) + } + onKeyDown={(e) => { + if (e.key === "Enter") { + onConfirm(); + } + }} + /> +
+ + + + +
+ + ); +}; + +const BatchDeleteAllTerminals = ({ refetch }: { refetch: () => void }) => { + const { mutate, isPending } = useWrite("BatchDeleteAllTerminals", { + onSuccess: refetch, + }); + const { tags } = useTags(); + return ( + } + className="w-[160px]" + onClick={() => mutate({ query: { tags } })} + loading={isPending} + /> + ); +}; + const DeleteTerminal = ({ server, terminal, @@ -129,7 +286,10 @@ const DeleteTerminal = ({ terminal: string; refetch: () => void; }) => { - const { mutate } = useWrite("DeleteTerminal", { onSuccess: refetch }); + const { canWrite } = usePermissions({ type: "Server", id: server }); + const { mutate, isPending } = useWrite("DeleteTerminal", { + onSuccess: refetch, + }); return ( } className="w-[120px]" onClick={() => mutate({ server, terminal })} - /> - ); -}; - -const BatchDeleteAllTerminals = ({ refetch }: { refetch: () => void }) => { - const { mutate } = useWrite("BatchDeleteAllTerminals", { - onSuccess: refetch, - }); - const { tags } = useTags(); - return ( - } - className="w-[180px]" - onClick={() => mutate({ query: { tags } })} + disabled={!canWrite} + loading={isPending} /> ); }; diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 8674ffc76..c93798644 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -1,7 +1,5 @@ import { Layout } from "@components/layouts"; import { LOGIN_TOKENS, useAuth, useUser } from "@lib/hooks"; -import TerminalsPage from "@pages/terminals"; -import UpdatePage from "@pages/update"; import { Loader2 } from "lucide-react"; import { lazy, Suspense } from "react"; import { @@ -19,6 +17,7 @@ const Resources = lazy(() => import("@pages/resources")); const Resource = lazy(() => import("@pages/resource")); const Login = lazy(() => import("@pages/login")); const UpdatesPage = lazy(() => import("@pages/updates")); +const UpdatePage = lazy(() => import("@pages/update")); const UserDisabled = lazy(() => import("@pages/user_disabled")); const AlertsPage = lazy(() => import("@pages/alerts")); const UserPage = lazy(() => import("@pages/user")); @@ -30,6 +29,8 @@ const ImagePage = lazy(() => import("@pages/server-info/image")); const VolumePage = lazy(() => import("@pages/server-info/volume")); const ContainerPage = lazy(() => import("@pages/server-info/container")); const ContainersPage = lazy(() => import("@pages/containers")); +const TerminalsPage = lazy(() => import("@pages/terminals")); +const TerminalPage = lazy(() => import("@pages/terminal")); const SchedulesPage = lazy(() => import("@pages/schedules")); const sanitize_query = (search: URLSearchParams) => { @@ -118,6 +119,10 @@ export const Router = () => { } /> } /> } /> + } + /> diff --git a/frontend/src/types.d.ts b/frontend/src/types.d.ts index e6ee58d27..393a4b8cf 100644 --- a/frontend/src/types.d.ts +++ b/frontend/src/types.d.ts @@ -20,7 +20,7 @@ export interface RequiredResourceComponents { Dashboard: React.FC; /** New resource button / dialog */ - New: React.FC<{ server_id?: string; build_id?: string }>; + New: React.FC<{ server_id?: string; builder_id?: string; build_id?: string }>; /** A table component to view resource list */ Table: React.FC<{ resources: Types.ResourceListItem[] }>;