Pulse/internal/api/docker_agents.go
2025-10-11 23:29:47 +00:00

106 lines
3.2 KiB
Go

package api
import (
"encoding/json"
"net/http"
"strings"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
"github.com/rcourtman/pulse-go-rewrite/internal/utils"
"github.com/rcourtman/pulse-go-rewrite/internal/websocket"
agentsdocker "github.com/rcourtman/pulse-go-rewrite/pkg/agents/docker"
"github.com/rs/zerolog/log"
)
// DockerAgentHandlers manages ingest from the external Docker agent.
type DockerAgentHandlers struct {
monitor *monitoring.Monitor
wsHub *websocket.Hub
}
// NewDockerAgentHandlers constructs a new Docker agent handler group.
func NewDockerAgentHandlers(m *monitoring.Monitor, hub *websocket.Hub) *DockerAgentHandlers {
return &DockerAgentHandlers{monitor: m, wsHub: hub}
}
// SetMonitor updates the monitor reference for docker agent handlers.
func (h *DockerAgentHandlers) SetMonitor(m *monitoring.Monitor) {
h.monitor = m
}
// HandleReport accepts heartbeat payloads from the Docker agent.
func (h *DockerAgentHandlers) HandleReport(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeErrorResponse(w, http.StatusMethodNotAllowed, "method_not_allowed", "Only POST is allowed", nil)
return
}
defer r.Body.Close()
var report agentsdocker.Report
if err := json.NewDecoder(r.Body).Decode(&report); err != nil {
writeErrorResponse(w, http.StatusBadRequest, "invalid_json", "Failed to decode request body", map[string]string{"error": err.Error()})
return
}
if report.Timestamp.IsZero() {
report.Timestamp = time.Now()
}
host, err := h.monitor.ApplyDockerReport(report)
if err != nil {
writeErrorResponse(w, http.StatusBadRequest, "invalid_report", err.Error(), nil)
return
}
log.Debug().
Str("dockerHost", host.Hostname).
Int("containers", len(host.Containers)).
Msg("Docker agent report processed")
// Broadcast the updated state for near-real-time UI updates
go h.wsHub.BroadcastState(h.monitor.GetState().ToFrontend())
response := map[string]any{
"success": true,
"hostId": host.ID,
"containers": len(host.Containers),
"lastSeen": host.LastSeen,
}
if err := utils.WriteJSONResponse(w, response); err != nil {
log.Error().Err(err).Msg("Failed to serialize docker agent response")
}
}
// HandleDeleteHost removes a docker host and its containers from the shared state.
func (h *DockerAgentHandlers) HandleDeleteHost(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodDelete {
writeErrorResponse(w, http.StatusMethodNotAllowed, "method_not_allowed", "Only DELETE is allowed", nil)
return
}
trimmedPath := strings.TrimPrefix(r.URL.Path, "/api/agents/docker/hosts/")
hostID := strings.TrimSpace(trimmedPath)
if hostID == "" {
writeErrorResponse(w, http.StatusBadRequest, "missing_host_id", "Docker host ID is required", nil)
return
}
host, err := h.monitor.RemoveDockerHost(hostID)
if err != nil {
writeErrorResponse(w, http.StatusNotFound, "docker_host_not_found", err.Error(), nil)
return
}
go h.wsHub.BroadcastState(h.monitor.GetState().ToFrontend())
if err := utils.WriteJSONResponse(w, map[string]any{
"success": true,
"hostId": host.ID,
"message": "Docker host removed",
}); err != nil {
log.Error().Err(err).Msg("Failed to serialize docker host removal response")
}
}