227 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			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()
 | 
						|
}
 |