Compare commits

...

2 Commits

Author SHA1 Message Date
e2cf9adc62 fix: improve cleanup toggle UX when database detection fails
All checks were successful
CI/CD / Test (push) Successful in 1m17s
CI/CD / Lint (push) Successful in 1m28s
CI/CD / Build & Release (push) Successful in 3m13s
- Allow cleanup toggle even when preview detection failed
- Show 'detection pending' message instead of blocking the toggle
- Will re-detect databases at restore execution time
- Always show cleanup toggle option for cluster restores
- Better messaging: 'enabled/disabled' instead of showing 0 count
2026-01-17 17:07:26 +01:00
29e089fe3b fix: re-detect databases at execution time for cluster cleanup
All checks were successful
CI/CD / Test (push) Successful in 1m16s
CI/CD / Lint (push) Successful in 1m25s
CI/CD / Build & Release (push) Successful in 3m10s
- Detection in preview may fail or return stale results
- Re-detect user databases when cleanup is enabled at execution time
- Fall back to preview list if re-detection fails
- Ensures actual databases are dropped, not just what was detected earlier
2026-01-17 17:00:28 +01:00
3 changed files with 71 additions and 41 deletions

View File

@@ -4,8 +4,8 @@ This directory contains pre-compiled binaries for the DB Backup Tool across mult
## Build Information ## Build Information
- **Version**: 3.42.50 - **Version**: 3.42.50
- **Build Time**: 2026-01-17_15:45:53_UTC - **Build Time**: 2026-01-17_16:00:43_UTC
- **Git Commit**: e363e19 - **Git Commit**: 29e089f
## Recent Updates (v1.1.0) ## Recent Updates (v1.1.0)
- ✅ Fixed TUI progress display with line-by-line output - ✅ Fixed TUI progress display with line-by-line output

View File

@@ -273,7 +273,20 @@ func executeRestoreWithTUIProgress(parentCtx context.Context, cfg *config.Config
defer dbClient.Close() defer dbClient.Close()
// STEP 1: Clean cluster if requested (drop all existing user databases) // STEP 1: Clean cluster if requested (drop all existing user databases)
if restoreType == "restore-cluster" && cleanClusterFirst && len(existingDBs) > 0 { if restoreType == "restore-cluster" && cleanClusterFirst {
// Re-detect databases at execution time to get current state
// The preview list may be stale or detection may have failed earlier
safety := restore.NewSafety(cfg, log)
currentDBs, err := safety.ListUserDatabases(ctx)
if err != nil {
log.Warn("Failed to list databases for cleanup, using preview list", "error", err)
currentDBs = existingDBs // Fall back to preview list
} else if len(currentDBs) > 0 {
log.Info("Re-detected user databases for cleanup", "count", len(currentDBs), "databases", currentDBs)
existingDBs = currentDBs // Update with fresh list
}
if len(existingDBs) > 0 {
log.Info("Dropping existing user databases before cluster restore", "count", len(existingDBs)) log.Info("Dropping existing user databases before cluster restore", "count", len(existingDBs))
// Drop databases using command-line psql (no connection required) // Drop databases using command-line psql (no connection required)
@@ -293,6 +306,9 @@ func executeRestoreWithTUIProgress(parentCtx context.Context, cfg *config.Config
} }
log.Info("Cluster cleanup completed", "dropped", droppedCount, "total", len(existingDBs)) log.Info("Cluster cleanup completed", "dropped", droppedCount, "total", len(existingDBs))
} else {
log.Info("No user databases to clean up")
}
} }
// STEP 2: Create restore engine with silent progress (no stdout interference with TUI) // STEP 2: Create restore engine with silent progress (no stdout interference with TUI)

View File

