Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1831bd7c1f |
@@ -3,9 +3,9 @@
|
|||||||
This directory contains pre-compiled binaries for the DB Backup Tool across multiple platforms and architectures.
|
This directory contains pre-compiled binaries for the DB Backup Tool across multiple platforms and architectures.
|
||||||
|
|
||||||
## Build Information
|
## Build Information
|
||||||
- **Version**: 3.42.1
|
- **Version**: 3.42.10
|
||||||
- **Build Time**: 2026-01-08_05:03:53_UTC
|
- **Build Time**: 2026-01-08_08:48:48_UTC
|
||||||
- **Git Commit**: 9c65821
|
- **Git Commit**: 24377ea
|
||||||
|
|
||||||
## 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
|
||||||
|
|||||||
@@ -180,11 +180,11 @@ func (m BackupManagerModel) View() string {
|
|||||||
return s.String()
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Column headers
|
// Column headers with better alignment
|
||||||
s.WriteString(archiveHeaderStyle.Render(fmt.Sprintf("%-35s %-25s %-12s %-20s",
|
s.WriteString(archiveHeaderStyle.Render(fmt.Sprintf(" %-32s %-22s %10s %-16s",
|
||||||
"FILENAME", "FORMAT", "SIZE", "MODIFIED")))
|
"FILENAME", "FORMAT", "SIZE", "MODIFIED")))
|
||||||
s.WriteString("\n")
|
s.WriteString("\n")
|
||||||
s.WriteString(strings.Repeat("-", 95))
|
s.WriteString(strings.Repeat("-", 90))
|
||||||
s.WriteString("\n")
|
s.WriteString("\n")
|
||||||
|
|
||||||
// Show archives (limit to visible area)
|
// Show archives (limit to visible area)
|
||||||
@@ -203,23 +203,23 @@ func (m BackupManagerModel) View() string {
|
|||||||
style := archiveNormalStyle
|
style := archiveNormalStyle
|
||||||
|
|
||||||
if i == m.cursor {
|
if i == m.cursor {
|
||||||
cursor = ">"
|
cursor = "> "
|
||||||
style = archiveSelectedStyle
|
style = archiveSelectedStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status icon
|
// Status icon - consistent 4-char width
|
||||||
statusIcon := "[+]"
|
statusIcon := " [+]"
|
||||||
if !archive.Valid {
|
if !archive.Valid {
|
||||||
statusIcon = "[-]"
|
statusIcon = " [-]"
|
||||||
style = archiveInvalidStyle
|
style = archiveInvalidStyle
|
||||||
} else if time.Since(archive.Modified) > 30*24*time.Hour {
|
} else if time.Since(archive.Modified) > 30*24*time.Hour {
|
||||||
statusIcon = "[WARN]"
|
statusIcon = " [!]"
|
||||||
}
|
}
|
||||||
|
|
||||||
filename := truncate(archive.Name, 33)
|
filename := truncate(archive.Name, 32)
|
||||||
format := truncate(archive.Format.String(), 23)
|
format := truncate(archive.Format.String(), 22)
|
||||||
|
|
||||||
line := fmt.Sprintf("%s %s %-33s %-23s %-10s %-19s",
|
line := fmt.Sprintf("%s%s %-32s %-22s %10s %-16s",
|
||||||
cursor,
|
cursor,
|
||||||
statusIcon,
|
statusIcon,
|
||||||
filename,
|
filename,
|
||||||
@@ -239,8 +239,20 @@ func (m BackupManagerModel) View() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.WriteString(infoStyle.Render(fmt.Sprintf("Selected: %d/%d", m.cursor+1, len(m.archives))))
|
s.WriteString(infoStyle.Render(fmt.Sprintf("Selected: %d/%d", m.cursor+1, len(m.archives))))
|
||||||
|
s.WriteString("\n\n")
|
||||||
|
|
||||||
|
// Grouped keyboard shortcuts for better readability
|
||||||
|
s.WriteString(infoStyle.Render("NAVIGATE ACTIONS OTHER"))
|
||||||
s.WriteString("\n")
|
s.WriteString("\n")
|
||||||
s.WriteString(infoStyle.Render("[KEY] ↑/↓: Navigate | r: Restore | v: Verify | d: Delete | i: Info | R: Refresh | Esc: Back"))
|
s.WriteString(infoStyle.Render("-------- ------- -----"))
|
||||||
|
s.WriteString("\n")
|
||||||
|
s.WriteString(infoStyle.Render("Up/Down: Move r: Restore R: Refresh"))
|
||||||
|
s.WriteString("\n")
|
||||||
|
s.WriteString(infoStyle.Render(" v: Verify Esc: Back"))
|
||||||
|
s.WriteString("\n")
|
||||||
|
s.WriteString(infoStyle.Render(" d: Delete q: Quit"))
|
||||||
|
s.WriteString("\n")
|
||||||
|
s.WriteString(infoStyle.Render(" i: Info"))
|
||||||
|
|
||||||
return s.String()
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,124 +204,132 @@ func (m DiagnoseViewModel) View() string {
|
|||||||
func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) string {
|
func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) string {
|
||||||
var s strings.Builder
|
var s strings.Builder
|
||||||
|
|
||||||
// Status
|
// Status Box
|
||||||
s.WriteString(strings.Repeat("-", 60))
|
s.WriteString("+--[ VALIDATION STATUS ]" + strings.Repeat("-", 37) + "+\n")
|
||||||
s.WriteString("\n")
|
|
||||||
|
|
||||||
if result.IsValid {
|
if result.IsValid {
|
||||||
s.WriteString(diagnosePassStyle.Render("[OK] STATUS: VALID"))
|
s.WriteString("| " + diagnosePassStyle.Render("[OK] VALID - Archive passed all checks") + strings.Repeat(" ", 18) + "|\n")
|
||||||
} else {
|
} else {
|
||||||
s.WriteString(diagnoseFailStyle.Render("[FAIL] STATUS: INVALID"))
|
s.WriteString("| " + diagnoseFailStyle.Render("[FAIL] INVALID - Archive has problems") + strings.Repeat(" ", 19) + "|\n")
|
||||||
}
|
}
|
||||||
s.WriteString("\n")
|
|
||||||
|
|
||||||
if result.IsTruncated {
|
if result.IsTruncated {
|
||||||
s.WriteString(diagnoseFailStyle.Render("[WARN] TRUNCATED: File appears incomplete"))
|
s.WriteString("| " + diagnoseFailStyle.Render("[!] TRUNCATED - File is incomplete") + strings.Repeat(" ", 22) + "|\n")
|
||||||
s.WriteString("\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.IsCorrupted {
|
if result.IsCorrupted {
|
||||||
s.WriteString(diagnoseFailStyle.Render("[WARN] CORRUPTED: File structure is damaged"))
|
s.WriteString("| " + diagnoseFailStyle.Render("[!] CORRUPTED - File structure damaged") + strings.Repeat(" ", 18) + "|\n")
|
||||||
s.WriteString("\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.WriteString(strings.Repeat("-", 60))
|
s.WriteString("+" + strings.Repeat("-", 60) + "+\n\n")
|
||||||
s.WriteString("\n\n")
|
|
||||||
|
|
||||||
// Details
|
// Details Box
|
||||||
if result.Details != nil {
|
if result.Details != nil {
|
||||||
s.WriteString(diagnoseHeaderStyle.Render("[STATS] DETAILS:"))
|
s.WriteString("+--[ DETAILS ]" + strings.Repeat("-", 46) + "+\n")
|
||||||
s.WriteString("\n")
|
|
||||||
|
|
||||||
if result.Details.HasPGDMPSignature {
|
if result.Details.HasPGDMPSignature {
|
||||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
s.WriteString("| " + diagnosePassStyle.Render("[+]") + " PostgreSQL custom format (PGDMP)" + strings.Repeat(" ", 20) + "|\n")
|
||||||
s.WriteString("Has PGDMP signature (custom format)\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Details.HasSQLHeader {
|
if result.Details.HasSQLHeader {
|
||||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
s.WriteString("| " + diagnosePassStyle.Render("[+]") + " PostgreSQL SQL header found" + strings.Repeat(" ", 25) + "|\n")
|
||||||
s.WriteString("Has PostgreSQL SQL header\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Details.GzipValid {
|
if result.Details.GzipValid {
|
||||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
s.WriteString("| " + diagnosePassStyle.Render("[+]") + " Gzip compression valid" + strings.Repeat(" ", 30) + "|\n")
|
||||||
s.WriteString("Gzip compression valid\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Details.PgRestoreListable {
|
if result.Details.PgRestoreListable {
|
||||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
tableInfo := fmt.Sprintf(" (%d tables)", result.Details.TableCount)
|
||||||
s.WriteString(fmt.Sprintf("pg_restore can list contents (%d tables)\n", result.Details.TableCount))
|
padding := 36 - len(tableInfo)
|
||||||
|
if padding < 0 {
|
||||||
|
padding = 0
|
||||||
|
}
|
||||||
|
s.WriteString("| " + diagnosePassStyle.Render("[+]") + " pg_restore can list contents" + tableInfo + strings.Repeat(" ", padding) + "|\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Details.CopyBlockCount > 0 {
|
if result.Details.CopyBlockCount > 0 {
|
||||||
s.WriteString(diagnoseInfoStyle.Render(" - "))
|
blockInfo := fmt.Sprintf("%d COPY blocks found", result.Details.CopyBlockCount)
|
||||||
s.WriteString(fmt.Sprintf("Contains %d COPY blocks\n", result.Details.CopyBlockCount))
|
padding := 50 - len(blockInfo)
|
||||||
|
if padding < 0 {
|
||||||
|
padding = 0
|
||||||
|
}
|
||||||
|
s.WriteString("| [-] " + blockInfo + strings.Repeat(" ", padding) + "|\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Details.UnterminatedCopy {
|
if result.Details.UnterminatedCopy {
|
||||||
s.WriteString(diagnoseFailStyle.Render(" [-] "))
|
s.WriteString("| " + diagnoseFailStyle.Render("[-]") + " Unterminated COPY: " + truncate(result.Details.LastCopyTable, 30) + strings.Repeat(" ", 5) + "|\n")
|
||||||
s.WriteString(fmt.Sprintf("Unterminated COPY block: %s (line %d)\n",
|
|
||||||
result.Details.LastCopyTable, result.Details.LastCopyLineNumber))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Details.ProperlyTerminated {
|
if result.Details.ProperlyTerminated {
|
||||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
s.WriteString("| " + diagnosePassStyle.Render("[+]") + " All COPY blocks properly terminated" + strings.Repeat(" ", 17) + "|\n")
|
||||||
s.WriteString("All COPY blocks properly terminated\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Details.ExpandedSize > 0 {
|
if result.Details.ExpandedSize > 0 {
|
||||||
s.WriteString(diagnoseInfoStyle.Render(" - "))
|
sizeInfo := fmt.Sprintf("Expanded: %s (%.1fx)", formatSize(result.Details.ExpandedSize), result.Details.CompressionRatio)
|
||||||
s.WriteString(fmt.Sprintf("Expanded size: %s (ratio: %.1fx)\n",
|
padding := 50 - len(sizeInfo)
|
||||||
formatSize(result.Details.ExpandedSize), result.Details.CompressionRatio))
|
if padding < 0 {
|
||||||
|
padding = 0
|
||||||
}
|
}
|
||||||
|
s.WriteString("| [-] " + sizeInfo + strings.Repeat(" ", padding) + "|\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errors
|
s.WriteString("+" + strings.Repeat("-", 60) + "+\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errors Box
|
||||||
if len(result.Errors) > 0 {
|
if len(result.Errors) > 0 {
|
||||||
s.WriteString("\n")
|
s.WriteString("\n+--[ ERRORS ]" + strings.Repeat("-", 47) + "+\n")
|
||||||
s.WriteString(diagnoseFailStyle.Render("[FAIL] ERRORS:"))
|
|
||||||
s.WriteString("\n")
|
|
||||||
for i, e := range result.Errors {
|
for i, e := range result.Errors {
|
||||||
if i >= 5 {
|
if i >= 5 {
|
||||||
s.WriteString(diagnoseInfoStyle.Render(fmt.Sprintf(" ... and %d more\n", len(result.Errors)-5)))
|
remaining := fmt.Sprintf("... and %d more errors", len(result.Errors)-5)
|
||||||
|
padding := 56 - len(remaining)
|
||||||
|
s.WriteString("| " + remaining + strings.Repeat(" ", padding) + "|\n")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
s.WriteString(diagnoseFailStyle.Render(" - "))
|
errText := truncate(e, 54)
|
||||||
s.WriteString(truncate(e, 70))
|
padding := 56 - len(errText)
|
||||||
s.WriteString("\n")
|
if padding < 0 {
|
||||||
|
padding = 0
|
||||||
}
|
}
|
||||||
|
s.WriteString("| " + errText + strings.Repeat(" ", padding) + "|\n")
|
||||||
|
}
|
||||||
|
s.WriteString("+" + strings.Repeat("-", 60) + "+\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warnings
|
// Warnings Box
|
||||||
if len(result.Warnings) > 0 {
|
if len(result.Warnings) > 0 {
|
||||||
s.WriteString("\n")
|
s.WriteString("\n+--[ WARNINGS ]" + strings.Repeat("-", 45) + "+\n")
|
||||||
s.WriteString(diagnoseWarnStyle.Render("[WARN] WARNINGS:"))
|
|
||||||
s.WriteString("\n")
|
|
||||||
for i, w := range result.Warnings {
|
for i, w := range result.Warnings {
|
||||||
if i >= 3 {
|
if i >= 3 {
|
||||||
s.WriteString(diagnoseInfoStyle.Render(fmt.Sprintf(" ... and %d more\n", len(result.Warnings)-3)))
|
remaining := fmt.Sprintf("... and %d more warnings", len(result.Warnings)-3)
|
||||||
|
padding := 56 - len(remaining)
|
||||||
|
s.WriteString("| " + remaining + strings.Repeat(" ", padding) + "|\n")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
s.WriteString(diagnoseWarnStyle.Render(" - "))
|
warnText := truncate(w, 54)
|
||||||
s.WriteString(truncate(w, 70))
|
padding := 56 - len(warnText)
|
||||||
s.WriteString("\n")
|
if padding < 0 {
|
||||||
|
padding = 0
|
||||||
}
|
}
|
||||||
|
s.WriteString("| " + warnText + strings.Repeat(" ", padding) + "|\n")
|
||||||
|
}
|
||||||
|
s.WriteString("+" + strings.Repeat("-", 60) + "+\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recommendations
|
// Recommendations Box
|
||||||
if !result.IsValid {
|
if !result.IsValid {
|
||||||
s.WriteString("\n")
|
s.WriteString("\n+--[ RECOMMENDATIONS ]" + strings.Repeat("-", 38) + "+\n")
|
||||||
s.WriteString(diagnoseHeaderStyle.Render("[HINT] RECOMMENDATIONS:"))
|
|
||||||
s.WriteString("\n")
|
|
||||||
if result.IsTruncated {
|
if result.IsTruncated {
|
||||||
s.WriteString(" 1. Re-run the backup process for this database\n")
|
s.WriteString("| 1. Re-run backup with current version (v3.42.12+) |\n")
|
||||||
s.WriteString(" 2. Check disk space on backup server\n")
|
s.WriteString("| 2. Check disk space on backup server |\n")
|
||||||
s.WriteString(" 3. Verify network stability for remote backups\n")
|
s.WriteString("| 3. Verify network stability for remote backups |\n")
|
||||||
}
|
}
|
||||||
if result.IsCorrupted {
|
if result.IsCorrupted {
|
||||||
s.WriteString(" 1. Verify backup was transferred completely\n")
|
s.WriteString("| 1. Verify backup was transferred completely |\n")
|
||||||
s.WriteString(" 2. Try restoring from a previous backup\n")
|
s.WriteString("| 2. Try restoring from a previous backup |\n")
|
||||||
}
|
}
|
||||||
|
s.WriteString("+" + strings.Repeat("-", 60) + "+\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.String()
|
return s.String()
|
||||||
|
|||||||
Reference in New Issue
Block a user