package main import ( "fmt" "log" "github.com/spf13/viper" ) type WebConfig struct { Enabled bool `mapstructure:"enabled"` Port int `mapstructure:"port"` Host string `mapstructure:"host"` } type LogFormat struct { Name string `mapstructure:"name"` Pattern string `mapstructure:"pattern"` Fields map[string]string `mapstructure:"fields"` } type ToolConfig struct { Name string `mapstructure:"name"` LogFile string `mapstructure:"log_file"` Format LogFormat `mapstructure:"format"` Enabled bool `mapstructure:"enabled"` BufferSize int `mapstructure:"buffer_size"` } type ServiceConfig struct { Name string `mapstructure:"name"` Service string `mapstructure:"service"` Enabled bool `mapstructure:"enabled"` SinceTime string `mapstructure:"since_time"` Priority string `mapstructure:"priority"` } type ElasticsearchConfig struct { Enabled bool `mapstructure:"enabled"` URL string `mapstructure:"url"` Index string `mapstructure:"index"` Username string `mapstructure:"username"` Password string `mapstructure:"password"` Timeout int `mapstructure:"timeout"` } type LocalStorage struct { Enable bool `mapstructure:"enabled"` DBPath string `mapstructure:"db_path"` } type SystemMetrics struct { Enabled bool `mapstructure:"enabled"` CollectCPU bool `mapstructure:"collect_cpu"` CollectMemory bool `mapstructure:"collect_memory"` CollectDisk bool `mapstructure:"collect_disk"` CollectNetwork bool `mapstructure:"collect_network"` CollectProcesses bool `mapstructure:"collect_processes"` DiskPaths []string `mapstructure:"disk_paths"` NetworkInterfaces []string `mapstructure:"network_interfaces"` TopProcessesLimit int `mapstructure:"top_processes_limit"` CollectNetworkConnections bool `mapstructure:"collect_network_connections"` CollectLoadAverage bool `mapstructure:"collect_load_average"` CollectTCPStats bool `mapstructure:"collect_tcp_stats"` CollectFileHandles bool `mapstructure:"collect_filehandles"` CollectDiskIO bool `mapstructure:"collect_disk_io"` CollectNetworkLatency bool `mapstructure:"collect_network_latency"` CollectBandwidthUsage bool `mapstructure:"collect_bandwidth_usage"` TransferPorts []int `mapstructure:"transfer_ports"` LatencyTestHosts []string `mapstructure:"latency_test_hosts"` } type Config struct { Elasticsearch ElasticsearchConfig `mapstructure:"elasticsearch"` LocalStorage LocalStorage `mapstrucutre:"localstorage"` Tools []ToolConfig `mapstructure:"tools"` Services []ServiceConfig `mapstructure:"services"` PollIntervalSeconds int `mapstructure:"poll_interval_seconds"` SystemMetrics SystemMetrics `mapstructure:"system_metrics"` WebService WebConfig `mapstructure:"web_service"` Logging struct { Level string `mapstructure:"level"` FilePath string `mapstructure:"file_path"` } `mapstructure:"logging"` } func LoadConfig() (*Config, error) { viper.SetConfigName("config") viper.AddConfigPath(".") viper.AddConfigPath("/opt/tixel/tixel-watch/") viper.SetConfigType("yaml") setConfigDefaults() if err := viper.ReadInConfig(); err != nil { return nil, fmt.Errorf("error reading config: %w", err) } var cfg Config if err := viper.Unmarshal(&cfg); err != nil { return nil, fmt.Errorf("error parsing config: %w", err) } if err := validateConfig(&cfg); err != nil { return nil, fmt.Errorf("config validation failed: %w", err) } return &cfg, nil } func setConfigDefaults() { viper.SetDefault("poll_interval_seconds", 30) viper.SetDefault("elasticsearch.timeout", 30) viper.SetDefault("system_metrics.enabled", true) viper.SetDefault("system_metrics.collect_cpu", true) viper.SetDefault("system_metrics.collect_memory", true) viper.SetDefault("system_metrics.collect_disk", true) viper.SetDefault("system_metrics.collect_network", false) viper.SetDefault("system_metrics.disk_paths", []string{"/"}) viper.SetDefault("web_service.enabled", false) viper.SetDefault("web_service.port", 8080) viper.SetDefault("web_service.host", "localhost") viper.SetDefault("logging.level", "info") } func validateConfig(cfg *Config) error { if cfg.Elasticsearch.URL == "" { return fmt.Errorf("elasticsearch.url is required") } if cfg.Elasticsearch.Index == "" { return fmt.Errorf("elasticsearch.index is required") } if cfg.PollIntervalSeconds <= 0 { log.Printf("Warning: poll_interval_seconds is %d, setting to 30", cfg.PollIntervalSeconds) cfg.PollIntervalSeconds = 30 } for i := range cfg.Tools { if cfg.Tools[i].BufferSize <= 0 { cfg.Tools[i].BufferSize = 100 } } return nil }