/* eslint-disable react-x/no-array-index-key */ import { mdiHarddisk, mdiInformation, mdiMemory, mdiNetwork, mdiPackageVariantClosed, } from "@mdi/js"; import Icon from "@mdi/react"; import { Box, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow, Typography, } from "@mui/material"; import Grid from "@mui/material/Grid"; import { PieChart } from "@mui/x-charts"; import { filesize } from "filesize"; import humanizeDuration from "humanize-duration"; import React from "react"; import { DiskInfo, NetworkInfo, ServerApi, ServerSystemInfo, } from "../api/ServerApi"; import { AsyncWidget } from "../widgets/AsyncWidget"; import { VirtWebPaper } from "../widgets/VirtWebPaper"; import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer"; export function SysInfoRoute(): React.ReactElement { const [info, setInfo] = React.useState<ServerSystemInfo>(); const load = async () => { setInfo(await ServerApi.SystemInfo()); }; return ( <AsyncWidget load={load} loadKey={1} build={() => <SysInfoRouteInner info={info!} />} errMsg="Failed to load system info" /> ); } export function SysInfoRouteInner(p: { info: ServerSystemInfo; }): React.ReactElement { const sumDiskUsage = p.info.disks.reduce( (prev, disk) => { return { used: prev.used + disk.total_space - disk.available_space, free: prev.free + disk.available_space, }; }, { used: 0, free: 0 } ); return ( <VirtWebRouteContainer label="Sysinfo"> <Grid container spacing={2}> {/* Memory */} <Grid size={{ xs: 4 }}> <Box flexGrow={1}> <Typography style={{ textAlign: "center" }}>Memory</Typography> <PieChart series={[ { data: [ { id: 3, value: p.info.system.free_memory, label: "Free", }, { id: 2, value: p.info.system.available_memory, label: "Available", }, { id: 1, value: p.info.system.used_memory, label: "Used", }, ], }, ]} width={400} height={200} /> </Box> </Grid> {/* Disk usage */} <Grid size={{ xs: 4 }}> <Box flexGrow={1}> <Typography style={{ textAlign: "center" }}>Disk usage</Typography> <PieChart series={[ { data: [ { id: 1, value: sumDiskUsage.free, label: "Free", }, { id: 2, value: sumDiskUsage.used, label: "Used", }, ], }, ]} width={400} height={200} /> </Box> </Grid> {/* CPU usage */} <Grid size={{ xs: 4 }}> <Box flexGrow={1}> <Typography style={{ textAlign: "center" }}>CPU usage</Typography> <PieChart series={[ { data: [ { id: 1, value: 100 - p.info.system.global_cpu_usage, label: "Free", }, { id: 2, value: p.info.system.global_cpu_usage, label: "Used", }, ], }, ]} width={400} height={200} /> </Box> </Grid> </Grid> <SysInfoDetailsTable label="General" icon={<Icon size={"1rem"} path={mdiInformation} />} entries={[ { label: "Load", value: `${p.info.system.load_average.one} ${p.info.system.load_average.five} ${p.info.system.load_average.fifteen}`, }, { label: "Uptime", value: humanizeDuration(p.info.system.uptime * 1000), }, { label: "Bootime", value: new Date(p.info.system.boot_time * 1000).toString(), }, { label: "Hypvervisor type", value: p.info.hypervisor.type, }, ]} /> <SysInfoDetailsTable label="CPU info" icon={<Icon size={"1rem"} path={mdiMemory} />} entries={[ { label: "Brand", value: p.info.system.cpus[0].brand }, { label: "Vendor ID", value: p.info.system.cpus[0].vendor_id, }, { label: "CPU usage", value: p.info.system.cpus[0].cpu_usage, }, { label: "Name", value: p.info.system.cpus[0].name, }, { label: "CPU model", value: p.info.hypervisor.node.cpu_model, }, { label: "CPU frequency (MHz)", value: p.info.hypervisor.node.cpu_frequency_mhz, }, { label: "Number of socket", value: p.info.hypervisor.node.number_of_cpu_socket_per_node, }, { label: "Number of cores per socket", value: p.info.hypervisor.node.number_of_core_per_sockets, }, { label: "Number of threads per core", value: p.info.hypervisor.node.number_of_threads_per_core, }, ]} /> <SysInfoDetailsTable label="OS info" icon={<Icon size={"1rem"} path={mdiPackageVariantClosed} />} entries={[ { label: "Name", value: p.info.system.name }, { label: "Host name", value: p.info.system.host_name }, { label: "Long OS version", value: p.info.system.long_os_version }, { label: "Kernel version", value: p.info.system.kernel_version }, ]} /> <DiskDetailsTable disks={p.info.disks} /> <NetworksDetailsTable networks={p.info.networks} /> </VirtWebRouteContainer> ); } function SysInfoDetailsTable(p: { label: string; icon: React.ReactElement; entries: { label: string; value: string | number }[]; }): React.ReactElement { return ( <VirtWebPaper label={ <> {p.icon} {p.label} </> } > <Table> <TableBody> {p.entries.map((e, c) => ( <TableRow hover key={c}> <TableCell style={{ padding: 5, fontWeight: "bold" }}> {e.label} </TableCell> <TableCell style={{ padding: 5 }}>{e.value}</TableCell> </TableRow> ))} </TableBody> </Table> </VirtWebPaper> ); } function DiskDetailsTable(p: { disks: DiskInfo[] }): React.ReactElement { return ( <VirtWebPaper label={ <> <Icon size={1} path={mdiHarddisk} /> Storage </> } > <Table> <TableHead> <TableRow> <TableCell>Name</TableCell> <TableCell>Kind</TableCell> <TableCell>Mount point</TableCell> <TableCell>Total space</TableCell> <TableCell>Free space</TableCell> <TableCell></TableCell> <TableCell>Removable</TableCell> </TableRow> </TableHead> <TableBody> {p.disks.map((e, c) => ( <TableRow hover key={c}> <TableCell>{e.name}</TableCell> <TableCell>{e.DiskKind}</TableCell> <TableCell>{e.mount_point}</TableCell> <TableCell>{filesize(e.total_space)}</TableCell> <TableCell>{filesize(e.available_space)}</TableCell> <TableCell> <LinearProgress variant="determinate" style={{ minWidth: "100px", width: "100%" }} value={ 100 * ((e.total_space - e.available_space) / e.total_space) } /> </TableCell> <TableCell>{e.is_removable ? "Yes" : "No"}</TableCell> </TableRow> ))} </TableBody> </Table> </VirtWebPaper> ); } function NetworksDetailsTable(p: { networks: NetworkInfo[]; }): React.ReactElement { return ( <VirtWebPaper label={ <> <Icon size={1} path={mdiNetwork} /> Networks </> } > <Table> <TableHead> <TableRow> <TableCell>Name</TableCell> <TableCell>Total received</TableCell> <TableCell>Total transmitted</TableCell> </TableRow> </TableHead> <TableBody> {p.networks.map((e, c) => ( <TableRow hover key={c}> <TableCell>{e[0]}</TableCell> <TableCell>{filesize(e[1].total_received)}</TableCell> <TableCell>{filesize(e[1].total_transmitted)}</TableCell> </TableRow> ))} </TableBody> </Table> </VirtWebPaper> ); }