This commit is contained in:
henrygd
2026-04-22 21:40:52 -04:00
parent 8f03cbf11c
commit 5fc774666f
12 changed files with 160 additions and 85 deletions

View File

@@ -1,11 +1,20 @@
package hub
import (
"strconv"
"github.com/henrygd/beszel/internal/entities/probe"
"github.com/henrygd/beszel/internal/hub/systems"
"github.com/pocketbase/pocketbase/core"
)
// generateProbeID creates a stable hash ID for a probe based on its configuration and the system it belongs to.
func generateProbeID(systemId string, config probe.Config) string {
intervalStr := strconv.FormatUint(uint64(config.Interval), 10)
portStr := strconv.FormatUint(uint64(config.Port), 10)
return systems.MakeStableHashId(systemId, config.Protocol, config.Target, portStr, intervalStr)
}
func bindNetworkProbesEvents(h *Hub) {
// on create, make sure the id is set to a stable hash
h.OnRecordCreate("network_probes").BindFunc(func(e *core.RecordEvent) error {
@@ -16,8 +25,7 @@ func bindNetworkProbesEvents(h *Hub) {
Port: uint16(e.Record.GetInt("port")),
Interval: uint16(e.Record.GetInt("interval")),
}
key := config.Key()
id := systems.MakeStableHashId(systemID, key)
id := generateProbeID(systemID, *config)
e.Record.Set("id", id)
return e.Next()
})

View File

@@ -0,0 +1,79 @@
package hub
import (
"testing"
"github.com/henrygd/beszel/internal/entities/probe"
"github.com/stretchr/testify/assert"
)
func TestGenerateProbeID(t *testing.T) {
tests := []struct {
name string
systemID string
config probe.Config
expected string
}{
{
name: "HTTP probe on example.com",
systemID: "sys123",
config: probe.Config{
Protocol: "http",
Target: "example.com",
Port: 80,
Interval: 60,
},
expected: "d5f27931",
},
{
name: "HTTP probe on example.com with different system ID",
systemID: "sys1234",
config: probe.Config{
Protocol: "http",
Target: "example.com",
Port: 80,
Interval: 60,
},
expected: "6f8b17f1",
},
{
name: "Same probe, different interval",
systemID: "sys1234",
config: probe.Config{
Protocol: "http",
Target: "example.com",
Port: 80,
Interval: 120,
},
expected: "6d4baf8",
},
{
name: "ICMP probe on 1.1.1.1",
systemID: "sys456",
config: probe.Config{
Protocol: "icmp",
Target: "1.1.1.1",
Port: 0,
Interval: 10,
},
expected: "80b5836b",
}, {
name: "ICMP probe on 1.1.1.1 with different system ID",
systemID: "sys4567",
config: probe.Config{
Protocol: "icmp",
Target: "1.1.1.1",
Port: 0,
Interval: 10,
},
expected: "a6652680",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := generateProbeID(tt.systemID, tt.config)
assert.Equal(t, tt.expected, got, "generateProbeID() = %v, want %v", got, tt.expected)
})
}
}

View File

@@ -335,7 +335,7 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst
if !realtimeActive {
db = app.DB()
nowString = time.Now().UTC().Format(types.DefaultDateLayout)
sql := fmt.Sprintf("UPDATE %s SET resAvg={:resAvg}, resMin1h={:resMin1h}, resMax1h={:resMax1h}, resAvg1h={:resAvg1h}, loss1h={:loss1h}, updated={:updated} WHERE id={:id}", collectionName)
sql := fmt.Sprintf("UPDATE %s SET resAvg={:res}, resMin1h={:resMin1h}, resMax1h={:resMax1h}, resAvg1h={:resAvg1h}, loss1h={:loss1h}, updated={:updated} WHERE id={:id}", collectionName)
updateQuery = db.NewQuery(sql)
}
@@ -365,14 +365,13 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst
}
// update network_probes records
for key, values := range data {
id := MakeStableHashId(systemId, key)
for id, values := range data {
switch realtimeActive {
case true:
var record *core.Record
record, err = app.FindRecordById(collectionName, id)
if err == nil {
record.Set("resAvg", probeMetric(values, 0))
record.Set("res", probeMetric(values, 0))
record.Set("resAvg1h", probeMetric(values, 1))
record.Set("resMin1h", probeMetric(values, 2))
record.Set("resMax1h", probeMetric(values, 3))
@@ -382,7 +381,7 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst
default:
_, err = updateQuery.Bind(dbx.Params{
"id": id,
"resAvg": probeMetric(values, 0),
"res": probeMetric(values, 0),
"resAvg1h": probeMetric(values, 1),
"resMin1h": probeMetric(values, 2),
"resMax1h": probeMetric(values, 3),
@@ -391,7 +390,7 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst
}).Execute()
}
if err != nil {
app.Logger().Warn("Failed to update probe", "system", systemId, "probe", key, "err", err)
app.Logger().Warn("Failed to update probe", "system", systemId, "probe", id, "err", err)
}
}

View File

@@ -325,7 +325,7 @@ func (sm *SystemManager) AddWebSocketSystem(systemId string, agentVersion semver
configs := sm.GetProbeConfigsForSystem(systemId)
if len(configs) > 0 {
if err := system.SyncNetworkProbes(configs); err != nil {
sm.hub.Logger().Warn("failed to sync probes on connect", "system", systemId, "err", err)
sm.hub.Logger().Warn("failed to sync probes to agent", "system", systemId, "err", err)
}
}
}()
@@ -344,26 +344,11 @@ func (sm *SystemManager) resetFailedSmartFetchState(systemID string) {
// GetProbeConfigsForSystem returns all enabled probe configs for a system.
func (sm *SystemManager) GetProbeConfigsForSystem(systemID string) []probe.Config {
records, err := sm.hub.FindRecordsByFilter(
"network_probes",
"system = {:system} && enabled = true",
"",
0, 0,
dbx.Params{"system": systemID},
)
if err != nil || len(records) == 0 {
return nil
}
configs := make([]probe.Config, 0, len(records))
for _, r := range records {
configs = append(configs, probe.Config{
Target: r.GetString("target"),
Protocol: r.GetString("protocol"),
Port: uint16(r.GetInt("port")),
Interval: uint16(r.GetInt("interval")),
})
}
var configs []probe.Config
_ = sm.hub.DB().
NewQuery("SELECT id, target, protocol, port, interval FROM network_probes WHERE system = {:system} AND enabled = true").
Bind(dbx.Params{"system": systemID}).
All(&configs)
return configs
}