Complete private Monero miner control stack.

Implement Windows agent with RandomX mining and WebSocket fleet reporting, wire dashboard settings into the builder with saved exe paths, and add project README.
This commit is contained in:
drjones
2026-05-26 22:51:47 -07:00
commit 6c42f2b600
48 changed files with 10001 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
package api
import (
"encoding/json"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"crypto-miner-server/internal/db"
"crypto-miner-server/internal/models"
)
type Handler struct {
db *db.Database
}
func NewHandler(database *db.Database) *Handler {
return &Handler{db: database}
}
// GET /api/v1/dashboard/stats
func (h *Handler) GetDashboardStats(w http.ResponseWriter, r *http.Request) {
stats, err := h.db.GetFleetStats()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
writeJSON(w, stats)
}
// GET /api/v1/agents
func (h *Handler) ListAgents(w http.ResponseWriter, r *http.Request) {
agents, err := h.db.ListAgents()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if agents == nil {
agents = []*models.Agent{}
}
writeJSON(w, agents)
}
// GET /api/v1/agents/{id}
func (h *Handler) GetAgent(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
agent, err := h.db.GetAgent(id)
if err != nil {
http.Error(w, "Agent not found", http.StatusNotFound)
return
}
writeJSON(w, agent)
}
// GET /api/v1/agents/{id}/stats
func (h *Handler) GetAgentStats(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
limitStr := r.URL.Query().Get("limit")
limit := 100
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
limit = l
}
samples, err := h.db.GetHashrateHistory(id, limit)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if samples == nil {
samples = []*models.HashrateSample{}
}
writeJSON(w, samples)
}
// GET /api/v1/shares
func (h *Handler) GetRecentShares(w http.ResponseWriter, r *http.Request) {
limitStr := r.URL.Query().Get("limit")
limit := 50
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
limit = l
}
shares, err := h.db.GetRecentShares(limit)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if shares == nil {
shares = []*models.Share{}
}
writeJSON(w, shares)
}
// GET /api/v1/health
func (h *Handler) HealthCheck(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]string{"status": "ok"})
}
// GET /api/v1/builds
func (h *Handler) ListBuilds(w http.ResponseWriter, r *http.Request) {
builds, err := h.db.ListBuilds(50)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if builds == nil {
builds = []*models.BuildRecord{}
}
writeJSON(w, builds)
}
func writeJSON(w http.ResponseWriter, v interface{}) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(v)
}