Files
AetherForge/server/internal/api/router.go
drjones 6c42f2b600 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.
2026-05-26 22:51:47 -07:00

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
}