package config import ( "fmt" "github.com/spf13/viper" ) type ClientConfig struct { SecretHash string `mapstructure:"secret_hash"` Exact []string `mapstructure:"exact"` Wildcard []string `mapstructure:"wildcard"` } type ServerConfig struct { BindAddress string `mapstructure:"bind_address"` TLS TLSConfig `mapstructure:"tls"` } type TLSConfig struct { Enabled bool `mapstructure:"enabled"` CertFile string `mapstructure:"cert_file"` KeyFile string `mapstructure:"key_file"` } type HetznerConfig struct { APIToken string `mapstructure:"api_token"` APITokenFile string `mapstructure:"api_token_file"` } type UpstreamConfig struct { Provider string `mapstructure:"provider"` Hetzner HetznerConfig `mapstructure:"hetzner"` } type Config struct { Server ServerConfig `mapstructure:"server"` Upstream UpstreamConfig `mapstructure:"upstream"` Clients map[string]ClientConfig `mapstructure:"clients"` } // LoadConfig reads the file at path (yaml, json, toml) into Config and validates it. func LoadConfig(path string) (*Config, error) { v := viper.New() v.SetConfigFile(path) if err := v.ReadInConfig(); err != nil { return nil, fmt.Errorf("reading config: %w", err) } var cfg Config if err := v.Unmarshal(&cfg); err != nil { return nil, fmt.Errorf("parsing config: %w", err) } for name, client := range cfg.Clients { if len(client.Exact) == 0 && len(client.Wildcard) == 0 { return nil, fmt.Errorf("client %q must have at least one of exact or wildcard", name) } } return &cfg, nil }