139 lines
3.8 KiB
Go
139 lines
3.8 KiB
Go
package probe
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/logger"
|
|
)
|
|
|
|
// RetentionDays reads the history retention window (days) from the
|
|
// panel settings (key retention_days, as in hub). Default 7 — the
|
|
// window the user settled on — clamped to [1, 365] (hub's cap).
|
|
func RetentionDays() int64 {
|
|
var row model.Setting
|
|
err := database.GetDB().Where("key = ?", "retention_days").First(&row).Error
|
|
if err != nil {
|
|
return DetailDays
|
|
}
|
|
n, perr := strconv.ParseInt(strings.TrimSpace(row.Value), 10, 64)
|
|
if perr != nil {
|
|
return DetailDays
|
|
}
|
|
if n < 1 {
|
|
return 1
|
|
}
|
|
if n > 365 {
|
|
return 365
|
|
}
|
|
return n
|
|
}
|
|
|
|
// Record files one heartbeat's probe sample into the default store.
|
|
// prevLastSeenMs is the agent row's last-seen stamp from before this
|
|
// heartbeat (milliseconds, as stored on agent_nodes). Best-effort: any
|
|
// failure is logged and swallowed so a heartbeat never fails over
|
|
// history.
|
|
func Record(agentID int64, smp Sample, prevLastSeenMs int64) {
|
|
st := Default()
|
|
if st == nil {
|
|
return
|
|
}
|
|
if err := st.Record(agentID, smp, time.Now(), prevLastSeenMs/1000); err != nil {
|
|
logger.Warningf("probe: record agent %d: %v", agentID, err)
|
|
}
|
|
}
|
|
|
|
// Maintain runs one housekeeping pass against the default store:
|
|
// roll_up first, then prune — prune runs even when roll_up fails, as
|
|
// it keeps minute rows until their hour is folded and skipping it
|
|
// would leave the database growing (hub housekeeping discipline).
|
|
func Maintain() {
|
|
st := Default()
|
|
if st == nil {
|
|
return
|
|
}
|
|
keep := RetentionDays()
|
|
if folded, err := st.RollUp(time.Now().Unix(), keep); err != nil {
|
|
logger.Warningf("probe: roll_up failed: %v", err)
|
|
} else if folded > 1 {
|
|
logger.Infof("probe: folded %d hours of history into the hourly tier", folded)
|
|
}
|
|
if _, err := st.Prune(keep); err != nil {
|
|
logger.Warningf("probe: prune failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// BookHeld books every node's held, not-yet-booked reading on the
|
|
// default store; called as the master shuts down.
|
|
func BookHeld() {
|
|
if st := Default(); st != nil {
|
|
st.BookHeld()
|
|
}
|
|
}
|
|
|
|
// TrafficSnapshot is the B3 read port: every agent's gated ledger.
|
|
func TrafficSnapshot() (map[int64]Traffic, error) {
|
|
st := Default()
|
|
if st == nil {
|
|
return map[int64]Traffic{}, nil
|
|
}
|
|
return st.AllTraffic()
|
|
}
|
|
|
|
// MetaSnapshot is the B3 read port for display metadata: every agent's
|
|
// probe_meta row, keyed by agent id. Agents without a row are absent;
|
|
// callers substitute DefaultMeta.
|
|
func MetaSnapshot() (map[int64]Meta, error) {
|
|
st := Default()
|
|
if st == nil {
|
|
return map[int64]Meta{}, nil
|
|
}
|
|
return st.AllMeta()
|
|
}
|
|
|
|
// MetaFor reads one agent's metadata row from the default store.
|
|
// ok is false when the agent never reported (no row ensured yet).
|
|
func MetaFor(agentID int64) (Meta, bool, error) {
|
|
st := Default()
|
|
if st == nil {
|
|
return Meta{}, false, nil
|
|
}
|
|
return st.Meta(agentID)
|
|
}
|
|
|
|
// SaveMeta persists one agent's metadata row (B3 edit surface).
|
|
func SaveMeta(m Meta) error {
|
|
st := Default()
|
|
if st == nil {
|
|
return nil
|
|
}
|
|
return st.UpsertMeta(m)
|
|
}
|
|
|
|
// MetricHistory is the B3 read port for one agent's history window.
|
|
// With no store the window is simply empty.
|
|
func MetricHistory(agentID int64, sp Span) ([]MetricRow, error) {
|
|
st := Default()
|
|
if st == nil {
|
|
return []MetricRow{}, nil
|
|
}
|
|
return st.MetricHistory(agentID, sp)
|
|
}
|
|
|
|
// DeleteAgentData drops a removed agent's probe rows (B3 cleanup point
|
|
// called from agent deletion). Best-effort: failures are logged and
|
|
// swallowed, as a stale probe row must never block a panel operation.
|
|
func DeleteAgentData(agentID int64) {
|
|
st := Default()
|
|
if st == nil {
|
|
return
|
|
}
|
|
if err := st.DeleteAgentData(agentID); err != nil {
|
|
logger.Warningf("probe: delete agent %d data: %v", agentID, err)
|
|
}
|
|
}
|