feat(node): add GetNodeCount to get node count and remove no need node update log, only log when node count changed
Build / ubuntu-latest (push) Failing after 3m17s
Build / windows-latest (push) Canceled after 8m6s

This commit is contained in:
2026-09-29 21:43:46 +08:00
parent c7642ecca5
commit aa7e7f87e1
6 changed files with 23 additions and 9 deletions
+5 -5
View File
@@ -46,7 +46,7 @@ var startupOnlySettings = []string{
"dbPath", "dbPath",
} }
// RestartRequiredKeys lists the settings in patch that only take effect at // IsRestartRequiredKeys lists the settings in patch that only take effect at
// startup, as dot-separated paths, in the order startupOnlySettings declares // startup, as dot-separated paths, in the order startupOnlySettings declares
// them. Only config.json carries such settings; an update to one of the other // them. Only config.json carries such settings; an update to one of the other
// files always reports nothing. // files always reports nothing.
@@ -54,7 +54,7 @@ var startupOnlySettings = []string{
// The write itself succeeds either way — this is advice for the user, not a // The write itself succeeds either way — this is advice for the user, not a
// rejection. The result is never nil, so a caller can put it straight into a // rejection. The result is never nil, so a caller can put it straight into a
// JSON response and get [] rather than null. // JSON response and get [] rather than null.
func RestartRequiredKeys(settingsType string, patch map[string]interface{}) []string { func IsRestartRequiredKeys(settingsType string, patch map[string]interface{}) []string {
keys := []string{} keys := []string{}
if settingsType != SettingGlobal { if settingsType != SettingGlobal {
return keys return keys
@@ -63,7 +63,7 @@ func RestartRequiredKeys(settingsType string, patch map[string]interface{}) []st
patched := patchPaths(patch) patched := patchPaths(patch)
for _, watched := range startupOnlySettings { for _, watched := range startupOnlySettings {
for _, path := range patched { for _, path := range patched {
if pathsOverlap(path, watched) { if isPathsOverlap(path, watched) {
keys = append(keys, watched) keys = append(keys, watched)
break break
} }
@@ -96,12 +96,12 @@ func patchPaths(patch map[string]interface{}) []string {
return paths return paths
} }
// pathsOverlap reports whether a patched path and a watched setting can affect // isPathsOverlap reports whether a patched path and a watched setting can affect
// each other: they are the same key, the patch names something inside the // each other: they are the same key, the patch names something inside the
// watched setting, or the patch names a section the watched setting lives in. // watched setting, or the patch names a section the watched setting lives in.
// The last case matters because a patch may replace a whole section, which // The last case matters because a patch may replace a whole section, which
// changes every key under it. // changes every key under it.
func pathsOverlap(patched, watched string) bool { func isPathsOverlap(patched, watched string) bool {
return patched == watched || return patched == watched ||
strings.HasPrefix(patched, watched+".") || strings.HasPrefix(patched, watched+".") ||
strings.HasPrefix(watched, patched+".") strings.HasPrefix(watched, patched+".")
+2 -2
View File
@@ -271,7 +271,7 @@ func TestRestartRequiredKeys(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
got := RestartRequiredKeys(tc.settingsType, tc.patch) got := IsRestartRequiredKeys(tc.settingsType, tc.patch)
if !reflect.DeepEqual(got, tc.want) { if !reflect.DeepEqual(got, tc.want) {
t.Errorf("RestartRequiredKeys() = %v, want %v", got, tc.want) t.Errorf("RestartRequiredKeys() = %v, want %v", got, tc.want)
} }
@@ -287,7 +287,7 @@ func TestRestartRequiredKeys(t *testing.T) {
func TestRestartRequiredKeysIsAdvisory(t *testing.T) { func TestRestartRequiredKeysIsAdvisory(t *testing.T) {
writeTempConfig(t, &global.ConfigPath.Global, `{"system":{"listenPort":"8080"}}`) writeTempConfig(t, &global.ConfigPath.Global, `{"system":{"listenPort":"8080"}}`)
keys := RestartRequiredKeys(SettingGlobal, map[string]interface{}{ keys := IsRestartRequiredKeys(SettingGlobal, map[string]interface{}{
"system": map[string]interface{}{"listenPort": "9090"}, "system": map[string]interface{}{"listenPort": "9090"},
}) })
if len(keys) != 1 { if len(keys) != 1 {
+1 -1
View File
@@ -88,6 +88,6 @@ func SettingsSetHandler(w http.ResponseWriter, r *http.Request) {
// it changed will not be live until the program is restarted. // it changed will not be live until the program is restarted.
utils.SendSuccessResponse(w, "settings updated successfully", map[string]interface{}{ utils.SendSuccessResponse(w, "settings updated successfully", map[string]interface{}{
"type": settingsType, "type": settingsType,
"restartRequired": config.RestartRequiredKeys(settingsType, patch), "restartRequired": config.IsRestartRequiredKeys(settingsType, patch),
}) })
} }
+1 -1
View File
@@ -248,7 +248,7 @@ func (c *Client) FetchNodes() ([]NodeInfo, error) {
postLog.Warning("Failed to save bot node config: " + err.Error()) postLog.Warning("Failed to save bot node config: " + err.Error())
} }
postLog.Info(fmt.Sprintf("Fetched %d nodes from Komari", len(nodes))) // postLog.Debug(fmt.Sprintf("Fetched %d nodes from Komari", len(nodes)))
return nodes, nil return nodes, nil
} }
+10
View File
@@ -107,6 +107,7 @@ type StatusChangeCallback func(change StatusChange)
// All methods are thread-safe. // All methods are thread-safe.
type Tracker struct { type Tracker struct {
mu sync.RWMutex mu sync.RWMutex
count int
nodes map[string]*Node // uuid → Node nodes map[string]*Node // uuid → Node
uuidToName map[string]string // uuid → name (from node list) uuidToName map[string]string // uuid → name (from node list)
onlineSet map[string]bool // which uuids are currently online onlineSet map[string]bool // which uuids are currently online
@@ -161,6 +162,13 @@ func (t *Tracker) fireCallbacks(change StatusChange) {
} }
} }
// GetNodeCount returns the total number of nodes in the tracker.
func (t *Tracker) GetNodeCount() int {
t.mu.RLock()
defer t.mu.RUnlock()
return t.count
}
// UpdateNodeList replaces the full node list and updates each node's name and // UpdateNodeList replaces the full node list and updates each node's name and
// static Info metadata. entries maps UUID → node metadata from the Komari node // static Info metadata. entries maps UUID → node metadata from the Komari node
// list. // list.
@@ -175,6 +183,8 @@ func (t *Tracker) UpdateNodeList(entries map[string]NodeListEntry) {
t.knownUUIDs[uuid] = true t.knownUUIDs[uuid] = true
} }
t.count = len(entries)
t.uuidToName = make(map[string]string, len(entries)) t.uuidToName = make(map[string]string, len(entries))
for uuid, entry := range entries { for uuid, entry := range entries {
t.uuidToName[uuid] = entry.Name t.uuidToName[uuid] = entry.Name
+4
View File
@@ -260,11 +260,15 @@ func startBackgroundTasks(_ *config.Config) {
if client == nil { if client == nil {
continue continue
} }
orgNodeCount := node.GetTracker().GetNodeCount()
nodes, err := client.FetchNodes() nodes, err := client.FetchNodes()
if err != nil { if err != nil {
postLog.Warning("Failed to refresh node list: " + err.Error()) postLog.Warning("Failed to refresh node list: " + err.Error())
continue continue
} }
if node.GetTracker().GetNodeCount() != orgNodeCount {
postLog.Info(fmt.Sprintf("Refreshed node list: %d nodes", len(nodes)))
}
tracker := node.GetTracker() tracker := node.GetTracker()
if tracker != nil { if tracker != nil {
tracker.UpdateNodeList(komari.BuildNodeListData(nodes)) tracker.UpdateNodeList(komari.BuildNodeListData(nodes))