151 lines
4.6 KiB
Go

package strategy
import (
"backea/internal/backup/config"
"backea/internal/backup/strategy/kopia"
"fmt"
"os"
"path/filepath"
"strings"
"time"
)
// Factory handles backup strategy creation
type Factory struct {
Config *config.Configuration
ConfigPath string
}
// NewFactory initializes and returns a Factory
func NewFactory(configPath string) (*Factory, error) {
cfg, err := config.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 *config.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 config.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":
// return createSFTPProvider(strategyConfig.Destination)
default:
return nil, fmt.Errorf("unknown kopia provider type: %s", strategyConfig.Provider)
}
}
// createSFTPProvider handles SFTP provider creation
func createSFTPProvider(dest DestinationConfig) (kopia.Provider, error) {
host, username := parseSFTPHost(dest.Host)
if host == "" {
return nil, fmt.Errorf("SFTP host not specified")
}
return kopia.NewSFTPProvider(host, getDefaultPath(dest.Path), username, getSSHKeyPath(dest.SSHKey)), nil
}
// parseSFTPHost extracts the username and host from "user@host"
func parseSFTPHost(host string) (string, string) {
if atIndex := strings.Index(host, "@"); atIndex > 0 {
return host[atIndex+1:], host[:atIndex]
}
return host, getEnvUser()
}
// 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")
}
// getEnvUser returns the system username
func getEnvUser() string {
if user := os.Getenv("USER"); user != "" {
return user
}
return "root"
}
// getSSHKeyPath returns the SSH key path
func getSSHKeyPath(key string) string {
if key != "" {
return key
}
home, _ := os.UserHomeDir()
return filepath.Join(home, ".ssh", "id_rsa")
}
// 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) (*config.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 := config.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()))
}