@@ -288,17 +288,19 @@ func (m RestorePreviewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "c": case "c":
if m.mode == "restore-cluster" { if m.mode == "restore-cluster" {
// Prevent toggle if we couldn't detect existing databases // Toggle cluster cleanup - databases will be re-detected at execution time
if m.existingDBError != "" {
m.message = checkWarningStyle.Render("[WARN] Cannot enable cleanup - database detection failed")
} else {
// Toggle cluster cleanup
m.cleanClusterFirst = !m.cleanClusterFirst m.cleanClusterFirst = !m.cleanClusterFirst
if m.cleanClusterFirst { if m.cleanClusterFirst {
if m.existingDBError != "" {
// Detection failed in preview - will re-detect at execution
m.message = checkWarningStyle.Render("[WARN] Will clean existing databases before restore (detection pending)")
} else if m.existingDBCount > 0 {
m.message = checkWarningStyle.Render(fmt.Sprintf("[WARN] Will drop %d existing database(s) before restore", m.existingDBCount)) m.message = checkWarningStyle.Render(fmt.Sprintf("[WARN] Will drop %d existing database(s) before restore", m.existingDBCount))
} else { } else {
m.message = fmt.Sprintf("Clean cluster first: disabled") m.message = infoStyle.Render("[INFO] Cleanup enabled (no databases currently detected)")
} }
} else {
m.message = fmt.Sprintf("Clean cluster first: disabled")
} }
} else { } else {
// Toggle create if missing // Toggle create if missing
@@ -401,9 +403,10 @@ func (m RestorePreviewModel) View() string {
s.WriteString(fmt.Sprintf(" Host: %s:%d\n", m.config.Host, m.config.Port)) s.WriteString(fmt.Sprintf(" Host: %s:%d\n", m.config.Host, m.config.Port))
if m.existingDBError != "" { if m.existingDBError != "" {
// Show error when database listing failed // Show warning when database listing failed - but still allow cleanup toggle
s.WriteString(checkWarningStyle.Render(fmt.Sprintf(" Existing Databases: Unable to detect (%s)\n", m.existingDBError))) s.WriteString(checkWarningStyle.Render(" Existing Databases: Detection failed\n"))
s.WriteString(infoStyle.Render(" (Cleanup option disabled - cannot verify database status)\n")) s.WriteString(infoStyle.Render(fmt.Sprintf(" (%s)\n", m.existingDBError)))
s.WriteString(infoStyle.Render(" (Will re-detect at restore time)\n"))
} else if m.existingDBCount > 0 { } else if m.existingDBCount > 0 {
s.WriteString(fmt.Sprintf(" Existing Databases: %d found\n", m.existingDBCount)) s.WriteString(fmt.Sprintf(" Existing Databases: %d found\n", m.existingDBCount))
@@ -417,16 +420,19 @@ func (m RestorePreviewModel) View() string {
} }
s.WriteString(fmt.Sprintf(" - %s\n", db)) s.WriteString(fmt.Sprintf(" - %s\n", db))
} }
} else {
s.WriteString(" Existing Databases: None (clean slate)\n")
}
// Always show cleanup toggle for cluster restore
cleanIcon := "[N]" cleanIcon := "[N]"
cleanStyle := infoStyle cleanStyle := infoStyle
if m.cleanClusterFirst { if m.cleanClusterFirst {
cleanIcon = "[Y]" cleanIcon := "[Y]"
cleanStyle = checkWarningStyle cleanStyle = checkWarningStyle
} s.WriteString(cleanStyle.Render(fmt.Sprintf(" Clean All First: %s enabled (press 'c' to toggle)\n", cleanIcon)))
s.WriteString(cleanStyle.Render(fmt.Sprintf(" Clean All First: %s %v (press 'c' to toggle)\n", cleanIcon, m.cleanClusterFirst)))
} else { } else {
s.WriteString(" Existing Databases: None (clean slate)\n") s.WriteString(cleanStyle.Render(fmt.Sprintf(" Clean All First: %s disabled (press 'c' to toggle)\n", cleanIcon)))
} }
s.WriteString("\n") s.WriteString("\n")
} }
@@ -475,10 +481,18 @@ func (m RestorePreviewModel) View() string {
s.WriteString(infoStyle.Render(" All existing data in target database will be dropped!")) s.WriteString(infoStyle.Render(" All existing data in target database will be dropped!"))
s.WriteString("\n\n") s.WriteString("\n\n")
} }
if m.cleanClusterFirst && m.existingDBCount > 0 { if m.cleanClusterFirst {
s.WriteString(checkWarningStyle.Render("[DANGER] WARNING: Cluster cleanup enabled")) s.WriteString(checkWarningStyle.Render("[DANGER] WARNING: Cluster cleanup enabled"))
s.WriteString("\n") s.WriteString("\n")
if m.existingDBError != "" {
s.WriteString(checkWarningStyle.Render(" Existing databases will be DROPPED before restore!"))
s.WriteString("\n")
s.WriteString(infoStyle.Render(" (Database count will be detected at restore time)"))
} else if m.existingDBCount > 0 {
s.WriteString(checkWarningStyle.Render(fmt.Sprintf(" %d existing database(s) will be DROPPED before restore!", m.existingDBCount))) s.WriteString(checkWarningStyle.Render(fmt.Sprintf(" %d existing database(s) will be DROPPED before restore!", m.existingDBCount)))
} else {
s.WriteString(infoStyle.Render(" No databases currently detected - cleanup will verify at restore time"))
}
s.WriteString("\n") s.WriteString("\n")
s.WriteString(infoStyle.Render(" This ensures a clean disaster recovery scenario")) s.WriteString(infoStyle.Render(" This ensures a clean disaster recovery scenario"))
s.WriteString("\n\n") s.WriteString("\n\n")