package api import ( "encoding/json" "log" "net/http" "time" "github.com/cynfo/rmm-backend/ws" ) func (h *Handler) setupTunnelRoutes(mux *http.ServeMux, hub *ws.Hub) { // Command-Endpoints mux.HandleFunc("POST /api/v1/agents/{id}/exec", h.executeCommand(hub)) mux.HandleFunc("POST /api/v1/agents/{id}/update", h.agentUpdate(hub)) mux.HandleFunc("POST /api/v1/agents/{id}/major-update", h.agentMajorUpdate(hub)) mux.HandleFunc("POST /api/v1/agents/{id}/update-check", h.agentUpdateCheck(hub)) mux.HandleFunc("POST /api/v1/agents/{id}/reboot", h.agentReboot(hub)) mux.HandleFunc("POST /api/v1/agents/{id}/tunnel", h.openTunnel(hub)) mux.HandleFunc("DELETE /api/v1/agents/{id}/tunnel/{tunnel_id}", h.closeTunnel(hub)) mux.HandleFunc("GET /api/v1/agents/{id}/tunnels", h.listTunnels(hub)) // HTTP-Proxy durch Tunnel mux.HandleFunc("GET /api/v1/agents/{id}/proxy/{tunnel_id}/", h.httpProxy(hub)) mux.HandleFunc("POST /api/v1/agents/{id}/proxy/{tunnel_id}/", h.httpProxy(hub)) mux.HandleFunc("PUT /api/v1/agents/{id}/proxy/{tunnel_id}/", h.httpProxy(hub)) mux.HandleFunc("DELETE /api/v1/agents/{id}/proxy/{tunnel_id}/", h.httpProxy(hub)) // Metrics-Endpoints mux.HandleFunc("GET /api/v1/agents/{id}/metrics", h.getMetrics()) mux.HandleFunc("GET /api/v1/agents/{id}/metrics/summary", h.getMetricsSummary()) // WireGuard-Endpoints mux.HandleFunc("POST /api/v1/agents/{id}/wireguard/peers", h.wgAddPeer(hub)) mux.HandleFunc("GET /api/v1/agents/{id}/wireguard/peers", h.wgListPeers(hub)) mux.HandleFunc("DELETE /api/v1/agents/{id}/wireguard/peers/{uuid}", h.wgDeletePeer(hub)) // Backup-Endpoints mux.HandleFunc("POST /api/v1/agents/{id}/backup", h.triggerBackup(hub)) mux.HandleFunc("GET /api/v1/agents/{id}/backups", h.listBackups()) mux.HandleFunc("GET /api/v1/agents/{id}/backups/{backup_id}", h.getBackup()) mux.HandleFunc("DELETE /api/v1/agents/{id}/backups/{backup_id}", h.deleteBackup()) mux.HandleFunc("GET /api/v1/agents/{id}/backups/diff", h.diffBackups()) // WebSocket-Endpoint mux.HandleFunc("GET /api/v1/agent/ws", ws.NewWebSocketHandler(hub)) // Hub-Status mux.HandleFunc("GET /api/v1/hub/status", h.hubStatus(hub)) } func (h *Handler) executeCommand(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { agentID := r.PathValue("id") // Request parsen var req struct { Command string `json:"command"` Timeout int `json:"timeout,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "Ungültige Anfrage") return } if req.Command == "" { writeError(w, http.StatusBadRequest, "Parameter 'command' erforderlich") return } if req.Timeout == 0 { req.Timeout = 30 } // Prüfen ob Agent verbunden ist if !hub.IsAgentConnected(agentID) { writeError(w, http.StatusServiceUnavailable, "Agent nicht verbunden") return } // Command-Message erstellen cmd := map[string]interface{}{ "type": "command", "id": generateID(), "command": "exec", "params": map[string]interface{}{ "command": req.Command, "timeout": req.Timeout, }, } cmdJSON, _ := json.Marshal(cmd) // Command an Agent senden if err := hub.SendToAgent(agentID, cmdJSON); err != nil { log.Printf("Command senden fehlgeschlagen: %v", err) writeError(w, http.StatusInternalServerError, "Command senden fehlgeschlagen") return } // Response zurückgeben writeJSON(w, http.StatusOK, map[string]interface{}{ "message": "Command gesendet", "command_id": cmd["id"], }) } } func (h *Handler) openTunnel(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { agentID := r.PathValue("id") // Request parsen var req struct { TargetHost string `json:"target_host"` TargetPort int `json:"target_port"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "Ungültige Anfrage") return } if req.TargetHost == "" || req.TargetPort <= 0 { writeError(w, http.StatusBadRequest, "target_host und target_port erforderlich") return } // Tunnel öffnen tunnel, err := hub.GetTunnelManager().OpenTunnel(agentID, req.TargetHost, req.TargetPort) if err != nil { log.Printf("Tunnel öffnen fehlgeschlagen: %v", err) writeError(w, http.StatusInternalServerError, err.Error()) return } // Response writeJSON(w, http.StatusCreated, map[string]interface{}{ "tunnel_id": tunnel.ID, "proxy_port": tunnel.ProxyPort, "target_host": tunnel.TargetHost, "target_port": tunnel.TargetPort, "created_at": tunnel.CreatedAt, }) } } func (h *Handler) closeTunnel(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { tunnelID := r.PathValue("tunnel_id") // Tunnel schließen if err := hub.GetTunnelManager().CloseTunnel(tunnelID); err != nil { writeError(w, http.StatusNotFound, err.Error()) return } writeJSON(w, http.StatusOK, map[string]string{ "message": "Tunnel geschlossen", }) } } func (h *Handler) listTunnels(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { agentID := r.PathValue("id") // Tunnel für Agent abrufen tunnels := hub.GetTunnelManager().GetAgentTunnels(agentID) // Response formatieren result := make([]map[string]interface{}, len(tunnels)) for i, tunnel := range tunnels { result[i] = map[string]interface{}{ "tunnel_id": tunnel.ID, "proxy_port": tunnel.ProxyPort, "target_host": tunnel.TargetHost, "target_port": tunnel.TargetPort, "active": tunnel.Active, "created_at": tunnel.CreatedAt, } } writeJSON(w, http.StatusOK, result) } } func (h *Handler) httpProxy(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { tunnelID := r.PathValue("tunnel_id") // HTTP-Proxy durch Tunnel if err := hub.GetTunnelManager().CreateHTTPProxy(tunnelID, w, r); err != nil { writeError(w, http.StatusNotFound, err.Error()) return } } } // --- Update Commands --- func (h *Handler) agentUpdateCheck(hub *ws.Hub) http.HandlerFunc { return h.sendAgentCommand(hub, "update_check", nil, 120) } func (h *Handler) agentUpdate(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { agentID := r.PathValue("id") var req struct { Reboot bool `json:"reboot"` } json.NewDecoder(r.Body).Decode(&req) params := map[string]interface{}{ "reboot": req.Reboot, } h.sendCommandAndWait(hub, agentID, "update", params, 900, w) } } func (h *Handler) agentMajorUpdate(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { agentID := r.PathValue("id") var req struct { Version string `json:"version"` Reboot bool `json:"reboot"` Phase string `json:"phase"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Version == "" { writeError(w, http.StatusBadRequest, "Parameter 'version' erforderlich (z.B. '26.1')") return } params := map[string]interface{}{ "version": req.Version, "reboot": req.Reboot, } if req.Phase != "" { params["phase"] = req.Phase } h.sendCommandAndWait(hub, agentID, "major_update", params, 1200, w) } } func (h *Handler) agentReboot(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { agentID := r.PathValue("id") var req struct { Delay int `json:"delay"` } json.NewDecoder(r.Body).Decode(&req) params := map[string]interface{}{} if req.Delay > 0 { params["delay"] = req.Delay } h.sendCommandAndWait(hub, agentID, "reboot", params, 30, w) } } // sendAgentCommand erstellt einen einfachen Command-Handler ohne Request-Body func (h *Handler) sendAgentCommand(hub *ws.Hub, command string, params map[string]interface{}, timeoutSec int) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { agentID := r.PathValue("id") h.sendCommandAndWait(hub, agentID, command, params, timeoutSec, w) } } // sendCommandAndWait schickt ein Command an den Agent und wartet auf Response func (h *Handler) sendCommandAndWait(hub *ws.Hub, agentID, command string, params map[string]interface{}, timeoutSec int, w http.ResponseWriter) { if !hub.IsAgentConnected(agentID) { writeError(w, http.StatusServiceUnavailable, "Agent nicht verbunden") return } cmdID := generateID() cmd := map[string]interface{}{ "type": "command", "id": cmdID, "command": command, } if params != nil { cmd["params"] = params } // Response-Channel registrieren responseCh := hub.RegisterResponseWaiter(cmdID) defer hub.UnregisterResponseWaiter(cmdID) cmdJSON, _ := json.Marshal(cmd) if err := hub.SendToAgent(agentID, cmdJSON); err != nil { log.Printf("Command '%s' senden fehlgeschlagen: %v", command, err) writeError(w, http.StatusInternalServerError, "Command senden fehlgeschlagen") return } log.Printf("Command '%s' (ID: %s) an Agent %s gesendet, warte auf Response...", command, cmdID, agentID) // Auf Response warten select { case resp := <-responseCh: writeJSON(w, http.StatusOK, resp) case <-time.After(time.Duration(timeoutSec) * time.Second): writeError(w, http.StatusGatewayTimeout, "Timeout: Agent hat nicht rechtzeitig geantwortet") } } func (h *Handler) hubStatus(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { stats := hub.GetAgentStats() connectedAgents := hub.GetConnectedAgents() response := map[string]interface{}{ "status": "running", "connected_agents": connectedAgents, "stats": stats, "timestamp": time.Now(), } writeJSON(w, http.StatusOK, response) } }