From 2223a156dd053138b7cd43945575814414657efb Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Mon, 26 May 2025 17:52:03 +0000 Subject: [PATCH 1/7] wip --- site/src/api/queries/deployment.ts | 2 + site/src/modules/resources/AgentRow.tsx | 73 +++++++------------ site/src/modules/resources/AgentVersion.tsx | 19 ++--- .../modules/resources/SSHButton/SSHButton.tsx | 6 +- site/src/pages/WorkspacePage/Workspace.tsx | 13 ---- .../WorkspacePage/WorkspaceReadyPage.tsx | 10 --- 6 files changed, 43 insertions(+), 80 deletions(-) diff --git a/site/src/api/queries/deployment.ts b/site/src/api/queries/deployment.ts index 4b65b20da82cc..4d1610bd57b46 100644 --- a/site/src/api/queries/deployment.ts +++ b/site/src/api/queries/deployment.ts @@ -1,4 +1,5 @@ import { API } from "api/api"; +import { disabledRefetchOptions } from "./util"; export const deploymentConfigQueryKey = ["deployment", "config"]; @@ -26,6 +27,7 @@ export const deploymentStats = () => { export const deploymentSSHConfig = () => { return { + ...disabledRefetchOptions, queryKey: ["deployment", "sshConfig"], queryFn: API.getDeploymentSSHConfig, }; diff --git a/site/src/modules/resources/AgentRow.tsx b/site/src/modules/resources/AgentRow.tsx index f97c91e89af2a..75df89e01328b 100644 --- a/site/src/modules/resources/AgentRow.tsx +++ b/site/src/modules/resources/AgentRow.tsx @@ -42,47 +42,36 @@ import { AgentSSHButton } from "./SSHButton/SSHButton"; import { TerminalLink } from "./TerminalLink/TerminalLink"; import { VSCodeDesktopButton } from "./VSCodeDesktopButton/VSCodeDesktopButton"; import { useAgentLogs } from "./useAgentLogs"; +import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; +import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; +import { buildInfo } from "api/queries/buildInfo"; export interface AgentRowProps { agent: WorkspaceAgent; workspace: Workspace; - showApps: boolean; - showBuiltinApps?: boolean; - sshPrefix?: string; - hideSSHButton?: boolean; - hideVSCodeDesktopButton?: boolean; - serverVersion: string; - serverAPIVersion: string; - onUpdateAgent: () => void; template: Template; storybookAgentMetadata?: WorkspaceAgentMetadata[]; + onUpdateAgent: () => void; } export const AgentRow: FC = ({ agent, workspace, template, - showApps, - showBuiltinApps = true, - hideSSHButton, - hideVSCodeDesktopButton, - serverVersion, - serverAPIVersion, onUpdateAgent, storybookAgentMetadata, - sshPrefix, }) => { // Apps visibility + const { browser_only } = useFeatureVisibility(); const visibleApps = agent.apps.filter((app) => !app.hidden); - const hasAppsToDisplay = !hideVSCodeDesktopButton || visibleApps.length > 0; + const hasAppsToDisplay = !browser_only && visibleApps.length > 0; const shouldDisplayApps = - showApps && - ((agent.status === "connected" && hasAppsToDisplay) || - agent.status === "connecting"); + (agent.status === "connected" && hasAppsToDisplay) || + agent.status === "connecting"; const hasVSCodeApp = agent.display_apps.includes("vscode") || agent.display_apps.includes("vscode_insiders"); - const showVSCode = hasVSCodeApp && !hideVSCodeDesktopButton; + const showVSCode = hasVSCodeApp && !browser_only; const hasStartupFeatures = Boolean(agent.logs_length); const { proxy } = useProxy(); @@ -184,12 +173,7 @@ export const AgentRow: FC = ({ {agent.status === "connected" && ( <> - + )} @@ -201,28 +185,25 @@ export const AgentRow: FC = ({ )} - {showBuiltinApps && ( -
- {!hideSSHButton && agent.display_apps.includes("ssh_helper") && ( - + {!browser_only && agent.display_apps.includes("ssh_helper") && ( + + )} + {proxy.preferredWildcardHostname !== "" && + agent.display_apps.includes("port_forwarding_helper") && ( + )} - {proxy.preferredWildcardHostname !== "" && - agent.display_apps.includes("port_forwarding_helper") && ( - - )} -
- )} +
@@ -257,7 +238,7 @@ export const AgentRow: FC = ({ )} - {showBuiltinApps && agent.display_apps.includes("web_terminal") && ( + {agent.display_apps.includes("web_terminal") && ( void; } -export const AgentVersion: FC = ({ - agent, - serverVersion, - serverAPIVersion, - onUpdate, -}) => { +export const AgentVersion: FC = ({ agent, onUpdate }) => { + const { metadata } = useEmbeddedMetadata(); + const { data: build } = useQuery(buildInfo(metadata["build-info"])); + const serverVersion = build?.version ?? ""; + const apiServerVersion = build?.agent_api_version ?? ""; + const { status } = getDisplayVersionStatus( agent.version, serverVersion, agent.api_version, - serverAPIVersion, + apiServerVersion, ); if (status === agentVersionStatus.Updated) { diff --git a/site/src/modules/resources/SSHButton/SSHButton.tsx b/site/src/modules/resources/SSHButton/SSHButton.tsx index 42e2b3828f3ae..841bd6e883582 100644 --- a/site/src/modules/resources/SSHButton/SSHButton.tsx +++ b/site/src/modules/resources/SSHButton/SSHButton.tsx @@ -1,4 +1,5 @@ import type { Interpolation, Theme } from "@emotion/react"; +import { deploymentSSHConfig } from "api/queries/deployment"; import { Button } from "components/Button/Button"; import { CodeExample } from "components/CodeExample/CodeExample"; import { @@ -15,20 +16,21 @@ import { import { type ClassName, useClassName } from "hooks/useClassName"; import { ChevronDownIcon } from "lucide-react"; import type { FC } from "react"; +import { useQuery } from "react-query"; import { docs } from "utils/docs"; export interface AgentSSHButtonProps { workspaceName: string; agentName: string; - sshPrefix?: string; } export const AgentSSHButton: FC = ({ workspaceName, agentName, - sshPrefix, }) => { const paper = useClassName(classNames.paper, []); + const { data } = useQuery(deploymentSSHConfig()); + const sshPrefix = data?.hostname_prefix; return ( diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx index 6016ca8c423eb..feb9cae8ee0e2 100644 --- a/site/src/pages/WorkspacePage/Workspace.tsx +++ b/site/src/pages/WorkspacePage/Workspace.tsx @@ -31,10 +31,7 @@ export interface WorkspaceProps { permissions: WorkspacePermissions; isUpdating: boolean; isRestarting: boolean; - hideSSHButton?: boolean; - hideVSCodeDesktopButton?: boolean; buildInfo?: TypesGen.BuildInfoResponse; - sshPrefix?: string; buildLogs?: TypesGen.ProvisionerJobLog[]; latestVersion?: TypesGen.TemplateVersion; timings?: TypesGen.WorkspaceBuildTimings; @@ -56,10 +53,7 @@ export const Workspace: FC = ({ workspace, isUpdating, isRestarting, - hideSSHButton, - hideVSCodeDesktopButton, buildInfo, - sshPrefix, template, buildLogs, latestVersion, @@ -252,13 +246,6 @@ export const Workspace: FC = ({ agent={agent} workspace={workspace} template={template} - sshPrefix={sshPrefix} - showApps={permissions.updateWorkspace} - showBuiltinApps={permissions.updateWorkspace} - hideSSHButton={hideSSHButton} - hideVSCodeDesktopButton={hideVSCodeDesktopButton} - serverVersion={buildInfo?.version || ""} - serverAPIVersion={buildInfo?.agent_api_version || ""} onUpdateAgent={handleUpdate} // On updating the workspace the agent version is also updated /> ))} diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index 5de4eb6b490f7..abf583de78356 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -43,10 +43,7 @@ export const WorkspaceReadyPage: FC = ({ template, permissions, }) => { - const { metadata } = useEmbeddedMetadata(); - const buildInfoQuery = useQuery(buildInfo(metadata["build-info"])); const queryClient = useQueryClient(); - const featureVisibility = useFeatureVisibility(); // Build logs const shouldStreamBuildLogs = workspace.latest_build.status !== "running"; @@ -65,9 +62,6 @@ export const WorkspaceReadyPage: FC = ({ mutationFn: API.restartWorkspace, }); - // SSH Prefix - const sshPrefixQuery = useQuery(deploymentSSHConfig()); - // Favicon const favicon = getFaviconByStatus(workspace.latest_build); const [faviconTheme, setFaviconTheme] = useState<"light" | "dark">("dark"); @@ -202,10 +196,6 @@ export const WorkspaceReadyPage: FC = ({ isRestarting={isRestarting} workspace={workspace} latestVersion={latestVersion} - hideSSHButton={featureVisibility.browser_only} - hideVSCodeDesktopButton={featureVisibility.browser_only} - buildInfo={buildInfoQuery.data} - sshPrefix={sshPrefixQuery.data?.hostname_prefix} template={template} buildLogs={buildLogs} timings={timingsQuery.data} From 54bf987d1acf6b52e7d8d993e637e134e8c4c8b6 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 28 May 2025 17:08:47 +0000 Subject: [PATCH 2/7] Simplify PortForwardButton --- site/src/modules/resources/AgentMetadata.tsx | 12 +- .../modules/resources/AgentRow.stories.tsx | 6 +- site/src/modules/resources/AgentRow.tsx | 15 +- .../modules/resources/PortForwardButton.tsx | 132 ++++++++++-------- site/src/pages/WorkspacePage/Workspace.tsx | 2 - 5 files changed, 83 insertions(+), 84 deletions(-) diff --git a/site/src/modules/resources/AgentMetadata.tsx b/site/src/modules/resources/AgentMetadata.tsx index f8eba334f09ce..5c6986a4dc618 100644 --- a/site/src/modules/resources/AgentMetadata.tsx +++ b/site/src/modules/resources/AgentMetadata.tsx @@ -42,24 +42,24 @@ export const AgentMetadataView: FC = ({ metadata }) => { interface AgentMetadataProps { agent: WorkspaceAgent; - storybookMetadata?: WorkspaceAgentMetadata[]; + initialMetadata?: WorkspaceAgentMetadata[]; } const maxSocketErrorRetryCount = 3; export const AgentMetadata: FC = ({ agent, - storybookMetadata, + initialMetadata, }) => { - const [activeMetadata, setActiveMetadata] = useState(storybookMetadata); + const [activeMetadata, setActiveMetadata] = useState(initialMetadata); useEffect(() => { // This is an unfortunate pitfall with this component's testing setup, - // but even though we use the value of storybookMetadata as the initial + // but even though we use the value of initialMetadata as the initial // value of the activeMetadata, we cannot put activeMetadata itself into // the dependency array. If we did, we would destroy and rebuild each // connection every single time a new message comes in from the socket, // because the socket has to be wired up to the state setter - if (storybookMetadata !== undefined) { + if (initialMetadata !== undefined) { return; } @@ -118,7 +118,7 @@ export const AgentMetadata: FC = ({ window.clearTimeout(timeoutId); activeSocket?.close(); }; - }, [agent.id, storybookMetadata]); + }, [agent.id, initialMetadata]); if (activeMetadata === undefined) { return ( diff --git a/site/src/modules/resources/AgentRow.stories.tsx b/site/src/modules/resources/AgentRow.stories.tsx index c08d28f295cd9..43cfe99ac973a 100644 --- a/site/src/modules/resources/AgentRow.stories.tsx +++ b/site/src/modules/resources/AgentRow.stories.tsx @@ -96,7 +96,7 @@ const meta: Meta = { }, workspace: M.MockWorkspace, showApps: true, - storybookAgentMetadata: defaultAgentMetadata, + initialMetadata: defaultAgentMetadata, }, decorators: [withProxyProvider(), withDashboardProvider, withWebSocket], parameters: { @@ -162,7 +162,7 @@ export const BunchOfApps: Story = { export const Connecting: Story = { args: { agent: M.MockWorkspaceAgentConnecting, - storybookAgentMetadata: [], + initialMetadata: [], }, }; @@ -190,7 +190,7 @@ export const Started: Story = { export const StartedNoMetadata: Story = { args: { ...Started.args, - storybookAgentMetadata: [], + initialMetadata: [], }, }; diff --git a/site/src/modules/resources/AgentRow.tsx b/site/src/modules/resources/AgentRow.tsx index 7e31c136f0aad..71aba5274d3d6 100644 --- a/site/src/modules/resources/AgentRow.tsx +++ b/site/src/modules/resources/AgentRow.tsx @@ -43,14 +43,12 @@ import { TerminalLink } from "./TerminalLink/TerminalLink"; import { VSCodeDesktopButton } from "./VSCodeDesktopButton/VSCodeDesktopButton"; import { useAgentLogs } from "./useAgentLogs"; import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; -import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; -import { buildInfo } from "api/queries/buildInfo"; export interface AgentRowProps { agent: WorkspaceAgent; workspace: Workspace; template: Template; - storybookAgentMetadata?: WorkspaceAgentMetadata[]; + initialMetadata?: WorkspaceAgentMetadata[]; onUpdateAgent: () => void; } @@ -59,7 +57,7 @@ export const AgentRow: FC = ({ workspace, template, onUpdateAgent, - storybookAgentMetadata, + initialMetadata, }) => { // Apps visibility const { browser_only } = useFeatureVisibility(); @@ -196,10 +194,8 @@ export const AgentRow: FC = ({ agent.display_apps.includes("port_forwarding_helper") && ( )} @@ -281,10 +277,7 @@ export const AgentRow: FC = ({ )} - +
{hasStartupFeatures && ( diff --git a/site/src/modules/resources/PortForwardButton.tsx b/site/src/modules/resources/PortForwardButton.tsx index 916c8f2338130..0e3f0688287d3 100644 --- a/site/src/modules/resources/PortForwardButton.tsx +++ b/site/src/modules/resources/PortForwardButton.tsx @@ -18,8 +18,10 @@ import { import { type Template, type UpsertWorkspaceAgentPortShareRequest, + type Workspace, type WorkspaceAgent, type WorkspaceAgentListeningPort, + type WorkspaceAgentPortShare, type WorkspaceAgentPortShareLevel, type WorkspaceAgentPortShareProtocol, WorkspaceAppSharingLevels, @@ -64,31 +66,41 @@ import * as Yup from "yup"; interface PortForwardButtonProps { host: string; - username: string; - workspaceName: string; - workspaceID: string; + workspace: Workspace; agent: WorkspaceAgent; template: Template; } -export const PortForwardButton: FC = (props) => { - const { agent } = props; +export const PortForwardButton: FC = ({ + host, + workspace, + template, + agent, +}) => { const { entitlements } = useDashboard(); const paper = useClassName(classNames.paper, []); - const portsQuery = useQuery({ + const { data: listeningPorts } = useQuery({ queryKey: ["portForward", agent.id], queryFn: () => API.getAgentListeningPorts(agent.id), enabled: agent.status === "connected", refetchInterval: 5_000, + select: (res) => res.ports, + }); + + const { data: sharedPorts, refetch: refetchSharedPorts } = useQuery({ + ...workspacePortShares(workspace.id), + enabled: agent.status === "connected", + select: (res) => res.shares, + initialData: { shares: [] }, }); return ( -