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:
107
server/internal/api/router.go
Normal file
107
server/internal/api/router.go
Normal file
@@ -0,0 +1,107 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user