package config import ( "fmt" "net" "os" "strconv" "time" mysqlDriver "github.com/go-sql-driver/mysql" "gopkg.in/yaml.v3" ) type Config struct { Server ServerConfig `yaml:"server"` Database DatabaseConfig `yaml:"database"` Pricing PricingConfig `yaml:"pricing"` Export ExportConfig `yaml:"export"` Alerts AlertsConfig `yaml:"alerts"` Notifications NotificationsConfig `yaml:"notifications"` Logging LoggingConfig `yaml:"logging"` Backup BackupConfig `yaml:"backup"` } type ServerConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` Mode string `yaml:"mode"` ReadTimeout time.Duration `yaml:"read_timeout"` WriteTimeout time.Duration `yaml:"write_timeout"` } type DatabaseConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` Name string `yaml:"name"` User string `yaml:"user"` Password string `yaml:"password"` MaxOpenConns int `yaml:"max_open_conns"` MaxIdleConns int `yaml:"max_idle_conns"` ConnMaxLifetime time.Duration `yaml:"conn_max_lifetime"` } func (d *DatabaseConfig) DSN() string { cfg := mysqlDriver.NewConfig() cfg.User = d.User cfg.Passwd = d.Password cfg.Net = "tcp" cfg.Addr = net.JoinHostPort(d.Host, strconv.Itoa(d.Port)) cfg.DBName = d.Name cfg.ParseTime = true cfg.Loc = time.Local cfg.Params = map[string]string{ "charset": "utf8mb4", } return cfg.FormatDSN() } type PricingConfig struct { DefaultMethod string `yaml:"default_method"` DefaultPeriodDays int `yaml:"default_period_days"` FreshnessGreenDays int `yaml:"freshness_green_days"` FreshnessYellowDays int `yaml:"freshness_yellow_days"` FreshnessRedDays int `yaml:"freshness_red_days"` MinQuotesForMedian int `yaml:"min_quotes_for_median"` PopularityDecayDays int `yaml:"popularity_decay_days"` } type ExportConfig struct { TempDir string `yaml:"temp_dir"` MaxFileAge time.Duration `yaml:"max_file_age"` CompanyName string `yaml:"company_name"` } type AlertsConfig struct { Enabled bool `yaml:"enabled"` CheckInterval time.Duration `yaml:"check_interval"` HighDemandThreshold int `yaml:"high_demand_threshold"` TrendingThresholdPercent int `yaml:"trending_threshold_percent"` } type NotificationsConfig struct { EmailEnabled bool `yaml:"email_enabled"` SMTPHost string `yaml:"smtp_host"` SMTPPort int `yaml:"smtp_port"` SMTPUser string `yaml:"smtp_user"` SMTPPassword string `yaml:"smtp_password"` FromAddress string `yaml:"from_address"` } type LoggingConfig struct { Level string `yaml:"level"` Format string `yaml:"format"` Output string `yaml:"output"` FilePath string `yaml:"file_path"` } type BackupConfig struct { Time string `yaml:"time"` } func Load(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("reading config file: %w", err) } var cfg Config if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("parsing config file: %w", err) } cfg.setDefaults() return &cfg, nil } func (c *Config) setDefaults() { if c.Server.Host == "" { c.Server.Host = "127.0.0.1" } if c.Server.Port == 0 { c.Server.Port = 8080 } if c.Server.Mode == "" { c.Server.Mode = "release" } if c.Server.ReadTimeout == 0 { c.Server.ReadTimeout = 30 * time.Second } if c.Server.WriteTimeout == 0 { c.Server.WriteTimeout = 30 * time.Second } if c.Database.Port == 0 { c.Database.Port = 3306 } if c.Database.MaxOpenConns == 0 { c.Database.MaxOpenConns = 25 } if c.Database.MaxIdleConns == 0 { c.Database.MaxIdleConns = 5 } if c.Database.ConnMaxLifetime == 0 { c.Database.ConnMaxLifetime = 5 * time.Minute } if c.Pricing.DefaultMethod == "" { c.Pricing.DefaultMethod = "weighted_median" } if c.Pricing.DefaultPeriodDays == 0 { c.Pricing.DefaultPeriodDays = 90 } if c.Pricing.FreshnessGreenDays == 0 { c.Pricing.FreshnessGreenDays = 30 } if c.Pricing.FreshnessYellowDays == 0 { c.Pricing.FreshnessYellowDays = 60 } if c.Pricing.FreshnessRedDays == 0 { c.Pricing.FreshnessRedDays = 90 } if c.Pricing.MinQuotesForMedian == 0 { c.Pricing.MinQuotesForMedian = 3 } if c.Logging.Level == "" { c.Logging.Level = "info" } if c.Logging.Format == "" { c.Logging.Format = "json" } if c.Logging.Output == "" { c.Logging.Output = "stdout" } if c.Backup.Time == "" { c.Backup.Time = "00:00" } } func (c *Config) Address() string { return net.JoinHostPort(c.Server.Host, strconv.Itoa(c.Server.Port)) }