Files
recover-server/internal/ui/prompts.go
Olaf Berberich 29a2886402 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>
2025-12-08 00:31:27 +00:00

101 lines
2.5 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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...)
}