package config import ( "fmt" "os" "time" "github.com/rs/zerolog/log" "github.com/spf13/viper" ) // Config represents the application configuration type Config struct { Server struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` } Shutdown struct { Timeout time.Duration `mapstructure:"timeout"` } } // LoadConfig loads configuration from file, environment variables, and defaults // Configuration priority: file > environment variables > defaults // To specify a custom config file path, set DLC_CONFIG_FILE environment variable func LoadConfig() (*Config, error) { v := viper.New() // Set default values v.SetDefault("server.host", "0.0.0.0") v.SetDefault("server.port", 8080) v.SetDefault("shutdown.timeout", 30*time.Second) // Check for custom config file path via environment variable if configFile := os.Getenv("DLC_CONFIG_FILE"); configFile != "" { v.SetConfigFile(configFile) log.Info().Str("config_file", configFile).Msg("Using custom config file path") } else { // Default: look for config.yaml in current directory v.SetConfigName("config") v.SetConfigType("yaml") v.AddConfigPath(".") } // Read config file if it exists if err := v.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); !ok { // Config file was found but there was an error reading it log.Warn().Err(err).Msg("Error reading config file, using defaults") } // Config file not found, continue with environment variables and defaults } else { log.Info().Str("config_file", v.ConfigFileUsed()).Msg("Config file loaded") } // Bind environment variables v.AutomaticEnv() v.SetEnvPrefix("DLC") // DanceLessonsCoach prefix v.BindEnv("server.host", "DLC_SERVER_HOST") v.BindEnv("server.port", "DLC_SERVER_PORT") v.BindEnv("shutdown.timeout", "DLC_SHUTDOWN_TIMEOUT") // Unmarshal into Config struct var config Config if err := v.Unmarshal(&config); err != nil { log.Error().Err(err).Msg("Failed to unmarshal config") return nil, fmt.Errorf("config unmarshal error: %w", err) } log.Info(). Str("host", config.Server.Host). Int("port", config.Server.Port). Dur("shutdown_timeout", config.Shutdown.Timeout). Msg("Configuration loaded") return &config, nil } // GetServerAddress returns the formatted server address (host:port) func (c *Config) GetServerAddress() string { return fmt.Sprintf("%s:%d", c.Server.Host, c.Server.Port) }