mirror of
https://github.com/henrygd/beszel.git
synced 2025-12-16 18:26:16 +01:00
add services column to all systems table
This commit is contained in:
@@ -161,8 +161,15 @@ func (a *Agent) gatherStats(cacheTimeMs uint16) *system.CombinedData {
|
||||
}
|
||||
|
||||
// skip updating systemd services if cache time is not the default 60sec interval
|
||||
if a.systemdManager != nil && cacheTimeMs == 60_000 && a.systemdManager.hasFreshStats {
|
||||
data.SystemdServices = a.systemdManager.getServiceStats(nil, false)
|
||||
if a.systemdManager != nil && cacheTimeMs == 60_000 {
|
||||
totalCount := uint16(a.systemdManager.getServiceStatsCount())
|
||||
if totalCount > 0 {
|
||||
numFailed := a.systemdManager.getFailedServiceCount()
|
||||
data.Info.Services = []uint16{totalCount, numFailed}
|
||||
}
|
||||
if a.systemdManager.hasFreshStats {
|
||||
data.SystemdServices = a.systemdManager.getServiceStats(nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
data.Stats.ExtraFs = make(map[string]*system.FsStats)
|
||||
|
||||
@@ -62,6 +62,24 @@ func (sm *systemdManager) startWorker(conn *dbus.Conn) {
|
||||
}()
|
||||
}
|
||||
|
||||
// getServiceStatsCount returns the number of systemd services.
|
||||
func (sm *systemdManager) getServiceStatsCount() int {
|
||||
return len(sm.serviceStatsMap)
|
||||
}
|
||||
|
||||
// getFailedServiceCount returns the number of systemd services in a failed state.
|
||||
func (sm *systemdManager) getFailedServiceCount() uint16 {
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
count := uint16(0)
|
||||
for _, service := range sm.serviceStatsMap {
|
||||
if service.State == systemd.StatusFailed {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// getServiceStats collects statistics for all running systemd services.
|
||||
func (sm *systemdManager) getServiceStats(conn *dbus.Conn, refresh bool) []*systemd.Service {
|
||||
// start := time.Now()
|
||||
|
||||
@@ -147,6 +147,7 @@ type Info struct {
|
||||
LoadAvg [3]float64 `json:"la,omitempty" cbor:"19,keyasint"`
|
||||
ConnectionType ConnectionType `json:"ct,omitempty" cbor:"20,keyasint,omitempty,omitzero"`
|
||||
ExtraFsPct map[string]float64 `json:"efs,omitempty" cbor:"21,keyasint,omitempty"`
|
||||
Services []uint16 `json:"sv,omitempty" cbor:"22,keyasint,omitempty"` // [totalServices, numFailedServices]
|
||||
}
|
||||
|
||||
// Final data structure to return to the hub
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
PenBoxIcon,
|
||||
PlayCircleIcon,
|
||||
ServerIcon,
|
||||
TerminalSquareIcon,
|
||||
Trash2Icon,
|
||||
WifiIcon,
|
||||
} from "lucide-react"
|
||||
@@ -57,6 +58,7 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu"
|
||||
import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon, WebSocketIcon } from "../ui/icons"
|
||||
import { Separator } from "../ui/separator"
|
||||
|
||||
const STATUS_COLORS = {
|
||||
[SystemStatus.Up]: "bg-green-500",
|
||||
@@ -69,7 +71,7 @@ const STATUS_COLORS = {
|
||||
* @param viewMode - "table" or "grid"
|
||||
* @returns - Column definitions for the systems table
|
||||
*/
|
||||
export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<SystemRecord>[] {
|
||||
export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<SystemRecord>[] {
|
||||
return [
|
||||
{
|
||||
// size: 200,
|
||||
@@ -134,7 +136,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.cpu,
|
||||
accessorFn: ({ info }) => info.cpu || undefined,
|
||||
id: "cpu",
|
||||
name: () => t`CPU`,
|
||||
cell: TableCellWithMeter,
|
||||
@@ -143,7 +145,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
},
|
||||
{
|
||||
// accessorKey: "info.mp",
|
||||
accessorFn: ({ info }) => info.mp,
|
||||
accessorFn: ({ info }) => info.mp || undefined,
|
||||
id: "memory",
|
||||
name: () => t`Memory`,
|
||||
cell: TableCellWithMeter,
|
||||
@@ -151,7 +153,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.dp,
|
||||
accessorFn: ({ info }) => info.dp || undefined,
|
||||
id: "disk",
|
||||
name: () => t`Disk`,
|
||||
cell: DiskCellWithMultiple,
|
||||
@@ -159,7 +161,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.g,
|
||||
accessorFn: ({ info }) => info.g || undefined,
|
||||
id: "gpu",
|
||||
name: () => "GPU",
|
||||
cell: TableCellWithMeter,
|
||||
@@ -172,9 +174,9 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
const sum = info.la?.reduce((acc, curr) => acc + curr, 0)
|
||||
// TODO: remove this in future release in favor of la array
|
||||
if (!sum) {
|
||||
return (info.l1 ?? 0) + (info.l5 ?? 0) + (info.l15 ?? 0)
|
||||
return (info.l1 ?? 0) + (info.l5 ?? 0) + (info.l15 ?? 0) || undefined
|
||||
}
|
||||
return sum
|
||||
return sum || undefined
|
||||
},
|
||||
name: () => t({ message: "Load Avg", comment: "Short label for load average" }),
|
||||
size: 0,
|
||||
@@ -217,7 +219,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.bb || (info.b || 0) * 1024 * 1024,
|
||||
accessorFn: ({ info }) => (info.bb || (info.b || 0) * 1024 * 1024) || undefined,
|
||||
id: "net",
|
||||
name: () => t`Net`,
|
||||
size: 0,
|
||||
@@ -259,11 +261,46 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.sv?.[0],
|
||||
id: "services",
|
||||
name: () => t`Services`,
|
||||
size: 50,
|
||||
Icon: TerminalSquareIcon,
|
||||
header: sortableHeader,
|
||||
hideSort: true,
|
||||
sortingFn: (a, b) => {
|
||||
// sort priorities: 1) failed services, 2) total services
|
||||
const [totalCountA, numFailedA] = a.original.info.sv ?? [0, 0]
|
||||
const [totalCountB, numFailedB] = b.original.info.sv ?? [0, 0]
|
||||
if (numFailedA !== numFailedB) {
|
||||
return numFailedA - numFailedB
|
||||
}
|
||||
return totalCountA - totalCountB
|
||||
},
|
||||
cell(info) {
|
||||
const sys = info.row.original
|
||||
const [totalCount, numFailed] = sys.info.sv ?? [0, 0]
|
||||
if (sys.status !== SystemStatus.Up || totalCount === 0) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<span className="tabular-nums whitespace-nowrap flex gap-1.5 items-center">
|
||||
<span
|
||||
className={cn("block size-2 rounded-full", {
|
||||
[STATUS_COLORS[SystemStatus.Down]]: numFailed > 0,
|
||||
[STATUS_COLORS[SystemStatus.Up]]: numFailed === 0,
|
||||
})}
|
||||
/>
|
||||
{totalCount} <span className="text-muted-foreground text-sm -ms-0.5">({t`Failed`.toLowerCase()}: {numFailed})</span>
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.v,
|
||||
id: "agent",
|
||||
name: () => t`Agent`,
|
||||
// invertSorting: true,
|
||||
size: 50,
|
||||
Icon: WifiIcon,
|
||||
hideSort: true,
|
||||
|
||||
@@ -47,7 +47,7 @@ import type { SystemRecord } from "@/types"
|
||||
import AlertButton from "../alerts/alert-button"
|
||||
import { $router, Link } from "../router"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||
import SystemsTableColumns, { ActionsButton, IndicatorDot } from "./systems-table-columns"
|
||||
import { SystemsTableColumns, ActionsButton, IndicatorDot } from "./systems-table-columns"
|
||||
|
||||
type ViewMode = "table" | "grid"
|
||||
type StatusFilter = "all" | SystemRecord["status"]
|
||||
|
||||
2
internal/site/src/types.d.ts
vendored
2
internal/site/src/types.d.ts
vendored
@@ -79,6 +79,8 @@ export interface SystemInfo {
|
||||
ct?: ConnectionType
|
||||
/** extra filesystem percentages */
|
||||
efs?: Record<string, number>
|
||||
/** services [totalServices, numFailedServices] */
|
||||
sv?: [number, number]
|
||||
}
|
||||
|
||||
export interface SystemStats {
|
||||
|
||||
Reference in New Issue
Block a user