191 lines
4.7 KiB
Go
191 lines
4.7 KiB
Go
package proxy
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const proxyAPIVersion = "1.0.0"
|
|
|
|
func startHTTPServer(p *Proxy) {
|
|
if p == nil || p.config == nil || !p.config.HTTP.Enabled || p.httpServer != nil {
|
|
return
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
|
registerMonitoringRoutes(mux, p)
|
|
|
|
address := net.JoinHostPort(p.config.HTTP.Host, strconv.Itoa(int(p.config.HTTP.Port)))
|
|
listener, errorValue := net.Listen("tcp", address)
|
|
if errorValue != nil {
|
|
return
|
|
}
|
|
|
|
server := &http.Server{
|
|
Handler: mux,
|
|
}
|
|
p.httpServer = server
|
|
|
|
go func() {
|
|
_ = server.Serve(listener)
|
|
}()
|
|
}
|
|
|
|
func stopHTTPServer(p *Proxy) {
|
|
if p == nil || p.httpServer == nil {
|
|
return
|
|
}
|
|
|
|
server := p.httpServer
|
|
p.httpServer = nil
|
|
|
|
shutdownContext, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
_ = server.Shutdown(shutdownContext)
|
|
}
|
|
|
|
func registerMonitoringRoutes(router *http.ServeMux, proxyValue *Proxy) {
|
|
if router == nil || proxyValue == nil {
|
|
return
|
|
}
|
|
|
|
router.HandleFunc("/1/summary", func(writer http.ResponseWriter, request *http.Request) {
|
|
if !allowHTTPRequest(writer, request, proxyValue.HTTPConfig()) {
|
|
return
|
|
}
|
|
summary := proxyValue.Summary()
|
|
response := map[string]interface{}{
|
|
"version": proxyAPIVersion,
|
|
"mode": proxyValue.Mode(),
|
|
"hashrate": map[string]interface{}{
|
|
"total": summary.Hashrate,
|
|
},
|
|
"miners": map[string]uint64{
|
|
"now": proxyValue.CurrentMiners(),
|
|
"max": proxyValue.MaxMiners(),
|
|
},
|
|
"workers": uint64(len(proxyValue.Workers())),
|
|
"upstreams": func() map[string]interface{} {
|
|
upstreams := proxyValue.Upstreams()
|
|
ratio := 0.0
|
|
if upstreams.Total > 0 {
|
|
ratio = float64(proxyValue.CurrentMiners()) / float64(upstreams.Total)
|
|
}
|
|
return map[string]interface{}{
|
|
"active": upstreams.Active,
|
|
"sleep": upstreams.Sleep,
|
|
"error": upstreams.Error,
|
|
"total": upstreams.Total,
|
|
"ratio": ratio,
|
|
}
|
|
}(),
|
|
"results": map[string]interface{}{
|
|
"accepted": summary.Accepted,
|
|
"rejected": summary.Rejected,
|
|
"invalid": summary.Invalid,
|
|
"expired": summary.Expired,
|
|
"avg_time": summary.AvgTime,
|
|
"latency": summary.AvgLatency,
|
|
"hashes_total": summary.Hashes,
|
|
"best": summary.TopDiff,
|
|
},
|
|
}
|
|
writeHTTPJSON(writer, response)
|
|
})
|
|
|
|
router.HandleFunc("/1/workers", func(writer http.ResponseWriter, request *http.Request) {
|
|
if !allowHTTPRequest(writer, request, proxyValue.HTTPConfig()) {
|
|
return
|
|
}
|
|
|
|
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),
|
|
})
|
|
}
|
|
|
|
writeHTTPJSON(writer, map[string]interface{}{
|
|
"mode": proxyValue.WorkersMode(),
|
|
"workers": rows,
|
|
})
|
|
})
|
|
|
|
router.HandleFunc("/1/miners", func(writer http.ResponseWriter, request *http.Request) {
|
|
if !allowHTTPRequest(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(),
|
|
})
|
|
}
|
|
|
|
writeHTTPJSON(writer, map[string]interface{}{
|
|
"format": []string{"id", "ip", "tx", "rx", "state", "diff", "user", "password", "rig_id", "agent"},
|
|
"miners": rows,
|
|
})
|
|
})
|
|
}
|
|
|
|
func allowHTTPRequest(writer http.ResponseWriter, request *http.Request, config 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 writeHTTPJSON(writer http.ResponseWriter, value interface{}) {
|
|
writer.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(writer).Encode(value)
|
|
}
|