feat: add server stats

This commit is contained in:
2024-11-13 22:45:03 +00:00
parent 2d4c81e15d
commit 7a00992ff9
12 changed files with 452 additions and 32 deletions
@@ -3,6 +3,8 @@ import Terminal from "./terminal";
import VNCViewer from "./vncviewer";
import { useAuthStore } from "@/stores/auth";
import { AppServer, useServer } from "@/stores/app";
import { useWebsocketUrl } from "@/hooks/useWebsocket";
import ServerStatsBar from "./server-stats-bar";
type SSHSessionProps = {
type: "ssh";
@@ -30,20 +32,25 @@ export type InteractiveSessionProps = {
const InteractiveSession = ({ type, params }: InteractiveSessionProps) => {
const { token } = useAuthStore();
const server = useServer();
const query = new URLSearchParams({ ...params, sid: token || "" });
const url = `${getBaseUrl(server)}/ws/term?${query}`;
const ws = useWebsocketUrl({ ...params, sid: token || "" });
const termUrl = ws("term");
const statsUrl = ws("stats");
switch (type) {
case "ssh":
return <Terminal url={url} />;
return (
<>
<Terminal url={termUrl} />
<ServerStatsBar url={statsUrl} />
</>
);
case "pve":
case "incus":
return params.client === "vnc" ? (
<VNCViewer url={url} />
<VNCViewer url={termUrl} />
) : (
<Terminal url={url} />
<Terminal url={termUrl} />
);
default:
@@ -51,8 +58,4 @@ const InteractiveSession = ({ type, params }: InteractiveSessionProps) => {
}
};
function getBaseUrl(server?: AppServer | null) {
return server?.url.replace("http://", "ws://") || "";
}
export default InteractiveSession;
@@ -0,0 +1,84 @@
import { View, Text, XStack, Separator } from "tamagui";
import React, { useState } from "react";
import { useWebSocket } from "@/hooks/useWebsocket";
import Icons from "../ui/icons";
type Props = {
url: string;
};
const ServerStatsBar = ({ url }: Props) => {
const [cpu, setCPU] = useState(0);
const [memory, setMemory] = useState({ total: 0, used: 0, available: 0 });
const [disk, setDisk] = useState({ total: "0", used: "0", percent: "0%" });
const [network, setNetwork] = useState({ tx: 0, rx: 0 });
const { isConnected } = useWebSocket(url, {
onMessage: (msg) => {
const type = msg.substring(0, 1);
const value = msg.substring(1);
let values: string[];
switch (type) {
case "\x01":
setCPU(parseFloat(value));
break;
case "\x02":
values = value.split(",");
const total = parseInt(values[0]) || 0;
const available = parseInt(values[1]) || 0;
const used = total - available;
setMemory({ total, used, available });
break;
case "\x03":
values = value.split(",");
setDisk({ total: values[0], used: values[1], percent: values[2] });
break;
case "\x04":
values = value.split(",");
setNetwork({
tx: parseInt(values[0]) || 0,
rx: parseInt(values[1]) || 0,
});
break;
}
},
});
if (!isConnected || !memory.total) {
return null;
}
return (
<XStack gap="$1" p="$2" alignItems="center">
<XStack gap="$1" alignItems="center" minWidth={48}>
<Icons name="desktop-tower" size={16} />
<Text fontSize="$2">{Math.round(cpu)}%</Text>
</XStack>
<Separator vertical h="100%" mx="$2" borderColor="$color" />
<Icons name="memory" size={16} />
<Text fontSize="$2">
{memory.used} MB / {memory.total} MB (
{Math.round((memory.used / memory.total) * 100) || 0}%)
</Text>
<Separator vertical h="100%" mx="$2" borderColor="$color" />
<Icons name="harddisk" size={16} />
<Text fontSize="$2">
{disk.used} / {disk.total} ({disk.percent})
</Text>
<Separator vertical h="100%" mx="$2" borderColor="$color" />
<Icons name="download" size={16} />
<Text fontSize="$2">{network.rx} MB</Text>
<Icons name="upload" size={16} />
<Text fontSize="$2">{network.tx} MB</Text>
</XStack>
);
};
export default ServerStatsBar;