From 1600007f70060d680cf008db278caec9b859da0c Mon Sep 17 00:00:00 2001 From: teemozhu Date: Mon, 20 May 2024 14:37:08 +0800 Subject: [PATCH] feat: monitor glance.yml for changes --- .gitignore | 2 ++ go.mod | 2 ++ go.sum | 4 +++ internal/glance/config.go | 57 +++++++++++++++++++++++++++++++++++++++ internal/glance/glance.go | 19 ++++++++++--- internal/glance/main.go | 19 +++++-------- 6 files changed, 87 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 062999d..b626d4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /assets /build /playground +.idea +.vscode glance.yml diff --git a/go.mod b/go.mod index b4a1e72..1ecc6f9 100644 --- a/go.mod +++ b/go.mod @@ -11,9 +11,11 @@ require ( require ( github.com/PuerkitoBio/goquery v1.9.1 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mmcdole/goxpp v1.1.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect ) diff --git a/go.sum b/go.sum index 54489e6..65dcbde 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -45,6 +47,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/internal/glance/config.go b/internal/glance/config.go index 9acc3be..ec99fad 100644 --- a/internal/glance/config.go +++ b/internal/glance/config.go @@ -1,8 +1,11 @@ package glance import ( + "errors" "fmt" + "github.com/fsnotify/fsnotify" "io" + "os" "gopkg.in/yaml.v3" ) @@ -35,6 +38,19 @@ func NewConfigFromYml(contents io.Reader) (*Config, error) { return config, nil } +// NewConfigFromFile reads a yaml file and returns a Config struct +func NewConfigFromFile(path string) (*Config, error) { + configFile, err := os.Open(path) + + if err != nil { + return nil, errors.New("failed opening config file: " + err.Error()) + } + + defer configFile.Close() + + return NewConfigFromYml(configFile) +} + func NewConfig() *Config { config := &Config{} @@ -77,3 +93,44 @@ func configIsValid(config *Config) error { return nil } + +func watchConfigFile(configPath string, f func(*Config) error) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + fmt.Printf("failed creating watcher: %v\n", err) + return + } + + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + if event.Op&fsnotify.Write == fsnotify.Write { + config, err := NewConfigFromFile(configPath) + if err != nil { + fmt.Printf("failed loading config file: %v\n", err) + return + } + err = f(config) + if err != nil { + fmt.Printf("failed applying new config: %v\n", err) + } + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + _ = watcher.Close() + fmt.Printf("watcher error: %v\n", err) + } + } + }() + + err = watcher.Add(configPath) + if err != nil { + fmt.Printf("failed adding watcher: %v\n", err) + } +} diff --git a/internal/glance/glance.go b/internal/glance/glance.go index 653be75..ab1c625 100644 --- a/internal/glance/glance.go +++ b/internal/glance/glance.go @@ -97,6 +97,10 @@ func titleToSlug(s string) string { } func NewApplication(config *Config) (*Application, error) { + // make sure the config is valid + if config == nil { + return nil, fmt.Errorf("config is nil") + } if len(config.Pages) == 0 { return nil, fmt.Errorf("no pages configured") } @@ -107,17 +111,24 @@ func NewApplication(config *Config) (*Application, error) { slugToPage: make(map[string]*Page), } - app.slugToPage[""] = &config.Pages[0] + app.Reload(config) + + return app, nil +} + +// Reload updates the application with a new config +func (a *Application) Reload(config *Config) { + a.Config = *config + + a.slugToPage[""] = &config.Pages[0] for i := range config.Pages { if config.Pages[i].Slug == "" { config.Pages[i].Slug = titleToSlug(config.Pages[i].Title) } - app.slugToPage[config.Pages[i].Slug] = &config.Pages[i] + a.slugToPage[config.Pages[i].Slug] = &config.Pages[i] } - - return app, nil } func (a *Application) HandlePageRequest(w http.ResponseWriter, r *http.Request) { diff --git a/internal/glance/main.go b/internal/glance/main.go index 5eee962..4bd37d9 100644 --- a/internal/glance/main.go +++ b/internal/glance/main.go @@ -2,7 +2,6 @@ package glance import ( "fmt" - "os" ) func Main() int { @@ -13,18 +12,9 @@ func Main() int { return 1 } - configFile, err := os.Open(options.ConfigPath) - + config, err := NewConfigFromFile(options.ConfigPath) if err != nil { - fmt.Printf("failed opening config file: %v\n", err) - return 1 - } - - config, err := NewConfigFromYml(configFile) - configFile.Close() - - if err != nil { - fmt.Printf("failed parsing config file: %v\n", err) + fmt.Printf("failed loading config file: %v\n", err) return 1 } @@ -36,6 +26,11 @@ func Main() int { return 1 } + watchConfigFile(options.ConfigPath, func(config *Config) error { + app.Reload(config) + return nil + }) + if app.Serve() != nil { fmt.Printf("http server error: %v\n", err) return 1