115 lines
2.1 KiB
Go
115 lines
2.1 KiB
Go
package proxy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// LoadConfig reads and unmarshals a JSON config file.
|
|
//
|
|
// cfg, errorValue := proxy.LoadConfig("config.json")
|
|
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
|
|
}
|
|
|
|
if errorValue = config.Validate(); errorValue != nil {
|
|
return nil, errorValue
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// Validate checks required fields.
|
|
//
|
|
// if errorValue := cfg.Validate(); errorValue != nil { return errorValue }
|
|
func (c *Config) Validate() error {
|
|
if c == nil {
|
|
return errors.New("config is nil")
|
|
}
|
|
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 builds a polling watcher for the config file.
|
|
//
|
|
// w := proxy.NewConfigWatcher("config.json", func(cfg *proxy.Config) { p.Reload(cfg) })
|
|
func NewConfigWatcher(path string, onChange func(*Config)) *ConfigWatcher {
|
|
return &ConfigWatcher{
|
|
path: path,
|
|
onChange: onChange,
|
|
done: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// Start begins the polling goroutine.
|
|
//
|
|
// w.Start()
|
|
func (w *ConfigWatcher) Start() {
|
|
if w == nil {
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
ticker := time.NewTicker(time.Second)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
info, errorValue := os.Stat(w.path)
|
|
if errorValue != nil {
|
|
continue
|
|
}
|
|
|
|
if !info.ModTime().After(w.lastMod) {
|
|
continue
|
|
}
|
|
w.lastMod = info.ModTime()
|
|
|
|
config, errorValue := LoadConfig(w.path)
|
|
if errorValue == nil && w.onChange != nil {
|
|
w.onChange(config)
|
|
}
|
|
case <-w.done:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Stop ends the polling goroutine cleanly.
|
|
//
|
|
// w.Stop()
|
|
func (w *ConfigWatcher) Stop() {
|
|
if w == nil || w.done == nil {
|
|
return
|
|
}
|
|
|
|
select {
|
|
case <-w.done:
|
|
default:
|
|
close(w.done)
|
|
}
|
|
}
|