package strategy import ( "backea/internal/backup/models" "backea/internal/backup/strategy/kopia" "fmt" "os" "path/filepath" "time" ) // Factory handles backup strategy creation type Factory struct { Config *models.Configuration ConfigPath string } // NewFactory initializes and returns a Factory func NewFactory(configPath string) (*Factory, error) { cfg, err := models.LoadConfig(configPath) if err != nil { return nil, fmt.Errorf("failed to load config: %w", err) } return &Factory{Config: cfg, ConfigPath: configPath}, nil } // CreateBackupStrategyForService returns a backup strategy based on config func (f *Factory) CreateBackupStrategyForService(groupName, serviceIndex string) (Strategy, error) { serviceConfig, err := f.getServiceConfig(groupName, serviceIndex) if err != nil { return nil, err } sourcePath, err := f.getServicePath(groupName) if err != nil { return nil, err } switch serviceConfig.BackupStrategy.Type { case "kopia": return f.createKopiaStrategy(serviceConfig, sourcePath) default: return nil, fmt.Errorf("unknown strategy type: %s", serviceConfig.BackupStrategy.Type) } } // createKopiaStrategy initializes a Kopia strategy func (f *Factory) createKopiaStrategy(serviceConfig *models.BackupConfig, sourcePath string) (Strategy, error) { provider, err := f.createKopiaProvider(serviceConfig.BackupStrategy) if err != nil { return nil, fmt.Errorf("failed to create kopia provider: %w", err) } retention := kopia.Retention(serviceConfig.BackupStrategy.Retention) configPath := generateTempConfigPath(provider.GetProviderType()) return kopia.NewStrategy(retention, provider, configPath, sourcePath), nil } // createKopiaProvider returns a Kopia storage provider func (f *Factory) createKopiaProvider(strategyConfig models.StrategyConfig) (kopia.Provider, error) { switch strategyConfig.Provider { case "local": return kopia.NewLocalProvider(getDefaultPath(strategyConfig.Destination.Path)), nil case "b2", "backblaze": return kopia.NewB2Provider(), nil case "sftp": host := strategyConfig.Destination.Host basepath := strategyConfig.Destination.Path username := strategyConfig.Destination.Username keyFile := strategyConfig.Destination.SSHKey return kopia.NewSFTPProvider(host, basepath, username, keyFile), nil default: return nil, fmt.Errorf("unknown kopia provider type: %s", strategyConfig.Provider) } } // getDefaultPath returns the given path or a default func getDefaultPath(path string) string { if path != "" { return path } home, _ := os.UserHomeDir() return filepath.Join(home, ".backea", "repos") } // getServicePath returns a service's path func (f *Factory) getServicePath(groupName string) (string, error) { if serviceGroup, exists := f.Config.Services[groupName]; exists { return serviceGroup.Source.Path, nil } return "", fmt.Errorf("service group not found: %s", groupName) } // getServiceConfig returns a service's config func (f *Factory) getServiceConfig(groupName, serviceIndex string) (*models.BackupConfig, error) { if serviceGroup, exists := f.Config.Services[groupName]; exists { if backupConfig, exists := serviceGroup.BackupConfigs[serviceIndex]; exists { return &backupConfig, nil } } return nil, fmt.Errorf("backup config not found: %s.%s", groupName, serviceIndex) } // ReloadConfig reloads configuration func (f *Factory) ReloadConfig() error { cfg, err := models.LoadConfig(f.ConfigPath) if err != nil { return fmt.Errorf("could not reload config: %w", err) } f.Config = cfg return nil } // generateTempConfigPath creates a temp Kopia config path func generateTempConfigPath(providerType string) string { tmpConfigDir := filepath.Join(os.TempDir(), "kopia_configs") _ = os.MkdirAll(tmpConfigDir, 0755) // Ensure directory exists return filepath.Join(tmpConfigDir, fmt.Sprintf("kopia_%s_%d.config", providerType, time.Now().UnixNano())) }