Refactor configuration system with centralized type-safe config package
- Create internal/config package with unified config structs and validation - Abstract viper dependency behind config.Loader interface for better testability - Replace manual config parsing and type assertions with type-safe loading - Consolidate AuthConfig, SiteConfig, and DiscoveryConfig into single package - Add comprehensive validation with clear error messages - Remove ~200 lines of duplicate config handling code - Maintain backward compatibility with existing config files
This commit is contained in:
106
internal/config/loader.go
Normal file
106
internal/config/loader.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Loader interface {
|
||||
Load(configFile string) (*Config, error)
|
||||
LoadWithDefaults() (*Config, error)
|
||||
LoadWithFlags(dbPath, apiURL, apiKey, siteID string) (*Config, error)
|
||||
}
|
||||
|
||||
type viperLoader struct{}
|
||||
|
||||
func NewLoader() Loader {
|
||||
return &viperLoader{}
|
||||
}
|
||||
|
||||
func (l *viperLoader) Load(configFile string) (*Config, error) {
|
||||
v := viper.New()
|
||||
|
||||
if configFile != "" {
|
||||
v.SetConfigFile(configFile)
|
||||
} else {
|
||||
v.AddConfigPath(".")
|
||||
v.SetConfigName("insertr")
|
||||
v.SetConfigType("yaml")
|
||||
}
|
||||
|
||||
v.SetEnvPrefix("INSERTR")
|
||||
v.AutomaticEnv()
|
||||
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||
return nil, fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
config := &Config{}
|
||||
if err := v.Unmarshal(config); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
|
||||
if err := l.setDefaults(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, validate(config)
|
||||
}
|
||||
|
||||
func (l *viperLoader) LoadWithDefaults() (*Config, error) {
|
||||
return l.Load("")
|
||||
}
|
||||
|
||||
func (l *viperLoader) LoadWithFlags(dbPath, apiURL, apiKey, siteID string) (*Config, error) {
|
||||
config, err := l.LoadWithDefaults()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dbPath != "" {
|
||||
config.Database.Path = dbPath
|
||||
}
|
||||
if apiURL != "" {
|
||||
config.API.URL = apiURL
|
||||
}
|
||||
if apiKey != "" {
|
||||
config.API.Key = apiKey
|
||||
}
|
||||
if siteID != "" {
|
||||
config.CLI.SiteID = siteID
|
||||
}
|
||||
|
||||
return config, validate(config)
|
||||
}
|
||||
|
||||
func (l *viperLoader) setDefaults(config *Config) error {
|
||||
if config.Database.Path == "" {
|
||||
config.Database.Path = "./insertr.db"
|
||||
}
|
||||
|
||||
if config.CLI.SiteID == "" {
|
||||
config.CLI.SiteID = "demo"
|
||||
}
|
||||
|
||||
if config.Server.Port == 0 {
|
||||
config.Server.Port = 8080
|
||||
}
|
||||
|
||||
if config.Server.Host == "" {
|
||||
config.Server.Host = "localhost"
|
||||
}
|
||||
|
||||
if config.Auth.Provider == "" {
|
||||
config.Auth.Provider = "mock"
|
||||
}
|
||||
|
||||
if config.Auth.JWTSecret == "" {
|
||||
config.Auth.JWTSecret = "dev-secret-change-in-production"
|
||||
config.Auth.DevMode = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user