From 3042bbdac69f9cf7b80688973c788ac3b84be7cf Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Wed, 18 Oct 2023 10:23:40 +0200 Subject: [PATCH] Show VM screenshot --- virtweb_frontend/src/api/ApiClient.ts | 3 +- virtweb_frontend/src/api/VMApi.ts | 12 ++++++ virtweb_frontend/src/routes/VMListRoute.tsx | 4 ++ virtweb_frontend/src/routes/VMRoute.tsx | 6 ++- .../src/widgets/vms/VMDetails.tsx | 10 +++++ .../src/widgets/vms/VMScreenshot.tsx | 42 +++++++++++++++++++ .../src/widgets/vms/VMStatusWidget.tsx | 9 +++- 7 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 virtweb_frontend/src/widgets/vms/VMScreenshot.tsx diff --git a/virtweb_frontend/src/api/ApiClient.ts b/virtweb_frontend/src/api/ApiClient.ts index 613040e..50acea7 100644 --- a/virtweb_frontend/src/api/ApiClient.ts +++ b/virtweb_frontend/src/api/ApiClient.ts @@ -103,6 +103,7 @@ export class APIClient { }); // Process response + // JSON response if (res.headers.get("content-type") === "application/json") data = await res.json(); // Binary file @@ -146,7 +147,7 @@ export class APIClient { data = await resInt.blob(); } - // Do not track progress + // Do not track progress (binary file) else data = await res.blob(); status = res.status; diff --git a/virtweb_frontend/src/api/VMApi.ts b/virtweb_frontend/src/api/VMApi.ts index 3c501e2..4c2f9b1 100644 --- a/virtweb_frontend/src/api/VMApi.ts +++ b/virtweb_frontend/src/api/VMApi.ts @@ -136,6 +136,18 @@ export class VMApi { ).data.state; } + /** + * Get a screenshot of a VM + */ + static async Screenshot(vm: VMInfo): Promise { + return ( + await APIClient.exec({ + uri: `/vm/${vm.uuid}/screenshot`, + method: "GET", + }) + ).data; + } + /** * Start the VM */ diff --git a/virtweb_frontend/src/routes/VMListRoute.tsx b/virtweb_frontend/src/routes/VMListRoute.tsx index a82452c..c288135 100644 --- a/virtweb_frontend/src/routes/VMListRoute.tsx +++ b/virtweb_frontend/src/routes/VMListRoute.tsx @@ -21,6 +21,7 @@ import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer"; import { VMStatusWidget } from "../widgets/vms/VMStatusWidget"; import { useSnackbar } from "../hooks/providers/SnackbarProvider"; import { useConfirm } from "../hooks/providers/ConfirmDialogProvider"; +import { useNavigate } from "react-router-dom"; export function VMListRoute(): React.ReactElement { const [list, setList] = React.useState(); @@ -66,6 +67,7 @@ function VMListWidget(p: { }): React.ReactElement { const confirm = useConfirm(); const snackbar = useSnackbar(); + const navigate = useNavigate(); const deleteVM = async (v: VMInfo) => { try { @@ -110,8 +112,10 @@ function VMListWidget(p: { {p.list.map((row) => ( navigate(row.ViewURL)} > {row.name} diff --git a/virtweb_frontend/src/routes/VMRoute.tsx b/virtweb_frontend/src/routes/VMRoute.tsx index 4bfb42f..1414d74 100644 --- a/virtweb_frontend/src/routes/VMRoute.tsx +++ b/virtweb_frontend/src/routes/VMRoute.tsx @@ -50,7 +50,11 @@ function VMRouteBody(p: { vm: VMInfo }): React.ReactElement { } > - + ); } diff --git a/virtweb_frontend/src/widgets/vms/VMDetails.tsx b/virtweb_frontend/src/widgets/vms/VMDetails.tsx index 59e6bb4..e1df427 100644 --- a/virtweb_frontend/src/widgets/vms/VMDetails.tsx +++ b/virtweb_frontend/src/widgets/vms/VMDetails.tsx @@ -6,14 +6,24 @@ import { VMInfo } from "../../api/VMApi"; import { CheckboxInput } from "../forms/CheckboxInput"; import { SelectInput } from "../forms/SelectInput"; import { TextInput } from "../forms/TextInput"; +import { VMScreenshot } from "./VMScreenshot"; export function VMDetails(p: { vm: VMInfo; editable: boolean; onChange?: () => void; + screenshot?: boolean; }): React.ReactElement { return ( + { + /* Screenshot section */ p.screenshot && ( + + + + ) + } + {/* Metadata section */} (); + + const int = React.useRef(); + + React.useEffect(() => { + const refresh = async () => { + try { + const screenshot = await VMApi.Screenshot(p.vm); + const u = URL.createObjectURL(screenshot); + setScreenshotURL(u); + } catch (e) { + console.error(e); + snackbar("Failed to get a screenshot of the VM!"); + } + }; + + if (int.current === undefined) { + refresh(); + int.current = setInterval(() => refresh(), 5000000); + } + + return () => { + if (int.current !== undefined) { + clearInterval(int.current); + int.current = undefined; + } + }; + }, [p.vm, snackbar]); + + return ( + VM screenshot + ); +} diff --git a/virtweb_frontend/src/widgets/vms/VMStatusWidget.tsx b/virtweb_frontend/src/widgets/vms/VMStatusWidget.tsx index 118e833..e1f2972 100644 --- a/virtweb_frontend/src/widgets/vms/VMStatusWidget.tsx +++ b/virtweb_frontend/src/widgets/vms/VMStatusWidget.tsx @@ -3,7 +3,12 @@ import PlayArrowIcon from "@mui/icons-material/PlayArrow"; import PowerSettingsNewIcon from "@mui/icons-material/PowerSettingsNew"; import ReplayIcon from "@mui/icons-material/Replay"; import StopIcon from "@mui/icons-material/Stop"; -import { CircularProgress, IconButton, Tooltip } from "@mui/material"; +import { + CircularProgress, + IconButton, + Tooltip, + Typography, +} from "@mui/material"; import React from "react"; import { VMApi, VMInfo, VMState } from "../../api/VMApi"; import { useAlert } from "../../hooks/providers/AlertDialogProvider"; @@ -47,7 +52,7 @@ export function VMStatusWidget(p: { return (
- {state} + {state} {/* Start VM */}