mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-24 06:26:17 +01:00
Compare commits
2 Commits
5d04ee5a65
...
split-syst
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
330d375997 | ||
|
|
8627e3ee97 |
@@ -6,11 +6,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"log/slog"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/henrygd/beszel/internal/common"
|
"github.com/henrygd/beszel/internal/common"
|
||||||
@@ -42,8 +41,9 @@ type System struct {
|
|||||||
WsConn *ws.WsConn // Handler for agent WebSocket connection
|
WsConn *ws.WsConn // Handler for agent WebSocket connection
|
||||||
agentVersion semver.Version // Agent version
|
agentVersion semver.Version // Agent version
|
||||||
updateTicker *time.Ticker // Ticker for updating the system
|
updateTicker *time.Ticker // Ticker for updating the system
|
||||||
smartOnce sync.Once // Once for fetching and saving smart devices
|
detailsFetched atomic.Bool // True if static system details have been fetched and saved
|
||||||
detailsOnce sync.Once // Once for fetching and saving static system details
|
smartFetched atomic.Bool // True if SMART devices have been fetched and saved
|
||||||
|
smartFetching atomic.Bool // True if SMART devices are currently being fetched
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *SystemManager) NewSystem(systemId string) *System {
|
func (sm *SystemManager) NewSystem(systemId string) *System {
|
||||||
@@ -119,10 +119,10 @@ func (sys *System) update() error {
|
|||||||
options := common.DataRequestOptions{
|
options := common.DataRequestOptions{
|
||||||
CacheTimeMs: uint16(interval),
|
CacheTimeMs: uint16(interval),
|
||||||
}
|
}
|
||||||
// fetch system details only on the first update
|
// fetch system details if not already fetched
|
||||||
sys.detailsOnce.Do(func() {
|
if !sys.detailsFetched.Load() {
|
||||||
options.IncludeDetails = true
|
options.IncludeDetails = true
|
||||||
})
|
}
|
||||||
data, err := sys.fetchDataFromAgent(options)
|
data, err := sys.fetchDataFromAgent(options)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_, err = sys.createRecords(data)
|
_, err = sys.createRecords(data)
|
||||||
@@ -151,18 +151,11 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
|||||||
}
|
}
|
||||||
hub := sys.manager.hub
|
hub := sys.manager.hub
|
||||||
err = hub.RunInTransaction(func(txApp core.App) error {
|
err = hub.RunInTransaction(func(txApp core.App) error {
|
||||||
if data.Details != nil {
|
// add system_stats record
|
||||||
slog.Info("Static info", "data", data.Details)
|
|
||||||
if err := createStaticInfoRecord(txApp, data.Details, sys.Id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add system_stats and container_stats records
|
|
||||||
systemStatsCollection, err := txApp.FindCachedCollectionByNameOrId("system_stats")
|
systemStatsCollection, err := txApp.FindCachedCollectionByNameOrId("system_stats")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
systemStatsRecord := core.NewRecord(systemStatsCollection)
|
systemStatsRecord := core.NewRecord(systemStatsCollection)
|
||||||
systemStatsRecord.Set("system", systemRecord.Id)
|
systemStatsRecord.Set("system", systemRecord.Id)
|
||||||
systemStatsRecord.Set("stats", data.Stats)
|
systemStatsRecord.Set("stats", data.Stats)
|
||||||
@@ -170,14 +163,14 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
|||||||
if err := txApp.SaveNoValidate(systemStatsRecord); err != nil {
|
if err := txApp.SaveNoValidate(systemStatsRecord); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add containers and container_stats records
|
||||||
if len(data.Containers) > 0 {
|
if len(data.Containers) > 0 {
|
||||||
// add / update containers records
|
|
||||||
if data.Containers[0].Id != "" {
|
if data.Containers[0].Id != "" {
|
||||||
if err := createContainerRecords(txApp, data.Containers, sys.Id); err != nil {
|
if err := createContainerRecords(txApp, data.Containers, sys.Id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add new container_stats record
|
|
||||||
containerStatsCollection, err := txApp.FindCachedCollectionByNameOrId("container_stats")
|
containerStatsCollection, err := txApp.FindCachedCollectionByNameOrId("container_stats")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -198,9 +191,16 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add system details record
|
||||||
|
if data.Details != nil {
|
||||||
|
if err := createSystemDetailsRecord(txApp, data.Details, sys.Id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sys.detailsFetched.Store(true)
|
||||||
|
}
|
||||||
|
|
||||||
// update system record (do this last because it triggers alerts and we need above records to be inserted first)
|
// update system record (do this last because it triggers alerts and we need above records to be inserted first)
|
||||||
systemRecord.Set("status", up)
|
systemRecord.Set("status", up)
|
||||||
|
|
||||||
systemRecord.Set("info", data.Info)
|
systemRecord.Set("info", data.Info)
|
||||||
if err := txApp.SaveNoValidate(systemRecord); err != nil {
|
if err := txApp.SaveNoValidate(systemRecord); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -210,36 +210,42 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
|||||||
|
|
||||||
// Fetch and save SMART devices when system first comes online
|
// Fetch and save SMART devices when system first comes online
|
||||||
if err == nil {
|
if err == nil {
|
||||||
sys.smartOnce.Do(func() {
|
if !sys.smartFetched.Load() && sys.smartFetching.CompareAndSwap(false, true) {
|
||||||
go sys.FetchAndSaveSmartDevices()
|
go func() {
|
||||||
})
|
defer sys.smartFetching.Store(false)
|
||||||
|
if err := sys.FetchAndSaveSmartDevices(); err == nil {
|
||||||
|
sys.smartFetched.Store(true)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return systemRecord, err
|
return systemRecord, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func createStaticInfoRecord(app core.App, data *system.Details, systemId string) error {
|
func createSystemDetailsRecord(app core.App, data *system.Details, systemId string) error {
|
||||||
record, err := app.FindRecordById("system_details", systemId)
|
collectionName := "system_details"
|
||||||
if err != nil {
|
params := dbx.Params{
|
||||||
collection, err := app.FindCollectionByNameOrId("system_details")
|
"id": systemId,
|
||||||
if err != nil {
|
"system": systemId,
|
||||||
|
"hostname": data.Hostname,
|
||||||
|
"kernel": data.Kernel,
|
||||||
|
"cores": data.Cores,
|
||||||
|
"threads": data.Threads,
|
||||||
|
"cpu": data.CpuModel,
|
||||||
|
"os": data.Os,
|
||||||
|
"os_name": data.OsName,
|
||||||
|
"arch": data.Arch,
|
||||||
|
"memory": data.MemoryTotal,
|
||||||
|
"podman": data.Podman,
|
||||||
|
"updated": time.Now().UTC(),
|
||||||
|
}
|
||||||
|
result, err := app.DB().Update(collectionName, params, dbx.HashExp{"id": systemId}).Execute()
|
||||||
|
rowsAffected, _ := result.RowsAffected()
|
||||||
|
if err != nil || rowsAffected == 0 {
|
||||||
|
_, err = app.DB().Insert(collectionName, params).Execute()
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
record = core.NewRecord(collection)
|
|
||||||
record.Set("id", systemId)
|
|
||||||
}
|
|
||||||
record.Set("system", systemId)
|
|
||||||
record.Set("hostname", data.Hostname)
|
|
||||||
record.Set("kernel", data.Kernel)
|
|
||||||
record.Set("cores", data.Cores)
|
|
||||||
record.Set("threads", data.Threads)
|
|
||||||
record.Set("cpu", data.CpuModel)
|
|
||||||
record.Set("os", data.Os)
|
|
||||||
record.Set("os_name", data.OsName)
|
|
||||||
record.Set("arch", data.Arch)
|
|
||||||
record.Set("memory", data.MemoryTotal)
|
|
||||||
record.Set("podman", data.Podman)
|
|
||||||
return app.SaveNoValidate(record)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSystemdStatsRecords(app core.App, data []*systemd.Service, systemId string) error {
|
func createSystemdStatsRecords(app core.App, data []*systemd.Service, systemId string) error {
|
||||||
|
|||||||
@@ -33,10 +33,7 @@
|
|||||||
"noUnusedFunctionParameters": "error",
|
"noUnusedFunctionParameters": "error",
|
||||||
"noUnusedPrivateClassMembers": "error",
|
"noUnusedPrivateClassMembers": "error",
|
||||||
"useExhaustiveDependencies": {
|
"useExhaustiveDependencies": {
|
||||||
"level": "warn",
|
"level": "off"
|
||||||
"options": {
|
|
||||||
"reportUnnecessaryDependencies": false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"useUniqueElementIds": "off",
|
"useUniqueElementIds": "off",
|
||||||
"noUnusedVariables": "error"
|
"noUnusedVariables": "error"
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import ChartTimeSelect from "@/components/charts/chart-time-select"
|
import { plural } from "@lingui/core/macro"
|
||||||
import { Button } from "@/components/ui/button"
|
import { useLingui } from "@lingui/react/macro"
|
||||||
import { Card } from "@/components/ui/card"
|
|
||||||
import { FreeBsdIcon, TuxIcon, WebSocketIcon, WindowsIcon } from "@/components/ui/icons"
|
|
||||||
import { SystemStatus, ConnectionType, connectionTypeLabels, Os } from "@/lib/enums"
|
|
||||||
import { cn, formatBytes, getHostDisplayValue, secondsToString, toFixedFloat } from "@/lib/utils"
|
|
||||||
import { Separator } from "@/components/ui/separator"
|
|
||||||
import {
|
import {
|
||||||
AppleIcon,
|
AppleIcon,
|
||||||
ChevronRightSquareIcon,
|
ChevronRightSquareIcon,
|
||||||
@@ -12,16 +7,21 @@ import {
|
|||||||
CpuIcon,
|
CpuIcon,
|
||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
LayoutGridIcon,
|
LayoutGridIcon,
|
||||||
|
MemoryStickIcon,
|
||||||
MonitorIcon,
|
MonitorIcon,
|
||||||
Rows,
|
Rows,
|
||||||
MemoryStickIcon,
|
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
|
||||||
import type { ChartData, SystemDetailsRecord, SystemRecord } from "@/types"
|
|
||||||
import { useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import { useLingui } from "@lingui/react/macro"
|
import ChartTimeSelect from "@/components/charts/chart-time-select"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Card } from "@/components/ui/card"
|
||||||
|
import { FreeBsdIcon, TuxIcon, WebSocketIcon, WindowsIcon } from "@/components/ui/icons"
|
||||||
|
import { Separator } from "@/components/ui/separator"
|
||||||
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||||
import { pb } from "@/lib/api"
|
import { pb } from "@/lib/api"
|
||||||
import { plural } from "@lingui/core/macro"
|
import { ConnectionType, connectionTypeLabels, Os, SystemStatus } from "@/lib/enums"
|
||||||
|
import { cn, formatBytes, getHostDisplayValue, secondsToString, toFixedFloat } from "@/lib/utils"
|
||||||
|
import type { ChartData, SystemDetailsRecord, SystemRecord } from "@/types"
|
||||||
|
|
||||||
export default function InfoBar({
|
export default function InfoBar({
|
||||||
system,
|
system,
|
||||||
@@ -41,6 +41,7 @@ export default function InfoBar({
|
|||||||
|
|
||||||
// Fetch system_details on mount / when system changes
|
// Fetch system_details on mount / when system changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let active = true
|
||||||
setDetails(null)
|
setDetails(null)
|
||||||
// skip fetching system details if agent is older version which includes details in Info struct
|
// skip fetching system details if agent is older version which includes details in Info struct
|
||||||
if (!system.id || system.info?.m) {
|
if (!system.id || system.info?.m) {
|
||||||
@@ -54,10 +55,16 @@ export default function InfoBar({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((details) => {
|
.then((details) => {
|
||||||
|
if (active) {
|
||||||
setDetails(details)
|
setDetails(details)
|
||||||
setIsPodman(details.podman)
|
setIsPodman(details.podman)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(() => setDetails(null))
|
.catch(() => {})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
active = false
|
||||||
|
}
|
||||||
}, [system.id])
|
}, [system.id])
|
||||||
|
|
||||||
// values for system info bar - use details with fallback to system.info
|
// values for system info bar - use details with fallback to system.info
|
||||||
|
|||||||
Reference in New Issue
Block a user