102 lines
2.4 KiB
Go
102 lines
2.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"forge-mesh/internal/alerts"
|
|
"forge-mesh/internal/auth"
|
|
"forge-mesh/internal/config"
|
|
"forge-mesh/internal/fleet"
|
|
)
|
|
|
|
// FleetHandler serves fleet REST endpoints.
|
|
type FleetHandler struct {
|
|
Store *fleet.Store
|
|
Hub *fleet.Hub
|
|
Alerts *alerts.Notifier
|
|
Cfg *config.Config
|
|
OperatorClearance int
|
|
}
|
|
|
|
func (h *FleetHandler) List(w http.ResponseWriter, r *http.Request) {
|
|
summary, err := h.Store.BuildFleetSummary()
|
|
if err != nil {
|
|
http.Error(w, "internal error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
auth.JSON(w, http.StatusOK, summary)
|
|
}
|
|
|
|
type registerRequest struct {
|
|
Hostname string `json:"hostname"`
|
|
Fingerprint string `json:"fingerprint"`
|
|
Arch string `json:"arch"`
|
|
}
|
|
|
|
func (h *FleetHandler) Register(w http.ResponseWriter, r *http.Request) {
|
|
var req registerRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "bad request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if req.Hostname == "" {
|
|
req.Hostname = "enrolled-host"
|
|
}
|
|
|
|
host, err := h.Store.TouchHost(req.Hostname, req.Fingerprint, req.Arch)
|
|
if err != nil {
|
|
http.Error(w, "register failed", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
auth.JSON(w, http.StatusOK, map[string]any{
|
|
"ok": true,
|
|
"host_id": host.ID,
|
|
"host": fleet.ToFleetCard(host),
|
|
})
|
|
}
|
|
|
|
func (h *FleetHandler) Command(w http.ResponseWriter, r *http.Request) {
|
|
hostID := r.PathValue("id")
|
|
if hostID == "" {
|
|
http.Error(w, "host id required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Action string `json:"action"`
|
|
Args map[string]any `json:"args"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "bad request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
action := strings.ToLower(req.Action)
|
|
if err := fleet.CheckAction(h.OperatorClearance, action); err != nil {
|
|
auth.JSON(w, http.StatusForbidden, map[string]any{
|
|
"error": err.Error(),
|
|
"required_clearance": fleet.RequiredClearanceOrZero(action),
|
|
})
|
|
return
|
|
}
|
|
|
|
cmd, err := h.Hub.DispatchCommand(hostID, action, req.Args)
|
|
if err != nil {
|
|
http.Error(w, "dispatch failed", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if h.Alerts != nil && h.Alerts.Enabled() {
|
|
h.Alerts.Send(alerts.FleetEvent(action, hostID, fmt.Sprintf("cmd: `%s`", cmd.ID)))
|
|
}
|
|
|
|
auth.JSON(w, http.StatusOK, map[string]any{
|
|
"ok": true,
|
|
"command": cmd,
|
|
})
|
|
}
|