package config import ( "errors" "fmt" "log/slog" "os" "strings" "gopkg.in/yaml.v3" ) type Config struct { Mode string `yaml:"mode"` ExternalIPProvider ExternalIpProviderConfig `yaml:"ip_provider"` DNSProvider DNSProviderConfig `yaml:"dns_provider"` NotificationProvider NotificationProviderConfig `yaml:"notification_provider,omitempty"` Domains []DomainConfig `yaml:"domains"` CheckInterval string `yaml:"check_interval"` LogLevel string `yaml:"log_level"` } const ( RunOnceMode = "RunOnce" ScheduledMode = "Scheduled" ) var LogLevelMap = map[string]slog.Level{ "debug": slog.LevelDebug, "info": slog.LevelInfo, "warn": slog.LevelWarn, "error": slog.LevelError, } func isValidLogLevel(level string) bool { _, ok := LogLevelMap[strings.ToLower(level)] return ok } type DomainConfig struct { TLD string `yaml:"tld"` Subdomains []string `yaml:"subdomains"` } type ExternalIpProviderConfig struct { Type string `yaml:"type"` ProviderConfig yaml.Node `yaml:"config"` } type DNSProviderConfig struct { Type string `yaml:"type"` ProviderConfig yaml.Node `yaml:"config"` } type NotificationProviderConfig struct { Type string `yaml:"type"` ProviderConfig yaml.Node `yaml:"config"` } func (c *Config) Load(filePath string) error { inputConfig, err := os.ReadFile(filePath) if err != nil { return fmt.Errorf("failed to read config file: %w", err) } if err := yaml.Unmarshal(inputConfig, c); err != nil { return fmt.Errorf("failed to unmarshal config file: %w", err) } if err := c.validate(); err != nil { return fmt.Errorf("failed to validate config: %w", err) } return nil } func (c *Config) validate() error { if c.Mode != RunOnceMode && c.Mode != ScheduledMode { return errors.New("mode must be one of 'RunOnce' or 'Scheduled'") } if c.Mode == ScheduledMode && c.CheckInterval == "" { return errors.New("check interval must be set when mode is 'Scheduled'") } if c.LogLevel != "" && !isValidLogLevel(c.LogLevel) { return fmt.Errorf("log level must be one of 'debug', 'info', 'warn', 'error', but got %s", c.LogLevel) } return nil }