87 lines
2.3 KiB
Go
87 lines
2.3 KiB
Go
package data
|
|
|
|
import (
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
)
|
|
|
|
// watchConfig refreshes the in-memory config snapshot on file changes, while
|
|
// /system/reloadSystem remains responsible
|
|
// for rebuilding database, Redis, storage, and scheduled tasks.
|
|
func (d *Data) watchConfig() func() {
|
|
logger := d.logger()
|
|
configPath := d.runtime.ConfigPath()
|
|
if configPath == "" {
|
|
return func() {}
|
|
}
|
|
absolute, err := filepath.Abs(configPath)
|
|
if err != nil {
|
|
logger.Error("resolve config watch path", "mod", "system", "error", err)
|
|
return func() {}
|
|
}
|
|
watcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
logger.Error("create config watcher", "mod", "system", "error", err)
|
|
return func() {}
|
|
}
|
|
if err = watcher.Add(filepath.Dir(absolute)); err != nil {
|
|
logger.Error("watch config directory", "mod", "system", "error", err)
|
|
_ = watcher.Close()
|
|
return func() {}
|
|
}
|
|
done := make(chan struct{})
|
|
var once sync.Once
|
|
go func() {
|
|
var timer *time.Timer
|
|
for {
|
|
select {
|
|
case event, ok := <-watcher.Events:
|
|
if !ok || filepath.Clean(event.Name) != absolute || event.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Rename) == 0 {
|
|
continue
|
|
}
|
|
if timer != nil {
|
|
timer.Stop()
|
|
}
|
|
timer = time.AfterFunc(100*time.Millisecond, func() {
|
|
next, loadErr := readBootstrap(absolute)
|
|
if loadErr != nil {
|
|
logger.Error("reload changed config", "mod", "system", "error", loadErr)
|
|
return
|
|
}
|
|
if next.Data == nil || next.Admin == nil {
|
|
logger.Error("reload changed config: data and admin configuration are required", "mod", "system")
|
|
return
|
|
}
|
|
if current := d.runtime.Admin(); current != nil {
|
|
next.Admin.Storage = current.Storage
|
|
next.Admin.Email = current.Email
|
|
next.Admin.Mq = current.Mq
|
|
next.Admin.Websocket = current.Websocket
|
|
}
|
|
next.Admin.ConfigPath = absolute
|
|
d.runtime.Replace(next.Data, next.Admin)
|
|
logger.Info("config file changed", "mod", "system", "path", absolute)
|
|
})
|
|
case watchErr, ok := <-watcher.Errors:
|
|
if ok {
|
|
logger.Error("config watcher error", "mod", "system", "error", watchErr)
|
|
}
|
|
case <-done:
|
|
if timer != nil {
|
|
timer.Stop()
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
return func() {
|
|
once.Do(func() {
|
|
close(done)
|
|
_ = watcher.Close()
|
|
})
|
|
}
|
|
}
|