58 lines
1.7 KiB
Go
58 lines
1.7 KiB
Go
package service
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
// ProbeCoverageRow is one agent's cf-probe retirement readiness
|
|
// (item 7 B6): whether its heartbeats carry the probe block the
|
|
// public page is fed from. The retirement checklist in the settings
|
|
// tab renders from these rows — the operator verifies every node is
|
|
// covered by the in-panel probe before standing the old cf-probe
|
|
// fleet down, node by node.
|
|
type ProbeCoverageRow struct {
|
|
Name string `json:"name"`
|
|
Online bool `json:"online"`
|
|
HasProbe bool `json:"hasProbe"`
|
|
ProbeAt int64 `json:"probeAt"` // unix ms of the last probe-carrying heartbeat, 0 = never
|
|
}
|
|
|
|
// ProbeAgentCoverage lists every agent with its probe coverage. The
|
|
// display name follows the public view's rule (the bound node's name
|
|
// first, the agent's own name otherwise); online is the public
|
|
// view's 150s freshness window.
|
|
func ProbeAgentCoverage() []ProbeCoverageRow {
|
|
db := database.GetDB()
|
|
var agents []model.AgentNode
|
|
if err := db.Find(&agents).Error; err != nil {
|
|
return []ProbeCoverageRow{}
|
|
}
|
|
nodeNames := map[int]string{}
|
|
var nodes []model.Node
|
|
if err := db.Find(&nodes).Error; err == nil {
|
|
for _, n := range nodes {
|
|
nodeNames[n.Id] = n.Name
|
|
}
|
|
}
|
|
nowMs := time.Now().UnixMilli()
|
|
out := make([]ProbeCoverageRow, 0, len(agents))
|
|
for _, a := range agents {
|
|
name := a.Name
|
|
if a.NodeID != nil {
|
|
if bound, ok := nodeNames[*a.NodeID]; ok && bound != "" {
|
|
name = bound
|
|
}
|
|
}
|
|
out = append(out, ProbeCoverageRow{
|
|
Name: name,
|
|
Online: a.LastSeen > 0 && nowMs-a.LastSeen <= 150_000,
|
|
HasProbe: a.ProbeAt > 0,
|
|
ProbeAt: a.ProbeAt,
|
|
})
|
|
}
|
|
return out
|
|
}
|