package api import ( "encoding/json" "net/http" "strconv" "strings" fleetai "crypto-miner-server/internal/ai" "crypto-miner-server/internal/db" ) // FleetAIHandler serves Fleet AI Control API routes. type FleetAIHandler struct { config FleetAIConfigSource db interface { ListAIDecisions(agentID string, limit int) ([]db.AIDecisionRecord, error) } } func NewFleetAIHandler(cfg FleetAIConfigSource, database interface { ListAIDecisions(agentID string, limit int) ([]db.AIDecisionRecord, error) }) *FleetAIHandler { return &FleetAIHandler{config: cfg, db: database} } func (h *FleetAIHandler) GetConfig(w http.ResponseWriter, r *http.Request) { if h.config == nil { http.Error(w, "config unavailable", http.StatusServiceUnavailable) return } writeJSON(w, h.config.GetFleetAIConfig()) } func (h *FleetAIHandler) PutConfig(w http.ResponseWriter, r *http.Request) { if h.config == nil { http.Error(w, "config unavailable", http.StatusServiceUnavailable) return } var body FleetAIConfigView if err := json.NewDecoder(r.Body).Decode(&body); err != nil { http.Error(w, "invalid JSON", http.StatusBadRequest) return } if body.AIDecisionIntervalSec < 0 { http.Error(w, "ai_decision_interval_sec must be ≥ 0", http.StatusBadRequest) return } if err := h.config.UpdateFleetAIConfig(body); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, h.config.GetFleetAIConfig()) } func (h *FleetAIHandler) GetModels(w http.ResponseWriter, r *http.Request) { endpoint := strings.TrimSpace(r.URL.Query().Get("endpoint")) if endpoint == "" && h.config != nil { endpoint = h.config.GetFleetAIConfig().AIEndpoint } models, err := fleetai.ListModels(r.Context(), endpoint) if err != nil { http.Error(w, err.Error(), http.StatusBadGateway) return } writeJSON(w, map[string]interface{}{"models": models, "endpoint": endpoint}) } func (h *FleetAIHandler) GetDecisions(w http.ResponseWriter, r *http.Request) { if h.db == nil { writeJSON(w, []db.AIDecisionRecord{}) return } agentID := strings.TrimSpace(r.URL.Query().Get("agent_id")) limit := 50 if raw := r.URL.Query().Get("limit"); raw != "" { if n, err := strconv.Atoi(raw); err == nil && n > 0 { limit = n } } rows, err := h.db.ListAIDecisions(agentID, limit) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if rows == nil { rows = []db.AIDecisionRecord{} } writeJSON(w, rows) }