Implement Windows agent with RandomX mining and WebSocket fleet reporting, wire dashboard settings into the builder with saved exe paths, and add project README.
108 lines
3.0 KiB
Go
108 lines
3.0 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/go-chi/cors"
|
|
"crypto-miner-server/internal/db"
|
|
"crypto-miner-server/internal/builder"
|
|
)
|
|
|
|
func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler, builderHandler *builder.Handler, webRoot string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
// Middleware
|
|
r.Use(middleware.Logger)
|
|
r.Use(middleware.Recoverer)
|
|
r.Use(cors.Handler(cors.Options{
|
|
AllowedOrigins: []string{"*"},
|
|
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
|
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
|
|
AllowCredentials: true,
|
|
}))
|
|
|
|
// REST API
|
|
r.Route("/api/v1", func(r chi.Router) {
|
|
h := NewHandler(database)
|
|
|
|
r.Get("/health", h.HealthCheck)
|
|
|
|
// Dashboard
|
|
r.Get("/dashboard/stats", h.GetDashboardStats)
|
|
|
|
// Agents
|
|
r.Get("/agents", h.ListAgents)
|
|
r.Get("/agents/{id}", h.GetAgent)
|
|
r.Get("/agents/{id}/stats", h.GetAgentStats)
|
|
|
|
// Shares
|
|
r.Get("/shares", h.GetRecentShares)
|
|
|
|
// Builds
|
|
r.Get("/builds", h.ListBuilds)
|
|
r.Get("/builds/{id}/download", builderHandler.DownloadBuild)
|
|
|
|
// Config
|
|
r.Get("/config", configHandler.ServeHTTP)
|
|
r.Put("/config", configHandler.ServeHTTP)
|
|
|
|
// Builder
|
|
r.Post("/builder/build", builderHandler.ServeHTTP)
|
|
})
|
|
|
|
// WebSocket
|
|
r.Get("/ws/agent", wsHub.HandleAgentWS)
|
|
r.Get("/ws/dashboard", wsHub.HandleDashboardWS)
|
|
|
|
// Serve frontend SPA
|
|
if webRoot != "" {
|
|
// Check if webroot directory exists
|
|
if info, err := os.Stat(webRoot); err == nil && info.IsDir() {
|
|
// Create a file server for the webroot
|
|
fileServer := http.FileServer(http.Dir(webRoot))
|
|
|
|
// SPA fallback: serve index.html for all non-API, non-WebSocket routes
|
|
r.Get("/*", func(w http.ResponseWriter, r *http.Request) {
|
|
// Clean the path
|
|
path := strings.TrimPrefix(r.URL.Path, "/")
|
|
fullPath := filepath.Join(webRoot, path)
|
|
|
|
// Check if the file exists
|
|
if _, err := os.Stat(fullPath); err == nil {
|
|
fileServer.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
// SPA fallback - serve index.html
|
|
http.ServeFile(w, r, filepath.Join(webRoot, "index.html"))
|
|
})
|
|
} else {
|
|
// Fallback if webroot doesn't exist
|
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "text/html")
|
|
w.Write([]byte(`<!DOCTYPE html><html><head><title>Crypto Miner</title></head><body>
|
|
<h1>Crypto Miner Control Server</h1>
|
|
<p>Server is running. Build the frontend with <code>cd server/web && npm install && npm run build</code></p>
|
|
<p>API: <a href="/api/v1/health">/api/v1/health</a></p>
|
|
</body></html>`))
|
|
})
|
|
}
|
|
} else {
|
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "text/html")
|
|
w.Write([]byte(`<!DOCTYPE html><html><head><title>Crypto Miner</title></head><body>
|
|
<h1>Crypto Miner Control Server</h1>
|
|
<p>Server is running. No frontend configured.</p>
|
|
<p>API: <a href="/api/v1/health">/api/v1/health</a></p>
|
|
</body></html>`))
|
|
})
|
|
}
|
|
|
|
return r
|
|
}
|