package main import ( "log" "os" "strings" "github.com/fsnotify/fsnotify" "gopkg.in/yaml.v3" ) type Configuration struct { Paths map[string]PathConfiguration `yaml:"paths"` } type PathConfiguration struct { Verify string `yaml:"verify"` Secret string `yaml:"secret"` SignatureHeader string `yaml:"signature_header"` SubscribeSecret string `yaml:"subscribe_secret"` } func LoadConfiguration(path string) (*Configuration, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } expanded := os.ExpandEnv(string(data)) var cfg Configuration if err := yaml.Unmarshal([]byte(expanded), &cfg); err != nil { return nil, err } return &cfg, nil } var newWatcher = fsnotify.NewWatcher func WatchConfiguration(path string, callback func(*Configuration)) (func(), error) { watcher, err := newWatcher() if err != nil { return nil, err } if err := watcher.Add(path); err != nil { watcher.Close() return nil, err } done := make(chan struct{}) go func() { defer watcher.Close() for { select { case <-done: return case event := <-watcher.Events: if event.Has(fsnotify.Write) || event.Has(fsnotify.Create) { cfg, err := LoadConfiguration(path) if err != nil { log.Printf("reloading configuration: %v", err) continue } callback(cfg) } } } }() return func() { close(done) }, nil } func (c *Configuration) LookupSubscribeSecret(path string) string { if c == nil { return "" } for { if pc, ok := c.Paths[path]; ok && pc.SubscribeSecret != "" { return pc.SubscribeSecret } i := strings.LastIndex(path, "/") if i < 0 { break } path = path[:i] } return "" } func (c *Configuration) LookupVerification(path string) *PathConfiguration { if c == nil { return nil } for { if pc, ok := c.Paths[path]; ok && pc.Verify != "" { return &pc } i := strings.LastIndex(path, "/") if i < 0 { break } path = path[:i] } return nil }