go-proxy/config_runtime.go
Virgil 9e997554fa fix(config): validate mode and workers enums
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-04 17:14:54 +00:00

148 lines
3.2 KiB
Go

package proxy
import (
"encoding/json"
"errors"
"os"
"strings"
"time"
)
// LoadConfig reads a JSON config file and validates the result.
//
// cfg, errorValue := proxy.LoadConfig("config.json")
// if errorValue != nil {
// return
// }
func LoadConfig(path string) (*Config, error) {
data, errorValue := os.ReadFile(path)
if errorValue != nil {
return nil, errorValue
}
config := &Config{}
if errorValue = json.Unmarshal(data, config); errorValue != nil {
return nil, errorValue
}
config.sourcePath = path
if errorValue = config.Validate(); errorValue != nil {
return nil, errorValue
}
return config, nil
}
// 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}},
// }
// if errorValue := cfg.Validate(); errorValue != nil {
// return
// }
func (c *Config) Validate() error {
if c == nil {
return errors.New("config is nil")
}
if c.Mode != "" && c.Mode != "nicehash" && c.Mode != "simple" {
return errors.New("mode is invalid")
}
if c.Workers != "" &&
c.Workers != WorkersByRigID &&
c.Workers != WorkersByUser &&
c.Workers != WorkersByPass &&
c.Workers != WorkersByPassword &&
c.Workers != WorkersByAgent &&
c.Workers != WorkersByIP &&
c.Workers != WorkersDisabled {
return errors.New("workers mode is invalid")
}
if len(c.Bind) == 0 {
return errors.New("bind list is empty")
}
if len(c.Pools) == 0 {
return errors.New("pool list is empty")
}
for _, poolConfig := range c.Pools {
if poolConfig.Enabled && strings.TrimSpace(poolConfig.URL) == "" {
return errors.New("enabled pool URL is empty")
}
}
return nil
}
// NewConfigWatcher watches a config file and reloads the proxy on modification.
//
// 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)
}
func newConfigWatcher(path string, onChange func(*Config), enabled bool) *ConfigWatcher {
return &ConfigWatcher{
path: path,
onChange: onChange,
enabled: enabled,
done: make(chan struct{}),
}
}
// Start begins 1-second polling for `config.json`.
//
// w.Start()
func (w *ConfigWatcher) Start() {
if w == nil || !w.enabled {
return
}
go func() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
if info, errorValue := os.Stat(w.path); errorValue == nil {
w.lastModifiedAt = info.ModTime()
}
for {
select {
case <-ticker.C:
info, errorValue := os.Stat(w.path)
if errorValue != nil {
continue
}
if !info.ModTime().After(w.lastModifiedAt) {
continue
}
w.lastModifiedAt = info.ModTime()
config, errorValue := LoadConfig(w.path)
if errorValue == nil && w.onChange != nil {
w.onChange(config)
}
case <-w.done:
return
}
}
}()
}
// Stop ends polling so the watcher can be shut down with `p.Stop()`.
//
// w.Stop()
func (w *ConfigWatcher) Stop() {
if w == nil || w.done == nil {
return
}
select {
case <-w.done:
default:
close(w.done)
}
}