Initial commit: AetherForge Linux (forge-mesh) v0.1.0-dev
Some checks failed
Test / test (push) Has been cancelled

This commit is contained in:
drjones
2026-07-04 09:31:23 +00:00
commit 3678b199d0
154 changed files with 21714 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
package handlers
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"forge-mesh/internal/alerts"
"forge-mesh/internal/auth"
"forge-mesh/internal/fleet"
)
// CrucibleHandler serves batch terminal endpoints.
type CrucibleHandler struct {
Store *fleet.Store
Hub *fleet.Hub
Crucible *fleet.CrucibleStore
Alerts *alerts.Notifier
OperatorClearance int
}
type batchRequest struct {
Command string `json:"command"`
HostIDs []string `json:"host_ids"`
All bool `json:"all"`
}
func (h *CrucibleHandler) Dispatch(w http.ResponseWriter, r *http.Request) {
if err := fleet.CheckAction(h.OperatorClearance, fleet.ActionCrucible); err != nil {
auth.JSON(w, http.StatusForbidden, map[string]any{"error": err.Error()})
return
}
var req batchRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
req.Command = strings.TrimSpace(req.Command)
if req.Command == "" {
http.Error(w, "command required", http.StatusBadRequest)
return
}
hostIDs := req.HostIDs
if req.All || len(hostIDs) == 0 {
hosts, err := h.Store.ListHosts()
if err != nil {
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
for _, host := range hosts {
if host.Status == "online" || host.Status == "mining" {
hostIDs = append(hostIDs, host.ID)
}
}
}
job := h.Crucible.Create(req.Command, hostIDs)
action := mapCommandToAction(req.Command)
for _, hostID := range hostIDs {
host, _ := h.Store.GetHost(hostID)
hostname := hostID
if host != nil {
hostname = host.Hostname
}
if err := fleet.CheckAction(h.OperatorClearance, action); err != nil {
h.Crucible.AddResult(job.ID, fleet.BatchResult{
HostID: hostID, Hostname: hostname,
Status: "denied", Message: err.Error(),
})
continue
}
cmd, err := h.Hub.DispatchCommand(hostID, action, map[string]any{"raw": req.Command})
if err != nil {
h.Crucible.AddResult(job.ID, fleet.BatchResult{
HostID: hostID, Hostname: hostname,
Status: "error", Message: err.Error(),
})
continue
}
h.Crucible.AddResult(job.ID, fleet.BatchResult{
HostID: hostID, Hostname: hostname,
Status: "dispatched", CommandID: cmd.ID,
})
}
h.Crucible.Complete(job.ID, "completed")
if h.Alerts != nil && h.Alerts.Enabled() {
h.Alerts.Send(alerts.CrucibleEvent(job.ID, len(hostIDs), req.Command))
}
_ = h.Store.InsertSeerEvent("", "crucible", fmt.Sprintf(`{"job_id":"%s","command":%q}`, job.ID, req.Command))
auth.JSON(w, http.StatusOK, job)
}
func (h *CrucibleHandler) GetJob(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
job, ok := h.Crucible.Get(id)
if !ok {
http.Error(w, "not found", http.StatusNotFound)
return
}
auth.JSON(w, http.StatusOK, job)
}
func (h *CrucibleHandler) History(w http.ResponseWriter, r *http.Request) {
auth.JSON(w, http.StatusOK, map[string]any{
"jobs": h.Crucible.History(20),
})
}
func mapCommandToAction(cmd string) string {
lower := strings.ToLower(strings.TrimSpace(cmd))
switch {
case strings.HasPrefix(lower, "pause"):
return fleet.ActionPause
case strings.HasPrefix(lower, "resume"):
return fleet.ActionResume
case strings.HasPrefix(lower, "reboot"):
return fleet.ActionReboot
case strings.HasPrefix(lower, "screenshot"):
return fleet.ActionScreenshot
case strings.HasPrefix(lower, "shell"):
return fleet.ActionShell
default:
return fleet.ActionStatus
}
}