package models import ( "backea/internal/logging" "github.com/joho/godotenv" "github.com/spf13/viper" ) // Configuration holds the complete application configuration type Configuration struct { Logging LoggingConfig `mapstructure:"logging"` Defaults DefaultsConfig `mapstructure:"defaults"` Services map[string]ServiceGroup `mapstructure:"services"` } // LoggingConfig holds logging configuration type LoggingConfig struct { LogLevel string `mapstructure:"log_level"` LogFile string `mapstructure:"log_file"` } // DefaultsConfig holds the default settings for all services type DefaultsConfig struct { Retention RetentionConfig `mapstructure:"retention"` } // ServiceGroup represents a service group with a common directory and multiple backup configurations type ServiceGroup struct { Directory string `mapstructure:"directory,omitempty"` Source SourceConfig `mapstructure:"source"` Hooks HooksConfig `mapstructure:"hooks"` BackupConfigs map[string]BackupConfig `mapstructure:"backup_configs"` } // SourceConfig represents source configuration for backups type SourceConfig struct { Host string `mapstructure:"host"` Path string `mapstructure:"path"` SSHKey string `mapstructure:"ssh_key"` } // HooksConfig contains the hooks to run before and after the backup type HooksConfig struct { BeforeHook string `mapstructure:"before_hook"` AfterHook string `mapstructure:"after_hook"` } // BackupConfig represents a specific backup configuration type BackupConfig struct { BackupStrategy StrategyConfig `mapstructure:"backup_strategy"` } // StrategyConfig represents a backup strategy configuration type StrategyConfig struct { Type string `mapstructure:"type"` Provider string `mapstructure:"provider"` Options string `mapstructure:"options,omitempty"` Retention RetentionConfig `mapstructure:"retention,omitempty"` Destination DestConfig `mapstructure:"destination"` } // RetentionConfig represents retention policy configuration type RetentionConfig struct { KeepLatest int `mapstructure:"keep_latest"` KeepHourly int `mapstructure:"keep_hourly"` KeepDaily int `mapstructure:"keep_daily"` KeepWeekly int `mapstructure:"keep_weekly"` KeepMonthly int `mapstructure:"keep_monthly"` KeepYearly int `mapstructure:"keep_yearly"` } // DestConfig represents destination configuration for remote backups type DestConfig struct { Host string `mapstructure:"host,omitempty"` Path string `mapstructure:"path"` SSHKey string `mapstructure:"ssh_key,omitempty"` Username string `mapstructure:"username"` } // parseLogLevel converts string log level to LogLevel func parseLogLevel(level string) logging.LogLevel { switch level { case "debug": return logging.DEBUG case "info": return logging.INFO case "warn": return logging.WARN case "error": return logging.ERROR case "fatal": return logging.FATAL default: return logging.INFO // Default to INFO if not specified or invalid } } // LoadConfig loads the application configuration from a file func LoadConfig(configPath string) (*Configuration, error) { // Create a logger first with default settings logger := logging.GetLogger() if err := godotenv.Load(); err != nil { logger.Warn("Error loading .env file: %v", err) } viper.SetConfigFile(configPath) if err := viper.ReadInConfig(); err != nil { logger.Error("Failed to read config file: %v", err) return nil, err } var config Configuration if err := viper.Unmarshal(&config); err != nil { logger.Error("Failed to unmarshal config: %v", err) return nil, err } // Initialize logger with config settings logLevel := parseLogLevel(config.Logging.LogLevel) logging.InitLogger(logLevel, config.Logging.LogFile) // Get the potentially reconfigured logger logger = logging.GetLogger() logger.Info("Initialized logger with level %v and output file %s", config.Logging.LogLevel, config.Logging.LogFile) // Apply default retention settings where needed for serviceName, service := range config.Services { for configID, backupConfig := range service.BackupConfigs { // If retention is not set, use defaults if isRetentionEmpty(backupConfig.BackupStrategy.Retention) { logger.Debug("Applying default retention settings for service %s, config %s", serviceName, configID) backupConfig.BackupStrategy.Retention = config.Defaults.Retention service.BackupConfigs[configID] = backupConfig } } config.Services[serviceName] = service } return &config, nil } // isRetentionEmpty checks if a retention config is empty (all zeros) func isRetentionEmpty(retention RetentionConfig) bool { return retention.KeepLatest == 0 && retention.KeepHourly == 0 && retention.KeepDaily == 0 && retention.KeepWeekly == 0 && retention.KeepMonthly == 0 && retention.KeepYearly == 0 }