veza/veza-backend-api/cmd/backup/main.go

147 lines
4 KiB
Go
Raw Normal View History

package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"veza-backend-api/internal/services"
"go.uber.org/zap"
)
func main() {
// Parse command line flags
backupDir := flag.String("backup-dir", "/backups/postgres", "Directory to store backups")
retentionDays := flag.Int("retention-days", 30, "Number of days to retain backups")
createBackup := flag.Bool("create", false, "Create a new backup")
cleanup := flag.Bool("cleanup", false, "Cleanup old backups")
list := flag.Bool("list", false, "List all backups")
flag.Parse()
// Initialize logger
logger, err := zap.NewProduction()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to initialize logger: %v\n", err)
os.Exit(1)
}
defer logger.Sync()
// Get database connection details from environment variables
databaseURL := os.Getenv("DATABASE_URL")
databaseName := os.Getenv("DB_NAME")
if databaseName == "" {
databaseName = "veza_db" // Default
}
databaseUser := os.Getenv("DB_USER")
if databaseUser == "" {
databaseUser = "veza_user" // Default
}
databaseHost := os.Getenv("DB_HOST")
if databaseHost == "" {
databaseHost = "localhost" // Default
}
databasePort := os.Getenv("DB_PORT")
if databasePort == "" {
databasePort = "5432" // Default
}
// If DATABASE_URL is not set, try to construct it from individual components
if databaseURL == "" {
// Use individual components for pg_dump
}
// Create backup service
backupConfig := services.BackupConfig{
BackupDir: *backupDir,
RetentionDays: *retentionDays,
DatabaseURL: databaseURL,
DatabaseName: databaseName,
DatabaseUser: databaseUser,
DatabaseHost: databaseHost,
DatabasePort: databasePort,
}
backupService, err := services.NewBackupService(backupConfig, logger)
if err != nil {
logger.Fatal("Failed to create backup service", zap.Error(err))
}
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()
// Handle signals for graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
logger.Info("Received shutdown signal, cancelling backup operation")
cancel()
}()
// Execute requested action
if *createBackup {
logger.Info("Creating database backup")
result, err := backupService.CreateBackup(ctx)
if err != nil {
logger.Fatal("Backup failed", zap.Error(err))
}
if result.Success {
logger.Info("Backup completed successfully",
zap.String("backup_path", result.BackupPath),
zap.Int64("backup_size", result.BackupSize),
zap.Duration("duration", result.Duration))
} else {
logger.Fatal("Backup failed", zap.String("error", result.ErrorMessage))
}
} else if *cleanup {
logger.Info("Cleaning up old backups")
if err := backupService.CleanupOldBackups(ctx); err != nil {
logger.Fatal("Cleanup failed", zap.Error(err))
}
logger.Info("Cleanup completed successfully")
} else if *list {
logger.Info("Listing backups")
backups, err := backupService.ListBackups(ctx)
if err != nil {
logger.Fatal("Failed to list backups", zap.Error(err))
}
fmt.Printf("\nFound %d backups:\n\n", len(backups))
for _, backup := range backups {
sizeMB := float64(backup.Size) / (1024 * 1024)
fmt.Printf(" %s\n", backup.FileName)
fmt.Printf(" Path: %s\n", backup.FilePath)
fmt.Printf(" Size: %.2f MB\n", sizeMB)
fmt.Printf(" Created: %s\n\n", backup.CreatedAt.Format(time.RFC3339))
}
} else {
// Default: create backup and cleanup
logger.Info("Creating backup and cleaning up old backups")
result, err := backupService.CreateBackup(ctx)
if err != nil {
logger.Fatal("Backup failed", zap.Error(err))
}
if result.Success {
logger.Info("Backup completed successfully",
zap.String("backup_path", result.BackupPath),
zap.Int64("backup_size", result.BackupSize),
zap.Duration("duration", result.Duration))
}
// Cleanup old backups
if err := backupService.CleanupOldBackups(ctx); err != nil {
logger.Warn("Cleanup failed", zap.Error(err))
}
}
}