package backup import ( "fmt" "os" "os/exec" "path/filepath" "strings" "time" ) // ValidateBackup checks if a backup exists and is valid func ValidateBackup(backupName string) error { backupDir := "/etc/wg-admin/backups" backupPath := filepath.Join(backupDir, backupName) // Check if backup directory exists info, err := os.Stat(backupPath) if err != nil { if os.IsNotExist(err) { return fmt.Errorf("backup '%s' does not exist", backupName) } return fmt.Errorf("failed to access backup '%s': %w", backupName, err) } // Check if it's a directory if !info.IsDir() { return fmt.Errorf("'%s' is not a valid backup directory", backupName) } // Check for required files metadataPath := filepath.Join(backupPath, "backup-info.txt") if _, err := os.Stat(metadataPath); os.IsNotExist(err) { return fmt.Errorf("backup '%s' is missing required metadata", backupName) } // Check for wireguard directory wgBackupPath := filepath.Join(backupPath, "wireguard") if _, err := os.Stat(wgBackupPath); os.IsNotExist(err) { return fmt.Errorf("backup '%s' is missing wireguard configuration", backupName) } return nil } // ReloadWireGuard reloads the WireGuard interface to apply configuration changes func ReloadWireGuard() error { interfaceName := "wg0" // Try to down the interface first cmdDown := exec.Command("wg-quick", "down", interfaceName) _ = cmdDown.Run() // Ignore errors if interface is not up // Bring the interface up cmdUp := exec.Command("wg-quick", "up", interfaceName) output, err := cmdUp.CombinedOutput() if err != nil { return fmt.Errorf("failed to reload wireguard interface: %w, output: %s", err, string(output)) } return nil } // GetBackupSize calculates the total size of a backup directory func GetBackupSize(backupName string) (int64, error) { backupDir := "/etc/wg-admin/backups" backupPath := filepath.Join(backupDir, backupName) var size int64 err := filepath.Walk(backupPath, func(_ string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { size += info.Size() } return nil }) return size, err } // GetBackupPath returns the full path for a backup name func GetBackupPath(backupName string) string { return filepath.Join("/etc/wg-admin/backups", backupName) } // ParseBackupName extracts operation and timestamp from backup directory name // Format: wg-backup-{operation}-{timestamp} func ParseBackupName(backupName string) (operation, timestamp string, err error) { if !strings.HasPrefix(backupName, "wg-backup-") { return "", "", fmt.Errorf("invalid backup name format") } nameWithoutPrefix := strings.TrimPrefix(backupName, "wg-backup-") // Timestamp format: 20060102-150405 (15 chars) if len(nameWithoutPrefix) < 16 { return "", "", fmt.Errorf("backup name too short") } // Extract operation (everything before last timestamp) timestampLen := 15 if len(nameWithoutPrefix) > timestampLen+1 { operation = nameWithoutPrefix[:len(nameWithoutPrefix)-timestampLen-1] // Remove trailing dash if present if strings.HasSuffix(operation, "-") { operation = operation[:len(operation)-1] } } // Extract timestamp timestamp = nameWithoutPrefix[len(nameWithoutPrefix)-timestampLen:] // Validate timestamp format if _, err := time.Parse("20060102-150405", timestamp); err != nil { return "", "", fmt.Errorf("invalid timestamp format in backup name: %w", err) } return operation, timestamp, nil }