P8(1): restore homepage overview curves on a memory-only sampling ring
Backend: new overviewMetrics store — a single-tier (2s x 360 = 12min) in-memory ring fed by ServerService.RefreshStatus for exactly the eight overview series (cpu/mem/swap/diskUsage/netUp/netDown/tcpCount/udpCount). No persistence, no gob snapshot, no system-history page: the only surface is GET /panel/api/server/history/:metric/:bucket restricted to those keys, which seeds the frontend curves. ~46KB bounded by construction; the ring starts empty on every panel start. Frontend: restore useOverviewHistory + the VitalTile/ThroughputCard/ ConnectionsCard sparklines and the IndexPage wiring exactly as they were before P7 removed them along with the history module.
This commit is contained in:
@@ -177,8 +177,8 @@ var allowedHistoryBuckets = map[int]bool{
|
||||
}
|
||||
|
||||
// IsAllowedHistoryBucket reports whether a bucket-seconds value is in the
|
||||
// whitelist used by /server/history, /server/cpuHistory, /server/xrayMetricsHistory,
|
||||
// /server/xrayObservatoryHistory, and /nodes/history.
|
||||
// whitelist used by the time-series aggregation endpoints
|
||||
// (/server/history for the homepage overview, /nodes/history).
|
||||
func IsAllowedHistoryBucket(bucketSeconds int) bool {
|
||||
return allowedHistoryBuckets[bucketSeconds]
|
||||
}
|
||||
@@ -242,7 +242,7 @@ func (s *ServerService) isFail2banInstalled() bool {
|
||||
// Returns the new snapshot (may be nil if collection failed). Called by the
|
||||
// background ticker; the caller is responsible for any side effects
|
||||
// (websocket broadcast). The realtime status served to the UI comes from
|
||||
// this snapshot — the removed system-history series used to be fed here too.
|
||||
// this snapshot; the homepage overview ring is fed here too.
|
||||
func (s *ServerService) RefreshStatus() *Status {
|
||||
next := s.GetStatus(s.LastStatus())
|
||||
if next == nil {
|
||||
@@ -251,9 +251,47 @@ func (s *ServerService) RefreshStatus() *Status {
|
||||
s.lastStatusMu.Lock()
|
||||
s.lastStatus = next
|
||||
s.lastStatusMu.Unlock()
|
||||
s.AppendOverviewSample(time.Now(), next)
|
||||
return next
|
||||
}
|
||||
|
||||
// AppendOverviewSample writes one tick of the homepage overview metrics —
|
||||
// CPU, memory/swap/disk percents, network throughput (bytes/s) and TCP/UDP
|
||||
// connection counts — into the memory-only overview ring. Called by
|
||||
// RefreshStatus on the @2s status cadence, so all series stay aligned.
|
||||
// Only the overview keys are kept; this is not the removed system-history
|
||||
// sampler (no extra series, no persistence).
|
||||
func (s *ServerService) AppendOverviewSample(t time.Time, status *Status) {
|
||||
if status == nil {
|
||||
return
|
||||
}
|
||||
overviewMetrics.append("cpu", t, status.Cpu)
|
||||
if status.Mem.Total > 0 {
|
||||
overviewMetrics.append("mem", t, float64(status.Mem.Current)*100.0/float64(status.Mem.Total))
|
||||
}
|
||||
if status.Swap.Total > 0 {
|
||||
overviewMetrics.append("swap", t, float64(status.Swap.Current)*100.0/float64(status.Swap.Total))
|
||||
} else {
|
||||
overviewMetrics.append("swap", t, 0)
|
||||
}
|
||||
overviewMetrics.append("netUp", t, float64(status.NetIO.Up))
|
||||
overviewMetrics.append("netDown", t, float64(status.NetIO.Down))
|
||||
if status.Disk.Total > 0 {
|
||||
overviewMetrics.append("diskUsage", t, float64(status.Disk.Current)*100.0/float64(status.Disk.Total))
|
||||
}
|
||||
overviewMetrics.append("tcpCount", t, float64(status.TcpCount))
|
||||
overviewMetrics.append("udpCount", t, float64(status.UdpCount))
|
||||
}
|
||||
|
||||
// AggregateOverviewMetric returns up to maxPoints averaged buckets of size
|
||||
// bucketSeconds for one overview metric (see OverviewMetricKeys). Output
|
||||
// points have keys {"t": unixSec, "v": value}. It backs the seed fetch of
|
||||
// the homepage overview curves; the ring behind it is memory-only, so
|
||||
// shortly after a panel restart it legitimately returns few or no points.
|
||||
func (s *ServerService) AggregateOverviewMetric(metric string, bucketSeconds int, maxPoints int) []map[string]any {
|
||||
return overviewMetrics.aggregate(metric, bucketSeconds, maxPoints)
|
||||
}
|
||||
|
||||
func getPublicIP(url string) string {
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
|
||||
Reference in New Issue
Block a user