From c96fb88fbddd068d8dbd7c55fc163a039b195f08 Mon Sep 17 00:00:00 2001 From: admin Date: Sun, 2 Aug 2026 09:07:53 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20edit=20node=20=E2=80=94=20reconfigure?= =?UTF-8?q?=20port,=20upstream=20IP/port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/deploy/main.go | 91 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/cmd/deploy/main.go b/cmd/deploy/main.go index a37eaee..f80ee40 100644 --- a/cmd/deploy/main.go +++ b/cmd/deploy/main.go @@ -60,9 +60,10 @@ func showMenu() { fmt.Println("─── Main Menu ───") fmt.Println(" [1] Deploy a node") fmt.Println(" [2] Remove a node") - fmt.Println(" [3] Show status") - fmt.Println(" [4] Remote tunnel") - fmt.Println(" [5] Dashboard (TUI)") + fmt.Println(" [3] Edit a node") + fmt.Println(" [4] Show status") + fmt.Println(" [5] Remote tunnel") + fmt.Println(" [6] Dashboard (TUI)") fmt.Println(" [q] Quit") fmt.Print("\nChoice: ") c, _ := reader.ReadString('\n') @@ -72,10 +73,12 @@ func showMenu() { case "2": removeMenu() case "3": - showStatus() + editMenu() case "4": - startTunnel() + showStatus() case "5": + startTunnel() + case "6": if err := ui.Run(); err != nil { fmt.Println("TUI error:", err) } @@ -282,6 +285,84 @@ func removeMenu() { fmt.Printf("✅ %s removed.\n", n.Name) } +// ─── Edit ───────────────────────────────────────────────────────────── + +func editMenu() { + s, _ := state.Load() + if s == nil || len(s.Nodes) == 0 { + fmt.Println("No nodes deployed.") + return + } + + fmt.Println("\n─── Edit Node ───") + for i, n := range s.Nodes { + out, _ := exec.Command("systemctl", "is-active", "wallarm-node@"+n.Name).CombinedOutput() + status := "●" + if strings.TrimSpace(string(out)) != "active" { status = "○" } + fmt.Printf(" [%d] %s %s %s → %s:%d\n", + i+1, status, n.Name, n.Address, n.UpstreamIP, n.UpstreamPort) + } + fmt.Print("\nSelect node number: ") + reader := bufio.NewReader(os.Stdin) + choice, _ := reader.ReadString('\n') + idx, err := strconv.Atoi(strings.TrimSpace(choice)) + if err != nil || idx < 1 || idx > len(s.Nodes) { + fmt.Println("Invalid choice") + return + } + n := &s.Nodes[idx-1] + + fmt.Println("\nCurrent config:") + fmt.Printf(" Name: %s\n", n.Name) + fmt.Printf(" Listen: %s\n", n.Address) + fmt.Printf(" Upstream: %s:%d\n", n.UpstreamIP, n.UpstreamPort) + fmt.Println("\nEnter new values (leave blank to keep current):") + + fmt.Print("Listen port [" + strings.TrimPrefix(n.Address, "0.0.0.0:") + "]: ") + port, _ := reader.ReadString('\n') + port = strings.TrimSpace(port) + + fmt.Printf("Upstream IP [%s]: ", n.UpstreamIP) + ip, _ := reader.ReadString('\n') + ip = strings.TrimSpace(ip) + + fmt.Printf("Upstream port [%d]: ", n.UpstreamPort) + portStr, _ := reader.ReadString('\n') + portStr = strings.TrimSpace(portStr) + + if port == "" && ip == "" && portStr == "" { + fmt.Println("No changes.") + return + } + if port != "" { n.Address = "0.0.0.0:" + port } + if ip != "" { n.UpstreamIP = ip } + if portStr != "" { + p, _ := strconv.Atoi(portStr) + if p > 0 { n.UpstreamPort = p } + } + + // Update nginx config + base := "/opt/fw/" + n.Name + "/wallarm" + nginxConf := base + "/nginx/conf/nginx.conf" + if _, err := os.Stat(nginxConf); err == nil { + data, _ := os.ReadFile(nginxConf) + newData := string(data) + if port != "" { + newData = strings.ReplaceAll(newData, n.Address, "0.0.0.0:"+port) + } + if ip != "" || portStr != "" { + oldUp := fmt.Sprintf("%s:%d", n.UpstreamIP, n.UpstreamPort) + newUp := fmt.Sprintf("%s:%d", n.UpstreamIP, n.UpstreamPort) + newData = strings.ReplaceAll(newData, oldUp, newUp) + } + os.WriteFile(nginxConf, []byte(newData), 0644) + } + + state.Save(s) + fmt.Println("✅ Config updated. Restart the node to apply:") + fmt.Printf(" systemctl restart wallarm-node@%s\n", n.Name) +} + // ─── Status ─────────────────────────────────────────────────────────── func showStatus() {