From e523fd0740df563fc4f0629c9a74df274e834271 Mon Sep 17 00:00:00 2001 From: Virgil Date: Sat, 4 Apr 2026 16:45:13 +0000 Subject: [PATCH] refactor(api): share monitoring route registration Co-Authored-By: Virgil --- api/router.go | 156 ++---------------------------------------- config_runtime.go | 9 ++- proxy_http_runtime.go | 16 ++++- runtime_support.go | 4 +- 4 files changed, 27 insertions(+), 158 deletions(-) diff --git a/api/router.go b/api/router.go index cb50ef5..cd368de 100644 --- a/api/router.go +++ b/api/router.go @@ -1,23 +1,15 @@ // Package api wires the monitoring endpoints onto an HTTP router. // -// api.RegisterRoutes(http.NewServeMux(), p) +// mux := http.NewServeMux() +// api.RegisterRoutes(mux, p) package api import ( - "encoding/json" - "net/http" - "strings" - "dappco.re/go/core/proxy" ) // Router is the minimal route-registration surface used by RegisterRoutes. -// -// mux := http.NewServeMux() -// api.RegisterRoutes(mux, p) -type Router interface { - HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) -} +type Router = proxy.RouteRegistrar type SummaryResponse = proxy.SummaryResponse type HashrateResponse = proxy.HashrateResponse @@ -30,145 +22,5 @@ type ResultsResponse = proxy.ResultsResponse // mux := http.NewServeMux() // api.RegisterRoutes(mux, p) func RegisterRoutes(router Router, proxyValue *proxy.Proxy) { - if router == nil || proxyValue == nil { - return - } - - router.HandleFunc("/1/summary", func(writer http.ResponseWriter, request *http.Request) { - if !allowRequest(writer, request, proxyValue.HTTPConfig()) { - return - } - summary := proxyValue.Summary() - response := SummaryResponse{ - Version: "1.0.0", - Mode: proxyValue.Mode(), - Hashrate: HashrateResponse{ - Total: summary.Hashrate, - }, - Miners: MinersCountResponse{ - Now: proxyValue.CurrentMiners(), - Max: proxyValue.MaxMiners(), - }, - Workers: uint64(len(proxyValue.Workers())), - Upstreams: func() UpstreamResponse { - upstreams := proxyValue.Upstreams() - ratio := 0.0 - if upstreams.Total > 0 { - ratio = float64(proxyValue.CurrentMiners()) / float64(upstreams.Total) - } - return UpstreamResponse{ - Active: upstreams.Active, - Sleep: upstreams.Sleep, - Error: upstreams.Error, - Total: upstreams.Total, - Ratio: ratio, - } - }(), - Results: ResultsResponse{ - Accepted: summary.Accepted, - Rejected: summary.Rejected, - Invalid: summary.Invalid, - Expired: summary.Expired, - AvgTime: summary.AvgTime, - Latency: summary.AvgLatency, - HashesTotal: summary.Hashes, - Best: summary.TopDiff, - }, - } - writeJSON(writer, response) - }) - - router.HandleFunc("/1/workers", func(writer http.ResponseWriter, request *http.Request) { - if !allowRequest(writer, request, proxyValue.HTTPConfig()) { - return - } - type responseBody struct { - Mode string `json:"mode"` - Workers [][]interface{} `json:"workers"` - } - - records := proxyValue.Workers() - rows := make([][]interface{}, 0, len(records)) - for _, record := range records { - rows = append(rows, []interface{}{ - record.Name, - record.LastIP, - record.Connections, - record.Accepted, - record.Rejected, - record.Invalid, - record.Hashes, - record.LastHashAt.Unix(), - record.Hashrate(60), - record.Hashrate(600), - record.Hashrate(3600), - record.Hashrate(43200), - record.Hashrate(86400), - }) - } - - writeJSON(writer, responseBody{ - Mode: proxyValue.WorkersMode(), - Workers: rows, - }) - }) - - router.HandleFunc("/1/miners", func(writer http.ResponseWriter, request *http.Request) { - if !allowRequest(writer, request, proxyValue.HTTPConfig()) { - return - } - miners := proxyValue.Miners() - rows := make([][]interface{}, 0, len(miners)) - for _, miner := range miners { - ip := "" - if remote := miner.RemoteAddr(); remote != nil { - ip = remote.String() - } - rows = append(rows, []interface{}{ - miner.ID(), - ip, - miner.TX(), - miner.RX(), - miner.State(), - miner.Diff(), - miner.User(), - "********", - miner.RigID(), - miner.Agent(), - }) - } - - writeJSON(writer, map[string]interface{}{ - "format": []string{"id", "ip", "tx", "rx", "state", "diff", "user", "password", "rig_id", "agent"}, - "miners": rows, - }) - }) -} - -func allowRequest(writer http.ResponseWriter, request *http.Request, config proxy.HTTPConfig) bool { - if request == nil { - return false - } - - if config.AccessToken != "" { - header := request.Header.Get("Authorization") - prefix := "Bearer " - if !strings.HasPrefix(header, prefix) || strings.TrimSpace(strings.TrimPrefix(header, prefix)) != config.AccessToken { - writer.Header().Set("WWW-Authenticate", "Bearer") - http.Error(writer, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return false - } - } - - if config.Restricted && request.Method != http.MethodGet { - http.Error(writer, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) - return false - } - - return true -} - -func writeJSON(writer http.ResponseWriter, value interface{}) { - writer.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(writer).Encode(value) + proxy.RegisterMonitoringRoutes(router, proxyValue) } diff --git a/config_runtime.go b/config_runtime.go index 2439c7c..5a3ef64 100644 --- a/config_runtime.go +++ b/config_runtime.go @@ -35,7 +35,10 @@ func LoadConfig(path string) (*Config, error) { // Validate checks that `bind` and `pools` are present and every enabled pool has a URL. // -// cfg := &proxy.Config{Bind: []proxy.BindAddr{{Host: "127.0.0.1", Port: 3333}}, Pools: []proxy.PoolConfig{{URL: "pool-a:3333", Enabled: true}}} +// cfg := &proxy.Config{ +// Bind: []proxy.BindAddr{{Host: "127.0.0.1", Port: 3333}}, +// Pools: []proxy.PoolConfig{{URL: "pool-a:3333", Enabled: true}}, +// } // if errorValue := cfg.Validate(); errorValue != nil { // return // } @@ -61,7 +64,9 @@ func (c *Config) Validate() error { // NewConfigWatcher watches a config file and reloads the proxy on modification. // -// w := proxy.NewConfigWatcher("config.json", func(cfg *proxy.Config) { p.Reload(cfg) }) +// w := proxy.NewConfigWatcher("config.json", func(cfg *proxy.Config) { +// p.Reload(cfg) +// }) func NewConfigWatcher(path string, onChange func(*Config)) *ConfigWatcher { return newConfigWatcher(path, onChange, true) } diff --git a/proxy_http_runtime.go b/proxy_http_runtime.go index b6c8862..f21db0e 100644 --- a/proxy_http_runtime.go +++ b/proxy_http_runtime.go @@ -12,6 +12,14 @@ import ( const proxyAPIVersion = "1.0.0" +// RouteRegistrar is the minimal route-registration surface used by RegisterMonitoringRoutes. +// +// mux := http.NewServeMux() +// RegisterMonitoringRoutes(mux, p) +type RouteRegistrar interface { + HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) +} + // SummaryResponse is the /1/summary JSON body. type SummaryResponse struct { Version string `json:"version"` @@ -61,7 +69,7 @@ func startHTTPServer(p *Proxy) { } mux := http.NewServeMux() - registerMonitoringRoutes(mux, p) + RegisterMonitoringRoutes(mux, p) address := net.JoinHostPort(p.config.HTTP.Host, strconv.Itoa(int(p.config.HTTP.Port))) listener, errorValue := net.Listen("tcp", address) @@ -92,7 +100,11 @@ func stopHTTPServer(p *Proxy) { _ = server.Shutdown(shutdownContext) } -func registerMonitoringRoutes(router *http.ServeMux, proxyValue *Proxy) { +// RegisterMonitoringRoutes mounts the monitoring endpoints on any router with HandleFunc. +// +// mux := http.NewServeMux() +// RegisterMonitoringRoutes(mux, p) +func RegisterMonitoringRoutes(router RouteRegistrar, proxyValue *Proxy) { if router == nil || proxyValue == nil { return } diff --git a/runtime_support.go b/runtime_support.go index 20a5066..0bf3bc9 100644 --- a/runtime_support.go +++ b/runtime_support.go @@ -8,7 +8,7 @@ import ( // NewRateLimiter creates a per-IP limiter, for example: // -// rl := proxy.NewRateLimiter(cfg.RateLimit) +// rl := proxy.NewRateLimiter(proxy.RateLimit{MaxConnectionsPerMinute: 30, BanDurationSeconds: 300}) func NewRateLimiter(config RateLimit) *RateLimiter { return &RateLimiter{ limitConfig: config, @@ -17,7 +17,7 @@ func NewRateLimiter(config RateLimit) *RateLimiter { } } -// SetConfig swaps in a live reload value such as `proxy.RateLimit{MaxConnectionsPerMinute: 30}`. +// SetConfig swaps in a live reload value such as: // // rl.SetConfig(proxy.RateLimit{MaxConnectionsPerMinute: 30, BanDurationSeconds: 300}) func (rateLimiter *RateLimiter) SetConfig(config RateLimit) {