Files
FlexDXClusterGui/config.go
2025-11-02 11:36:37 +01:00

227 lines
5.8 KiB
Go

package main
import (
"fmt"
"os"
"sync"
"github.com/fsnotify/fsnotify"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)
var Cfg *Config
type Config struct {
General struct {
DeleteLogFileAtStart bool `yaml:"delete_log_file_at_start"`
LogToFile bool `yaml:"log_to_file"`
Callsign string `yaml:"callsign"`
LogLevel string `yaml:"log_level"`
TelnetServer bool `yaml:"telnetserver"`
FlexRadioSpot bool `yaml:"flexradiospot"`
SendFreqModeToLog bool `yaml:"sendFreqModeToLog4OM"`
SpotColorNewEntity string `yaml:"spot_color_new_entity"`
BackgroundColorNewEntity string `yaml:"background_color_new_entity"`
SpotColorNewBand string `yaml:"spot_color_new_band"`
BackgroundColorNewBand string `yaml:"background_color_new_band"`
SpotColorNewMode string `yaml:"spot_color_new_mode"`
BackgroundColorNewMode string `yaml:"background_color_new_mode"`
SpotColorNewBandMode string `yaml:"spot_color_new_band_mode"`
BackgroundColorNewBandMode string `yaml:"background_color_new_band_mode"`
SpotColorNewSlot string `yaml:"spot_color_new_slot"`
BackgroundColorNewSlot string `yaml:"background_color_new_slot"`
SpotColorMyCallsign string `yaml:"spot_color_my_callsign"`
BackgroundColorMyCallsign string `yaml:"background_color_my_callsign"`
SpotColorWorked string `yaml:"spot_color_worked"`
BackgroundColorWorked string `yaml:"background_color_worked"`
} `yaml:"general"`
Database struct {
MySQL bool `yaml:"mysql"`
SQLite bool `yaml:"sqlite"`
MySQLUser string `yaml:"mysql_db_user"`
MySQLPassword string `yaml:"mysql_db_password"`
MySQLDbName string `yaml:"mysql_db_name"`
MySQLHost string `yaml:"mysql_host"`
MySQLPort string `yaml:"mysql_port"`
} `yaml:"database"`
SQLite struct {
SQLitePath string `yaml:"sqlite_path"`
} `yaml:"sqlite"`
Cluster struct {
Server string `yaml:"server"`
Port string `yaml:"port"`
Login string `yaml:"login"`
Password string `yaml:"password"`
Skimmer bool `yaml:"skimmer"`
FT8 bool `yaml:"ft8"`
FT4 bool `yaml:"ft4"`
Beacon bool `yaml:"beacon"`
Command string `yaml:"command"`
LoginPrompt string `yaml:"login_prompt"`
} `yaml:"cluster"`
Flex struct {
Discover bool `yaml:"discovery"`
IP string `yaml:"ip"`
SpotLife string `yaml:"spot_life"`
} `yaml:"flex"`
TelnetServer struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
} `yaml:"telnetserver"`
Gotify struct {
Enable bool `yaml:"enable"`
URL string `yaml:"url"`
Token string `yaml:"token"`
NewDXCC bool `yaml:"NewDXCC"`
NewBand bool `yaml:"NewBand"`
NewMode bool `yaml:"NewMode"`
NewBandAndMode bool `yaml:"NewBandAndMode"`
WatchList bool `yaml:"Watchlist"`
} `yaml:"gotify"`
}
type ConfigWatcher struct {
watcher *fsnotify.Watcher
configPath string
mu sync.RWMutex
}
func NewConfig(configPath string) *Config {
Cfg = &Config{}
file, err := os.Open(configPath)
if err != nil {
log.Println("could not open config file")
}
defer file.Close()
d := yaml.NewDecoder(file)
if err := d.Decode(&Cfg); err != nil {
log.Println("could not decode config file")
}
return Cfg
}
func ValidateConfigPath(path string) error {
s, err := os.Stat(path)
if err != nil {
return err
}
if s.IsDir() {
return fmt.Errorf("'%s' is a directory, not a normal file", path)
}
return nil
}
func NewConfigWatcher(configPath string) (*ConfigWatcher, error) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
return &ConfigWatcher{
watcher: watcher,
configPath: configPath,
}, nil
}
func (cw *ConfigWatcher) Start() error {
if err := cw.watcher.Add(cw.configPath); err != nil {
return err
}
go func() {
for {
select {
case event, ok := <-cw.watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write == fsnotify.Write {
Log.Info("Config file modified, reloading...")
cw.reloadConfig()
}
case err, ok := <-cw.watcher.Errors:
if !ok {
return
}
Log.Errorf("Config watcher error: %v", err)
}
}
}()
return nil
}
func (cw *ConfigWatcher) reloadConfig() {
cw.mu.Lock()
defer cw.mu.Unlock()
newCfg := &Config{}
file, err := os.Open(cw.configPath)
if err != nil {
Log.Errorf("Could not reload config: %v", err)
return
}
defer file.Close()
d := yaml.NewDecoder(file)
if err := d.Decode(newCfg); err != nil {
Log.Errorf("Could not decode reloaded config: %v", err)
return
}
// Sauvegarder l'ancienne config
oldCfg := Cfg
// Appliquer la nouvelle config
Cfg = newCfg
// Vérifier les changements qui nécessitent des actions
cw.applyConfigChanges(oldCfg, newCfg)
Log.Info("✅ Config reloaded successfully")
}
func (cw *ConfigWatcher) applyConfigChanges(oldCfg, newCfg *Config) {
// Log level
if oldCfg.General.LogLevel != newCfg.General.LogLevel {
switch newCfg.General.LogLevel {
case "DEBUG":
Log.SetLevel(log.DebugLevel)
case "INFO":
Log.SetLevel(log.InfoLevel)
case "WARN":
Log.SetLevel(log.WarnLevel)
default:
Log.SetLevel(log.InfoLevel)
}
Log.Infof("Log level changed to %s", newCfg.General.LogLevel)
}
// Gotify
if oldCfg.Gotify.Enable != newCfg.Gotify.Enable {
Log.Infof("Gotify notifications %s", map[bool]string{true: "enabled", false: "disabled"}[newCfg.Gotify.Enable])
}
if oldCfg.Cluster.FT8 != newCfg.Cluster.FT8 ||
oldCfg.Cluster.FT4 != newCfg.Cluster.FT4 ||
oldCfg.Cluster.Skimmer != newCfg.Cluster.Skimmer ||
oldCfg.Cluster.Beacon != newCfg.Cluster.Beacon {
Log.Info("Cluster filters changed, applying")
httpServerInstance.TCPClient.ReloadFilters()
}
}
func (cw *ConfigWatcher) Stop() {
cw.watcher.Close()
}