Initial commit: Disaster recovery CLI tool
A Go-based CLI tool for recovering servers from backups to new cloud VMs. Features: - Multi-cloud support: Exoscale, Cloudscale, Hetzner Cloud - Backup sources: Local filesystem, Hetzner Storage Box - 6-stage restore pipeline with /etc whitelist protection - DNS migration with safety checks and auto-rollback - Dry-run by default, requires --yes to execute - Cloud-init for SSH key injection Packages: - cmd/recover-server: CLI commands (recover, migrate-dns, list, cleanup) - internal/providers: Cloud provider implementations - internal/backup: Backup source implementations - internal/restore: 6-stage restore pipeline - internal/dns: Exoscale DNS management - internal/ui: Prompts, progress, dry-run display - internal/config: Environment and host configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
100
internal/ui/prompts.go
Normal file
100
internal/ui/prompts.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ConfirmAction asks for yes/no confirmation
|
||||
func ConfirmAction(message string) bool {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Printf("%s [y/N]: ", message)
|
||||
|
||||
response, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
response = strings.TrimSpace(strings.ToLower(response))
|
||||
return response == "y" || response == "yes"
|
||||
}
|
||||
|
||||
// ConfirmHostname requires typing the hostname to confirm
|
||||
func ConfirmHostname(hostname string) bool {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Printf("\n⚠️ DESTRUCTIVE OPERATION ⚠️\n")
|
||||
fmt.Printf("This will modify DNS for: %s\n", hostname)
|
||||
fmt.Printf("Type the hostname exactly to confirm: ")
|
||||
|
||||
response, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
response = strings.TrimSpace(response)
|
||||
return response == hostname
|
||||
}
|
||||
|
||||
// ConfirmRecovery requires confirmation for recovery operation
|
||||
func ConfirmRecovery(host, source, target string) bool {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Printf("\n=== RECOVERY CONFIRMATION ===\n")
|
||||
fmt.Printf("Host: %s\n", host)
|
||||
fmt.Printf("Source: %s\n", source)
|
||||
fmt.Printf("Target: %s\n", target)
|
||||
fmt.Printf("\nThis will create a new VM and restore data.\n")
|
||||
fmt.Printf("Type 'RECOVER' to proceed: ")
|
||||
|
||||
response, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
response = strings.TrimSpace(response)
|
||||
return response == "RECOVER"
|
||||
}
|
||||
|
||||
// SelectOption presents options and returns selection
|
||||
func SelectOption(prompt string, options []string) (int, error) {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Println(prompt)
|
||||
for i, opt := range options {
|
||||
fmt.Printf(" %d. %s\n", i+1, opt)
|
||||
}
|
||||
fmt.Print("Selection: ")
|
||||
|
||||
var selection int
|
||||
_, err := fmt.Fscanf(reader, "%d\n", &selection)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if selection < 1 || selection > len(options) {
|
||||
return -1, fmt.Errorf("invalid selection")
|
||||
}
|
||||
|
||||
return selection - 1, nil
|
||||
}
|
||||
|
||||
// PrintError prints an error message in red
|
||||
func PrintError(format string, args ...interface{}) {
|
||||
fmt.Printf("\033[31mError: "+format+"\033[0m\n", args...)
|
||||
}
|
||||
|
||||
// PrintSuccess prints a success message in green
|
||||
func PrintSuccess(format string, args ...interface{}) {
|
||||
fmt.Printf("\033[32m✓ "+format+"\033[0m\n", args...)
|
||||
}
|
||||
|
||||
// PrintWarning prints a warning message in yellow
|
||||
func PrintWarning(format string, args ...interface{}) {
|
||||
fmt.Printf("\033[33m⚠ "+format+"\033[0m\n", args...)
|
||||
}
|
||||
|
||||
// PrintInfo prints an info message in blue
|
||||
func PrintInfo(format string, args ...interface{}) {
|
||||
fmt.Printf("\033[34mℹ "+format+"\033[0m\n", args...)
|
||||
}
|
||||
Reference in New Issue
Block a user