From bf4c75fe246947b910e4aa840b286f275c150fe1 Mon Sep 17 00:00:00 2001 From: DBT Date: Wed, 25 Feb 2026 17:26:02 +0000 Subject: [PATCH] webui: add offline-node delete API and remove channels management endpoint --- cmd/clawgo/cmd_gateway.go | 3 -- pkg/nodes/manager.go | 19 +++++++++++++ pkg/nodes/registry_server.go | 55 +++++++++++++++++++----------------- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/cmd/clawgo/cmd_gateway.go b/cmd/clawgo/cmd_gateway.go index d34bfeb..d86e40d 100644 --- a/cmd/clawgo/cmd_gateway.go +++ b/cmd/clawgo/cmd_gateway.go @@ -188,9 +188,6 @@ func gatewayCmd() { } return agentLoop.ProcessDirect(cctx, content, sessionKey) }) - registryServer.SetChannelsHandler(func() interface{} { - return channelManager.GetStatus() - }) registryServer.SetConfigAfterHook(func() { _ = syscall.Kill(os.Getpid(), syscall.SIGHUP) }) diff --git a/pkg/nodes/manager.go b/pkg/nodes/manager.go index 5d17796..b781c84 100644 --- a/pkg/nodes/manager.go +++ b/pkg/nodes/manager.go @@ -104,6 +104,25 @@ func (m *Manager) List() []NodeInfo { return out } +func (m *Manager) Remove(id string) bool { + id = strings.TrimSpace(id) + if id == "" { + return false + } + m.mu.Lock() + _, exists := m.nodes[id] + if exists { + delete(m.nodes, id) + delete(m.handlers, id) + } + m.mu.Unlock() + if exists { + m.saveState() + m.appendAudit("delete", id, nil) + } + return exists +} + func (m *Manager) RegisterHandler(nodeID string, h Handler) { m.mu.Lock() defer m.mu.Unlock() diff --git a/pkg/nodes/registry_server.go b/pkg/nodes/registry_server.go index 8ac12a7..aa2c6ff 100644 --- a/pkg/nodes/registry_server.go +++ b/pkg/nodes/registry_server.go @@ -26,7 +26,6 @@ type RegistryServer struct { onChat func(ctx context.Context, sessionKey, content string) (string, error) onConfigAfter func() onCron func(action string, args map[string]interface{}) (interface{}, error) - onChannels func() interface{} webUIDir string } @@ -47,7 +46,6 @@ func (s *RegistryServer) SetLogFilePath(path string) { s.logFilePath = strings.T func (s *RegistryServer) SetChatHandler(fn func(ctx context.Context, sessionKey, content string) (string, error)) { s.onChat = fn } -func (s *RegistryServer) SetChannelsHandler(fn func() interface{}) { s.onChannels = fn } func (s *RegistryServer) SetConfigAfterHook(fn func()) { s.onConfigAfter = fn } func (s *RegistryServer) SetCronHandler(fn func(action string, args map[string]interface{}) (interface{}, error)) { s.onCron = fn @@ -73,7 +71,6 @@ func (s *RegistryServer) Start(ctx context.Context) error { mux.HandleFunc("/webui/api/upload", s.handleWebUIUpload) mux.HandleFunc("/webui/api/nodes", s.handleWebUINodes) mux.HandleFunc("/webui/api/cron", s.handleWebUICron) - mux.HandleFunc("/webui/api/channels", s.handleWebUIChannels) mux.HandleFunc("/webui/api/skills", s.handleWebUISkills) mux.HandleFunc("/webui/api/exec_approvals", s.handleWebUIExecApprovals) mux.HandleFunc("/webui/api/logs/stream", s.handleWebUILogsStream) @@ -414,15 +411,37 @@ func (s *RegistryServer) handleWebUINodes(w http.ResponseWriter, r *http.Request http.Error(w, "unauthorized", http.StatusUnauthorized) return } - if r.Method != http.MethodGet { + switch r.Method { + case http.MethodGet: + list := []NodeInfo{} + if s.mgr != nil { + list = s.mgr.List() + } + _ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "nodes": list}) + case http.MethodPost: + var body struct { + Action string `json:"action"` + ID string `json:"id"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + http.Error(w, "invalid json", http.StatusBadRequest) + return + } + action := strings.ToLower(strings.TrimSpace(body.Action)) + if action != "delete" { + http.Error(w, "unsupported action", http.StatusBadRequest) + return + } + if s.mgr == nil { + http.Error(w, "nodes manager unavailable", http.StatusInternalServerError) + return + } + id := strings.TrimSpace(body.ID) + ok := s.mgr.Remove(id) + _ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "deleted": ok, "id": id}) + default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) - return } - list := []NodeInfo{} - if s.mgr != nil { - list = s.mgr.List() - } - _ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "nodes": list}) } func (s *RegistryServer) handleWebUICron(w http.ResponseWriter, r *http.Request) { @@ -475,22 +494,6 @@ func (s *RegistryServer) handleWebUICron(w http.ResponseWriter, r *http.Request) } } -func (s *RegistryServer) handleWebUIChannels(w http.ResponseWriter, r *http.Request) { - if !s.checkAuth(r) { - http.Error(w, "unauthorized", http.StatusUnauthorized) - return - } - if r.Method != http.MethodGet { - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) - return - } - res := map[string]interface{}{"ok": true, "channels": []interface{}{}} - if s.onChannels != nil { - res["channels"] = s.onChannels() - } - _ = json.NewEncoder(w).Encode(res) -} - func (s *RegistryServer) handleWebUISkills(w http.ResponseWriter, r *http.Request) { if !s.checkAuth(r) { http.Error(w, "unauthorized", http.StatusUnauthorized)