From ea665e02dae5a70b8521a38394c0b0f115fed627 Mon Sep 17 00:00:00 2001 From: henrygd Date: Tue, 22 Apr 2025 20:29:17 -0400 Subject: [PATCH] Improve system information retrieval for macOS and Windows - Introduce `Os` enum to represent supported operating systems. - Update `SystemInfo` interface to include OS type. - Refactor `ContainerChart` component to use `ChartType` enum for better clarity. - Switched to dynamic units in container memory chart. --- beszel/internal/agent/system.go | 17 ++++++++- beszel/internal/entities/system/system.go | 9 +++++ .../src/components/charts/container-chart.tsx | 14 +++++--- beszel/site/src/components/routes/system.tsx | 36 +++++++++++-------- beszel/site/src/components/ui/icons.tsx | 21 ++++++++--- beszel/site/src/lib/enums.ts | 13 +++++++ beszel/site/src/types.d.ts | 3 ++ 7 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 beszel/site/src/lib/enums.ts diff --git a/beszel/internal/agent/system.go b/beszel/internal/agent/system.go index 7ce4fee..a1a129f 100644 --- a/beszel/internal/agent/system.go +++ b/beszel/internal/agent/system.go @@ -22,7 +22,22 @@ import ( func (a *Agent) initializeSystemInfo() { a.systemInfo.AgentVersion = beszel.Version a.systemInfo.Hostname, _ = os.Hostname() - a.systemInfo.KernelVersion, _ = host.KernelVersion() + + platform, _, version, _ := host.PlatformInformation() + + if platform == "darwin" { + a.systemInfo.KernelVersion = version + a.systemInfo.Os = system.Darwin + } else if strings.Contains(platform, "indows") { + a.systemInfo.KernelVersion = strings.Replace(platform, "Microsoft ", "", 1) + " " + version + a.systemInfo.Os = system.Windows + } else { + a.systemInfo.Os = system.Linux + } + + if a.systemInfo.KernelVersion == "" { + a.systemInfo.KernelVersion, _ = host.KernelVersion() + } // cpu model if info, err := cpu.Info(); err == nil && len(info) > 0 { diff --git a/beszel/internal/entities/system/system.go b/beszel/internal/entities/system/system.go index 316377d..e298911 100644 --- a/beszel/internal/entities/system/system.go +++ b/beszel/internal/entities/system/system.go @@ -64,6 +64,14 @@ type NetIoStats struct { Name string } +type Os uint8 + +const ( + Linux Os = iota + Darwin + Windows +) + type Info struct { Hostname string `json:"h"` KernelVersion string `json:"k,omitempty"` @@ -79,6 +87,7 @@ type Info struct { Podman bool `json:"p,omitempty"` GpuPct float64 `json:"g,omitempty"` DashboardTemp float64 `json:"dt,omitempty"` + Os Os `json:"os"` } // Final data structure to return to the hub diff --git a/beszel/site/src/components/charts/container-chart.tsx b/beszel/site/src/components/charts/container-chart.tsx index 32727da..b4661ee 100644 --- a/beszel/site/src/components/charts/container-chart.tsx +++ b/beszel/site/src/components/charts/container-chart.tsx @@ -16,16 +16,17 @@ import { useStore } from "@nanostores/react" import { $containerFilter } from "@/lib/stores" import { ChartData } from "@/types" import { Separator } from "../ui/separator" +import { ChartType } from "@/lib/enums" export default memo(function ContainerChart({ dataKey, chartData, - chartName, + chartType, unit = "%", }: { dataKey: string chartData: ChartData - chartName: string + chartType: ChartType unit?: string }) { const filter = useStore($containerFilter) @@ -33,7 +34,7 @@ export default memo(function ContainerChart({ const { containerData } = chartData - const isNetChart = chartName === "net" + const isNetChart = chartType === ChartType.Network const chartConfig = useMemo(() => { let config = {} as Record< @@ -81,7 +82,7 @@ export default memo(function ContainerChart({ tickFormatter: (value: any) => string } // tick formatter - if (chartName === "cpu") { + if (chartType === ChartType.CPU) { obj.tickFormatter = (value) => { const val = toFixedWithoutTrailingZeros(value, 2) + unit return updateYAxisWidth(val) @@ -111,6 +112,11 @@ export default memo(function ContainerChart({ return null } } + } else if (chartType === ChartType.Memory) { + obj.toolTipFormatter = (item: any) => { + const { v, u } = getSizeAndUnit(item.value, false) + return updateYAxisWidth(toFixedFloat(v, 2) + u) + } } else { obj.toolTipFormatter = (item: any) => decimalString(item.value) + unit } diff --git a/beszel/site/src/components/routes/system.tsx b/beszel/site/src/components/routes/system.tsx index f6a27c0..d033bb3 100644 --- a/beszel/site/src/components/routes/system.tsx +++ b/beszel/site/src/components/routes/system.tsx @@ -2,6 +2,7 @@ import { t } from "@lingui/core/macro" import { Plural, Trans } from "@lingui/react/macro" import { $systems, pb, $chartTime, $containerFilter, $userSettings, $direction, $maxValues } from "@/lib/stores" import { ChartData, ChartTimes, ContainerStatsRecord, GPUData, SystemRecord, SystemStatsRecord } from "@/types" +import { ChartType, Os } from "@/lib/enums" import React, { lazy, memo, useCallback, useEffect, useMemo, useRef, useState } from "react" import { Card, CardHeader, CardTitle, CardDescription } from "../ui/card" import { useStore } from "@nanostores/react" @@ -22,7 +23,7 @@ import { Separator } from "../ui/separator" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip" import { Button } from "../ui/button" import { Input } from "../ui/input" -import { ChartAverage, ChartMax, Rows, TuxIcon, WindowsIcon } from "../ui/icons" +import { ChartAverage, ChartMax, Rows, TuxIcon, WindowsIcon, AppleIcon } from "../ui/icons" import { useIntersectionObserver } from "@/lib/use-intersection-observer" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select" import { timeTicks } from "d3-time" @@ -251,12 +252,23 @@ export default function SystemDetail({ name }: { name: string }) { if (!system.info) { return [] } - let version = system.info.k ?? "" - const buildIndex = version.indexOf(" Build") - const isWindows = buildIndex !== -1 - if (isWindows) { - version = version.substring(0, buildIndex) + + const osInfo = { + [Os.Linux]: { + Icon: TuxIcon, + value: system.info.k, + label: t({ comment: "Linux kernel", message: "Kernel" }), + }, + [Os.Darwin]: { + Icon: AppleIcon, + value: `macOS ${system.info.k}`, + }, + [Os.Windows]: { + Icon: WindowsIcon, + value: system.info.k, + }, } + let uptime: React.ReactNode if (system.info.u < 172800) { const hours = Math.trunc(system.info.u / 3600) @@ -274,11 +286,7 @@ export default function SystemDetail({ name }: { name: string }) { hide: system.info.h === system.host || system.info.h === system.name, }, { value: uptime, Icon: ClockArrowUp, label: t`Uptime`, hide: !system.info.u }, - { - value: version, - Icon: isWindows ? WindowsIcon : TuxIcon, - label: isWindows ? t`Windows build` : t({ comment: "Linux kernel", message: "Kernel" }), - }, + osInfo[system.info.os ?? Os.Linux], { value: `${system.info.m} (${system.info.c}c${system.info.t ? `/${system.info.t}t` : ""})`, Icon: CpuIcon, @@ -456,7 +464,7 @@ export default function SystemDetail({ name }: { name: string }) { description={t`Average CPU utilization of containers`} cornerEl={containerFilterBar} > - + )} @@ -477,7 +485,7 @@ export default function SystemDetail({ name }: { name: string }) { description={dockerOrPodman(t`Memory usage of docker containers`, system)} cornerEl={containerFilterBar} > - + )} @@ -519,7 +527,7 @@ export default function SystemDetail({ name }: { name: string }) { cornerEl={containerFilterBar} > {/* @ts-ignore */} - + )} diff --git a/beszel/site/src/components/ui/icons.tsx b/beszel/site/src/components/ui/icons.tsx index 8fe4060..6dd6d6d 100644 --- a/beszel/site/src/components/ui/icons.tsx +++ b/beszel/site/src/components/ui/icons.tsx @@ -12,16 +12,27 @@ export function TuxIcon(props: SVGProps) { ) } -// meteor icons (MIT) https://github.com/zkreations/icons/blob/main/LICENSE +// icon park (Apache 2.0) https://github.com/bytedance/IconPark/blob/master/LICENSE export function WindowsIcon(props: SVGProps) { return ( - + + + ) +} + +// teenyicons (MIT) https://github.com/teenyicons/teenyicons/blob/master/LICENSE +export function AppleIcon(props: SVGProps) { + return ( + + ) diff --git a/beszel/site/src/lib/enums.ts b/beszel/site/src/lib/enums.ts new file mode 100644 index 0000000..8c42574 --- /dev/null +++ b/beszel/site/src/lib/enums.ts @@ -0,0 +1,13 @@ +export enum Os { + Linux = 0, + Darwin, + Windows, + // FreeBSD, +} + +export enum ChartType { + Memory, + Disk, + Network, + CPU, +} diff --git a/beszel/site/src/types.d.ts b/beszel/site/src/types.d.ts index 10d4b5b..d6095e4 100644 --- a/beszel/site/src/types.d.ts +++ b/beszel/site/src/types.d.ts @@ -1,4 +1,5 @@ import { RecordModel } from "pocketbase" +import { Os } from "./lib/enums" // global window properties declare global { @@ -48,6 +49,8 @@ export interface SystemInfo { g?: number /** dashboard display temperature */ dt?: number + /** operating system */ + os?: Os } export interface SystemStats {