Files
wg-admin/internal/backup/restore.go
Calmcacil 26120b8bc2 Add WireGuard TUI implementation
- Add Go TUI with bubbletea for WireGuard management
- Implement client CRUD operations with QR code generation
- Add configuration and validation modules
- Install/update scripts for client setup
- Update Makefile to build binaries to bin/ directory
- Add .gitignore for Go projects
2026-01-12 19:03:35 +01:00

123 lines
3.4 KiB
Go

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
}