From 3e41d88445d95a148896ddf3b15aca4340160d63 Mon Sep 17 00:00:00 2001 From: Alexander Renz Date: Thu, 8 Jan 2026 09:42:01 +0100 Subject: [PATCH] v3.42.11: Replace all Unicode emojis with ASCII text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace all emoji characters with ASCII equivalents throughout codebase - Replace Unicode box-drawing characters (═║╔╗╚╝━─) with ASCII (+|-=) - Replace checkmarks (✓✗) with [OK]/[FAIL] markers - 59 files updated, 741 lines changed - Improves terminal compatibility and reduces visual noise --- cmd/catalog.go | 116 ++++++++--------- cmd/cleanup.go | 60 ++++----- cmd/cloud.go | 34 ++--- cmd/cpu.go | 4 +- cmd/dedup.go | 4 +- cmd/drill.go | 58 ++++----- cmd/engine.go | 4 +- cmd/install.go | 18 +-- cmd/pitr.go | 76 +++++------ cmd/placeholder.go | 120 ++++++++--------- cmd/restore.go | 46 +++---- cmd/rto.go | 56 ++++---- cmd/status.go | 4 +- cmd/verify.go | 26 ++-- internal/auth/helper.go | 6 +- internal/backup/engine.go | 8 +- internal/backup/incremental_test.go | 2 +- internal/checks/disk_check.go | 14 +- internal/checks/disk_check_bsd.go | 14 +- internal/checks/disk_check_netbsd.go | 14 +- internal/checks/disk_check_windows.go | 14 +- internal/checks/error_hints.go | 28 ++-- internal/checks/preflight.go | 10 +- internal/checks/report.go | 20 +-- internal/drill/drill.go | 6 +- internal/drill/engine.go | 58 ++++----- internal/encryption/encryption_test.go | 14 +- internal/installer/installer.go | 8 +- internal/notify/batch.go | 8 +- internal/notify/notify.go | 24 ++-- internal/notify/templates.go | 50 ++++---- internal/pitr/restore.go | 24 ++-- internal/progress/detailed.go | 6 +- internal/progress/progress.go | 28 ++-- internal/report/report.go | 6 +- internal/restore/diagnose.go | 38 +++--- internal/restore/engine.go | 14 +- internal/restore/error_report.go | 52 ++++---- internal/security/privileges.go | 2 +- internal/security/resources.go | 2 +- internal/security/resources_unix.go | 2 +- internal/tui/archive_browser.go | 26 ++-- internal/tui/backup_exec.go | 20 +-- internal/tui/backup_manager.go | 24 ++-- internal/tui/confirmation.go | 2 +- internal/tui/dbselector.go | 10 +- internal/tui/diagnose_view.go | 70 +++++----- internal/tui/dirpicker.go | 14 +- internal/tui/history.go | 12 +- internal/tui/input.go | 4 +- internal/tui/menu.go | 28 ++-- internal/tui/operations.go | 6 +- internal/tui/restore_exec.go | 16 +-- internal/tui/restore_preview.go | 74 +++++------ internal/tui/settings.go | 44 +++---- internal/tui/status.go | 14 +- internal/wal/pitr_config.go | 8 +- internal/wal/timeline.go | 6 +- main.go | 2 +- scripts/remove_all_unicode.sh | 171 +++++++++++++++++++++++++ scripts/remove_emojis.sh | 130 +++++++++++++++++++ 61 files changed, 1040 insertions(+), 739 deletions(-) create mode 100755 scripts/remove_all_unicode.sh create mode 100755 scripts/remove_emojis.sh diff --git a/cmd/catalog.go b/cmd/catalog.go index d6ea38e..9314700 100644 --- a/cmd/catalog.go +++ b/cmd/catalog.go @@ -252,8 +252,8 @@ func runCatalogSync(cmd *cobra.Command, args []string) error { } defer cat.Close() - fmt.Printf("📁 Syncing backups from: %s\n", absDir) - fmt.Printf("📊 Catalog database: %s\n\n", catalogDBPath) + fmt.Printf("[DIR] Syncing backups from: %s\n", absDir) + fmt.Printf("[STATS] Catalog database: %s\n\n", catalogDBPath) ctx := context.Background() result, err := cat.SyncFromDirectory(ctx, absDir) @@ -265,17 +265,17 @@ func runCatalogSync(cmd *cobra.Command, args []string) error { cat.SetLastSync(ctx) // Show results - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("=====================================================\n") fmt.Printf(" Sync Results\n") - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") - fmt.Printf(" ✅ Added: %d\n", result.Added) - fmt.Printf(" 🔄 Updated: %d\n", result.Updated) - fmt.Printf(" 🗑️ Removed: %d\n", result.Removed) + fmt.Printf("=====================================================\n") + fmt.Printf(" [OK] Added: %d\n", result.Added) + fmt.Printf(" [SYNC] Updated: %d\n", result.Updated) + fmt.Printf(" [DEL] Removed: %d\n", result.Removed) if result.Errors > 0 { - fmt.Printf(" ❌ Errors: %d\n", result.Errors) + fmt.Printf(" [FAIL] Errors: %d\n", result.Errors) } - fmt.Printf(" ⏱️ Duration: %.2fs\n", result.Duration) - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf(" [TIME] Duration: %.2fs\n", result.Duration) + fmt.Printf("=====================================================\n") // Show details if verbose if catalogVerbose && len(result.Details) > 0 { @@ -323,7 +323,7 @@ func runCatalogList(cmd *cobra.Command, args []string) error { // Table format fmt.Printf("%-30s %-12s %-10s %-20s %-10s %s\n", "DATABASE", "TYPE", "SIZE", "CREATED", "STATUS", "PATH") - fmt.Println(strings.Repeat("─", 120)) + fmt.Println(strings.Repeat("-", 120)) for _, entry := range entries { dbName := truncateString(entry.Database, 28) @@ -331,10 +331,10 @@ func runCatalogList(cmd *cobra.Command, args []string) error { status := string(entry.Status) if entry.VerifyValid != nil && *entry.VerifyValid { - status = "✓ verified" + status = "[OK] verified" } if entry.DrillSuccess != nil && *entry.DrillSuccess { - status = "✓ tested" + status = "[OK] tested" } fmt.Printf("%-30s %-12s %-10s %-20s %-10s %s\n", @@ -377,20 +377,20 @@ func runCatalogStats(cmd *cobra.Command, args []string) error { } // Table format - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("=====================================================\n") if catalogDatabase != "" { fmt.Printf(" Catalog Statistics: %s\n", catalogDatabase) } else { fmt.Printf(" Catalog Statistics\n") } - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n") + fmt.Printf("=====================================================\n\n") - fmt.Printf("📊 Total Backups: %d\n", stats.TotalBackups) - fmt.Printf("💾 Total Size: %s\n", stats.TotalSizeHuman) - fmt.Printf("📏 Average Size: %s\n", catalog.FormatSize(stats.AvgSize)) - fmt.Printf("⏱️ Average Duration: %.1fs\n", stats.AvgDuration) - fmt.Printf("✅ Verified: %d\n", stats.VerifiedCount) - fmt.Printf("🧪 Drill Tested: %d\n", stats.DrillTestedCount) + fmt.Printf("[STATS] Total Backups: %d\n", stats.TotalBackups) + fmt.Printf("[SAVE] Total Size: %s\n", stats.TotalSizeHuman) + fmt.Printf("[SIZE] Average Size: %s\n", catalog.FormatSize(stats.AvgSize)) + fmt.Printf("[TIME] Average Duration: %.1fs\n", stats.AvgDuration) + fmt.Printf("[OK] Verified: %d\n", stats.VerifiedCount) + fmt.Printf("[TEST] Drill Tested: %d\n", stats.DrillTestedCount) if stats.OldestBackup != nil { fmt.Printf("📅 Oldest Backup: %s\n", stats.OldestBackup.Format("2006-01-02 15:04")) @@ -400,27 +400,27 @@ func runCatalogStats(cmd *cobra.Command, args []string) error { } if len(stats.ByDatabase) > 0 && catalogDatabase == "" { - fmt.Printf("\n📁 By Database:\n") + fmt.Printf("\n[DIR] By Database:\n") for db, count := range stats.ByDatabase { fmt.Printf(" %-30s %d\n", db, count) } } if len(stats.ByType) > 0 { - fmt.Printf("\n📦 By Type:\n") + fmt.Printf("\n[PKG] By Type:\n") for t, count := range stats.ByType { fmt.Printf(" %-15s %d\n", t, count) } } if len(stats.ByStatus) > 0 { - fmt.Printf("\n📋 By Status:\n") + fmt.Printf("\n[LOG] By Status:\n") for s, count := range stats.ByStatus { fmt.Printf(" %-15s %d\n", s, count) } } - fmt.Printf("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("\n=====================================================\n") return nil } @@ -488,26 +488,26 @@ func runCatalogGaps(cmd *cobra.Command, args []string) error { } if len(allGaps) == 0 { - fmt.Printf("✅ No backup gaps detected (expected interval: %s)\n", interval) + fmt.Printf("[OK] No backup gaps detected (expected interval: %s)\n", interval) return nil } - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("=====================================================\n") fmt.Printf(" Backup Gaps Detected (expected interval: %s)\n", interval) - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n") + fmt.Printf("=====================================================\n\n") totalGaps := 0 criticalGaps := 0 for database, gaps := range allGaps { - fmt.Printf("📁 %s (%d gaps)\n", database, len(gaps)) + fmt.Printf("[DIR] %s (%d gaps)\n", database, len(gaps)) for _, gap := range gaps { totalGaps++ - icon := "ℹ️" + icon := "[INFO]" switch gap.Severity { case catalog.SeverityWarning: - icon = "⚠️" + icon = "[WARN]" case catalog.SeverityCritical: icon = "🚨" criticalGaps++ @@ -523,7 +523,7 @@ func runCatalogGaps(cmd *cobra.Command, args []string) error { fmt.Println() } - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("=====================================================\n") fmt.Printf("Total: %d gaps detected", totalGaps) if criticalGaps > 0 { fmt.Printf(" (%d critical)", criticalGaps) @@ -598,20 +598,20 @@ func runCatalogSearch(cmd *cobra.Command, args []string) error { fmt.Printf("Found %d matching backups:\n\n", len(entries)) for _, entry := range entries { - fmt.Printf("📁 %s\n", entry.Database) + fmt.Printf("[DIR] %s\n", entry.Database) fmt.Printf(" Path: %s\n", entry.BackupPath) fmt.Printf(" Type: %s | Size: %s | Created: %s\n", entry.DatabaseType, catalog.FormatSize(entry.SizeBytes), entry.CreatedAt.Format("2006-01-02 15:04:05")) if entry.Encrypted { - fmt.Printf(" 🔒 Encrypted\n") + fmt.Printf(" [LOCK] Encrypted\n") } if entry.VerifyValid != nil && *entry.VerifyValid { - fmt.Printf(" ✅ Verified: %s\n", entry.VerifiedAt.Format("2006-01-02 15:04")) + fmt.Printf(" [OK] Verified: %s\n", entry.VerifiedAt.Format("2006-01-02 15:04")) } if entry.DrillSuccess != nil && *entry.DrillSuccess { - fmt.Printf(" 🧪 Drill Tested: %s\n", entry.DrillTestedAt.Format("2006-01-02 15:04")) + fmt.Printf(" [TEST] Drill Tested: %s\n", entry.DrillTestedAt.Format("2006-01-02 15:04")) } fmt.Println() } @@ -655,64 +655,64 @@ func runCatalogInfo(cmd *cobra.Command, args []string) error { return nil } - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("=====================================================\n") fmt.Printf(" Backup Details\n") - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n") + fmt.Printf("=====================================================\n\n") - fmt.Printf("📁 Database: %s\n", entry.Database) + fmt.Printf("[DIR] Database: %s\n", entry.Database) fmt.Printf("🔧 Type: %s\n", entry.DatabaseType) - fmt.Printf("🖥️ Host: %s:%d\n", entry.Host, entry.Port) + fmt.Printf("[HOST] Host: %s:%d\n", entry.Host, entry.Port) fmt.Printf("📂 Path: %s\n", entry.BackupPath) - fmt.Printf("📦 Backup Type: %s\n", entry.BackupType) - fmt.Printf("💾 Size: %s (%d bytes)\n", catalog.FormatSize(entry.SizeBytes), entry.SizeBytes) - fmt.Printf("🔐 SHA256: %s\n", entry.SHA256) + fmt.Printf("[PKG] Backup Type: %s\n", entry.BackupType) + fmt.Printf("[SAVE] Size: %s (%d bytes)\n", catalog.FormatSize(entry.SizeBytes), entry.SizeBytes) + fmt.Printf("[HASH] SHA256: %s\n", entry.SHA256) fmt.Printf("📅 Created: %s\n", entry.CreatedAt.Format("2006-01-02 15:04:05 MST")) - fmt.Printf("⏱️ Duration: %.2fs\n", entry.Duration) - fmt.Printf("📋 Status: %s\n", entry.Status) + fmt.Printf("[TIME] Duration: %.2fs\n", entry.Duration) + fmt.Printf("[LOG] Status: %s\n", entry.Status) if entry.Compression != "" { - fmt.Printf("📦 Compression: %s\n", entry.Compression) + fmt.Printf("[PKG] Compression: %s\n", entry.Compression) } if entry.Encrypted { - fmt.Printf("🔒 Encrypted: yes\n") + fmt.Printf("[LOCK] Encrypted: yes\n") } if entry.CloudLocation != "" { - fmt.Printf("☁️ Cloud: %s\n", entry.CloudLocation) + fmt.Printf("[CLOUD] Cloud: %s\n", entry.CloudLocation) } if entry.RetentionPolicy != "" { fmt.Printf("📆 Retention: %s\n", entry.RetentionPolicy) } - fmt.Printf("\n📊 Verification:\n") + fmt.Printf("\n[STATS] Verification:\n") if entry.VerifiedAt != nil { - status := "❌ Failed" + status := "[FAIL] Failed" if entry.VerifyValid != nil && *entry.VerifyValid { - status = "✅ Valid" + status = "[OK] Valid" } fmt.Printf(" Status: %s (checked %s)\n", status, entry.VerifiedAt.Format("2006-01-02 15:04")) } else { - fmt.Printf(" Status: ⏳ Not verified\n") + fmt.Printf(" Status: [WAIT] Not verified\n") } - fmt.Printf("\n🧪 DR Drill Test:\n") + fmt.Printf("\n[TEST] DR Drill Test:\n") if entry.DrillTestedAt != nil { - status := "❌ Failed" + status := "[FAIL] Failed" if entry.DrillSuccess != nil && *entry.DrillSuccess { - status = "✅ Passed" + status = "[OK] Passed" } fmt.Printf(" Status: %s (tested %s)\n", status, entry.DrillTestedAt.Format("2006-01-02 15:04")) } else { - fmt.Printf(" Status: ⏳ Not tested\n") + fmt.Printf(" Status: [WAIT] Not tested\n") } if len(entry.Metadata) > 0 { - fmt.Printf("\n📝 Additional Metadata:\n") + fmt.Printf("\n[NOTE] Additional Metadata:\n") for k, v := range entry.Metadata { fmt.Printf(" %s: %s\n", k, v) } } - fmt.Printf("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("\n=====================================================\n") return nil } diff --git a/cmd/cleanup.go b/cmd/cleanup.go index b98932f..20d375d 100644 --- a/cmd/cleanup.go +++ b/cmd/cleanup.go @@ -115,7 +115,7 @@ func runCleanup(cmd *cobra.Command, args []string) error { DryRun: dryRun, } - fmt.Printf("🗑️ Cleanup Policy:\n") + fmt.Printf("[CLEANUP] Cleanup Policy:\n") fmt.Printf(" Directory: %s\n", backupDir) fmt.Printf(" Retention: %d days\n", policy.RetentionDays) fmt.Printf(" Min backups: %d\n", policy.MinBackups) @@ -142,16 +142,16 @@ func runCleanup(cmd *cobra.Command, args []string) error { } // Display results - fmt.Printf("📊 Results:\n") + fmt.Printf("[RESULTS] Results:\n") fmt.Printf(" Total backups: %d\n", result.TotalBackups) fmt.Printf(" Eligible for deletion: %d\n", result.EligibleForDeletion) if len(result.Deleted) > 0 { fmt.Printf("\n") if dryRun { - fmt.Printf("🔍 Would delete %d backup(s):\n", len(result.Deleted)) + fmt.Printf("[DRY-RUN] Would delete %d backup(s):\n", len(result.Deleted)) } else { - fmt.Printf("✅ Deleted %d backup(s):\n", len(result.Deleted)) + fmt.Printf("[OK] Deleted %d backup(s):\n", len(result.Deleted)) } for _, file := range result.Deleted { fmt.Printf(" - %s\n", filepath.Base(file)) @@ -159,33 +159,33 @@ func runCleanup(cmd *cobra.Command, args []string) error { } if len(result.Kept) > 0 && len(result.Kept) <= 10 { - fmt.Printf("\n📦 Kept %d backup(s):\n", len(result.Kept)) + fmt.Printf("\n[KEPT] Kept %d backup(s):\n", len(result.Kept)) for _, file := range result.Kept { fmt.Printf(" - %s\n", filepath.Base(file)) } } else if len(result.Kept) > 10 { - fmt.Printf("\n📦 Kept %d backup(s)\n", len(result.Kept)) + fmt.Printf("\n[KEPT] Kept %d backup(s)\n", len(result.Kept)) } if !dryRun && result.SpaceFreed > 0 { - fmt.Printf("\n💾 Space freed: %s\n", metadata.FormatSize(result.SpaceFreed)) + fmt.Printf("\n[FREED] Space freed: %s\n", metadata.FormatSize(result.SpaceFreed)) } if len(result.Errors) > 0 { - fmt.Printf("\n⚠️ Errors:\n") + fmt.Printf("\n[WARN] Errors:\n") for _, err := range result.Errors { fmt.Printf(" - %v\n", err) } } - fmt.Println(strings.Repeat("─", 50)) + fmt.Println(strings.Repeat("-", 50)) if dryRun { - fmt.Println("✅ Dry run completed (no files were deleted)") + fmt.Println("[OK] Dry run completed (no files were deleted)") } else if len(result.Deleted) > 0 { - fmt.Println("✅ Cleanup completed successfully") + fmt.Println("[OK] Cleanup completed successfully") } else { - fmt.Println("ℹ️ No backups eligible for deletion") + fmt.Println("[INFO] No backups eligible for deletion") } return nil @@ -212,7 +212,7 @@ func runCloudCleanup(ctx context.Context, uri string) error { return fmt.Errorf("invalid cloud URI: %w", err) } - fmt.Printf("☁️ Cloud Cleanup Policy:\n") + fmt.Printf("[CLOUD] Cloud Cleanup Policy:\n") fmt.Printf(" URI: %s\n", uri) fmt.Printf(" Provider: %s\n", cloudURI.Provider) fmt.Printf(" Bucket: %s\n", cloudURI.Bucket) @@ -295,7 +295,7 @@ func runCloudCleanup(ctx context.Context, uri string) error { } // Display results - fmt.Printf("📊 Results:\n") + fmt.Printf("[RESULTS] Results:\n") fmt.Printf(" Total backups: %d\n", totalBackups) fmt.Printf(" Eligible for deletion: %d\n", len(toDelete)) fmt.Printf(" Will keep: %d\n", len(toKeep)) @@ -303,9 +303,9 @@ func runCloudCleanup(ctx context.Context, uri string) error { if len(toDelete) > 0 { if dryRun { - fmt.Printf("🔍 Would delete %d backup(s):\n", len(toDelete)) + fmt.Printf("[DRY-RUN] Would delete %d backup(s):\n", len(toDelete)) } else { - fmt.Printf("🗑️ Deleting %d backup(s):\n", len(toDelete)) + fmt.Printf("[DELETE] Deleting %d backup(s):\n", len(toDelete)) } var totalSize int64 @@ -321,7 +321,7 @@ func runCloudCleanup(ctx context.Context, uri string) error { if !dryRun { if err := backend.Delete(ctx, backup.Key); err != nil { - fmt.Printf(" ❌ Error: %v\n", err) + fmt.Printf(" [FAIL] Error: %v\n", err) } else { deletedCount++ // Also try to delete metadata @@ -330,12 +330,12 @@ func runCloudCleanup(ctx context.Context, uri string) error { } } - fmt.Printf("\n💾 Space %s: %s\n", + fmt.Printf("\n[FREED] Space %s: %s\n", map[bool]string{true: "would be freed", false: "freed"}[dryRun], cloud.FormatSize(totalSize)) if !dryRun && deletedCount > 0 { - fmt.Printf("✅ Successfully deleted %d backup(s)\n", deletedCount) + fmt.Printf("[OK] Successfully deleted %d backup(s)\n", deletedCount) } } else { fmt.Println("No backups eligible for deletion") @@ -405,7 +405,7 @@ func runGFSCleanup(backupDir string) error { } // Display tier breakdown - fmt.Printf("📊 Backup Classification:\n") + fmt.Printf("[STATS] Backup Classification:\n") fmt.Printf(" Yearly: %d\n", result.YearlyKept) fmt.Printf(" Monthly: %d\n", result.MonthlyKept) fmt.Printf(" Weekly: %d\n", result.WeeklyKept) @@ -416,9 +416,9 @@ func runGFSCleanup(backupDir string) error { // Display deletions if len(result.Deleted) > 0 { if dryRun { - fmt.Printf("🔍 Would delete %d backup(s):\n", len(result.Deleted)) + fmt.Printf("[SEARCH] Would delete %d backup(s):\n", len(result.Deleted)) } else { - fmt.Printf("✅ Deleted %d backup(s):\n", len(result.Deleted)) + fmt.Printf("[OK] Deleted %d backup(s):\n", len(result.Deleted)) } for _, file := range result.Deleted { fmt.Printf(" - %s\n", filepath.Base(file)) @@ -427,7 +427,7 @@ func runGFSCleanup(backupDir string) error { // Display kept backups (limited display) if len(result.Kept) > 0 && len(result.Kept) <= 15 { - fmt.Printf("\n📦 Kept %d backup(s):\n", len(result.Kept)) + fmt.Printf("\n[PKG] Kept %d backup(s):\n", len(result.Kept)) for _, file := range result.Kept { // Show tier classification info, _ := os.Stat(file) @@ -440,28 +440,28 @@ func runGFSCleanup(backupDir string) error { } } } else if len(result.Kept) > 15 { - fmt.Printf("\n📦 Kept %d backup(s)\n", len(result.Kept)) + fmt.Printf("\n[PKG] Kept %d backup(s)\n", len(result.Kept)) } if !dryRun && result.SpaceFreed > 0 { - fmt.Printf("\n💾 Space freed: %s\n", metadata.FormatSize(result.SpaceFreed)) + fmt.Printf("\n[SAVE] Space freed: %s\n", metadata.FormatSize(result.SpaceFreed)) } if len(result.Errors) > 0 { - fmt.Printf("\n⚠️ Errors:\n") + fmt.Printf("\n[WARN] Errors:\n") for _, err := range result.Errors { fmt.Printf(" - %v\n", err) } } - fmt.Println(strings.Repeat("─", 50)) + fmt.Println(strings.Repeat("-", 50)) if dryRun { - fmt.Println("✅ GFS dry run completed (no files were deleted)") + fmt.Println("[OK] GFS dry run completed (no files were deleted)") } else if len(result.Deleted) > 0 { - fmt.Println("✅ GFS cleanup completed successfully") + fmt.Println("[OK] GFS cleanup completed successfully") } else { - fmt.Println("ℹ️ No backups eligible for deletion under GFS policy") + fmt.Println("[INFO] No backups eligible for deletion under GFS policy") } return nil diff --git a/cmd/cloud.go b/cmd/cloud.go index 65e813e..3f60f03 100644 --- a/cmd/cloud.go +++ b/cmd/cloud.go @@ -189,12 +189,12 @@ func runCloudUpload(cmd *cobra.Command, args []string) error { } } - fmt.Printf("☁️ Uploading %d file(s) to %s...\n\n", len(files), backend.Name()) + fmt.Printf("[CLOUD] Uploading %d file(s) to %s...\n\n", len(files), backend.Name()) successCount := 0 for _, localPath := range files { filename := filepath.Base(localPath) - fmt.Printf("📤 %s\n", filename) + fmt.Printf("[UPLOAD] %s\n", filename) // Progress callback var lastPercent int @@ -214,21 +214,21 @@ func runCloudUpload(cmd *cobra.Command, args []string) error { err := backend.Upload(ctx, localPath, filename, progress) if err != nil { - fmt.Printf(" ❌ Failed: %v\n\n", err) + fmt.Printf(" [FAIL] Failed: %v\n\n", err) continue } // Get file size if info, err := os.Stat(localPath); err == nil { - fmt.Printf(" ✅ Uploaded (%s)\n\n", cloud.FormatSize(info.Size())) + fmt.Printf(" [OK] Uploaded (%s)\n\n", cloud.FormatSize(info.Size())) } else { - fmt.Printf(" ✅ Uploaded\n\n") + fmt.Printf(" [OK] Uploaded\n\n") } successCount++ } - fmt.Println(strings.Repeat("─", 50)) - fmt.Printf("✅ Successfully uploaded %d/%d file(s)\n", successCount, len(files)) + fmt.Println(strings.Repeat("-", 50)) + fmt.Printf("[OK] Successfully uploaded %d/%d file(s)\n", successCount, len(files)) return nil } @@ -248,8 +248,8 @@ func runCloudDownload(cmd *cobra.Command, args []string) error { localPath = filepath.Join(localPath, filepath.Base(remotePath)) } - fmt.Printf("☁️ Downloading from %s...\n\n", backend.Name()) - fmt.Printf("📥 %s → %s\n", remotePath, localPath) + fmt.Printf("[CLOUD] Downloading from %s...\n\n", backend.Name()) + fmt.Printf("[DOWNLOAD] %s -> %s\n", remotePath, localPath) // Progress callback var lastPercent int @@ -274,9 +274,9 @@ func runCloudDownload(cmd *cobra.Command, args []string) error { // Get file size if info, err := os.Stat(localPath); err == nil { - fmt.Printf(" ✅ Downloaded (%s)\n", cloud.FormatSize(info.Size())) + fmt.Printf(" [OK] Downloaded (%s)\n", cloud.FormatSize(info.Size())) } else { - fmt.Printf(" ✅ Downloaded\n") + fmt.Printf(" [OK] Downloaded\n") } return nil @@ -294,7 +294,7 @@ func runCloudList(cmd *cobra.Command, args []string) error { prefix = args[0] } - fmt.Printf("☁️ Listing backups in %s/%s...\n\n", backend.Name(), cloudBucket) + fmt.Printf("[CLOUD] Listing backups in %s/%s...\n\n", backend.Name(), cloudBucket) backups, err := backend.List(ctx, prefix) if err != nil { @@ -311,7 +311,7 @@ func runCloudList(cmd *cobra.Command, args []string) error { totalSize += backup.Size if cloudVerbose { - fmt.Printf("📦 %s\n", backup.Name) + fmt.Printf("[FILE] %s\n", backup.Name) fmt.Printf(" Size: %s\n", cloud.FormatSize(backup.Size)) fmt.Printf(" Modified: %s\n", backup.LastModified.Format(time.RFC3339)) if backup.StorageClass != "" { @@ -328,7 +328,7 @@ func runCloudList(cmd *cobra.Command, args []string) error { } } - fmt.Println(strings.Repeat("─", 50)) + fmt.Println(strings.Repeat("-", 50)) fmt.Printf("Total: %d backup(s), %s\n", len(backups), cloud.FormatSize(totalSize)) return nil @@ -360,7 +360,7 @@ func runCloudDelete(cmd *cobra.Command, args []string) error { // Confirmation prompt if !cloudConfirm { - fmt.Printf("⚠️ Delete %s (%s) from cloud storage?\n", remotePath, cloud.FormatSize(size)) + fmt.Printf("[WARN] Delete %s (%s) from cloud storage?\n", remotePath, cloud.FormatSize(size)) fmt.Print("Type 'yes' to confirm: ") var response string fmt.Scanln(&response) @@ -370,14 +370,14 @@ func runCloudDelete(cmd *cobra.Command, args []string) error { } } - fmt.Printf("🗑️ Deleting %s...\n", remotePath) + fmt.Printf("[DELETE] Deleting %s...\n", remotePath) err = backend.Delete(ctx, remotePath) if err != nil { return fmt.Errorf("delete failed: %w", err) } - fmt.Printf("✅ Deleted %s (%s)\n", remotePath, cloud.FormatSize(size)) + fmt.Printf("[OK] Deleted %s (%s)\n", remotePath, cloud.FormatSize(size)) return nil } diff --git a/cmd/cpu.go b/cmd/cpu.go index 5e31eed..db9330a 100755 --- a/cmd/cpu.go +++ b/cmd/cpu.go @@ -61,10 +61,10 @@ func runCPUInfo(ctx context.Context) error { // Show current vs optimal if cfg.AutoDetectCores { - fmt.Println("\n✅ CPU optimization is enabled") + fmt.Println("\n[OK] CPU optimization is enabled") fmt.Println("Job counts are automatically optimized based on detected hardware") } else { - fmt.Println("\n⚠️ CPU optimization is disabled") + fmt.Println("\n[WARN] CPU optimization is disabled") fmt.Println("Consider enabling --auto-detect-cores for better performance") } diff --git a/cmd/dedup.go b/cmd/dedup.go index 5abebaa..ece7d0e 100644 --- a/cmd/dedup.go +++ b/cmd/dedup.go @@ -372,9 +372,9 @@ func runDedupRestore(cmd *cobra.Command, args []string) error { // Verify hash if manifest.SHA256 != "" { if restoredHash == manifest.SHA256 { - fmt.Printf(" Verification: ✓ SHA-256 matches\n") + fmt.Printf(" Verification: [OK] SHA-256 matches\n") } else { - fmt.Printf(" Verification: ✗ SHA-256 MISMATCH!\n") + fmt.Printf(" Verification: [FAIL] SHA-256 MISMATCH!\n") fmt.Printf(" Expected: %s\n", manifest.SHA256) fmt.Printf(" Got: %s\n", restoredHash) return fmt.Errorf("integrity verification failed") diff --git a/cmd/drill.go b/cmd/drill.go index 2c196f0..4d2d18e 100644 --- a/cmd/drill.go +++ b/cmd/drill.go @@ -318,7 +318,7 @@ func runDrillList(cmd *cobra.Command, args []string) error { } fmt.Printf("%-15s %-40s %-20s %s\n", "ID", "NAME", "IMAGE", "STATUS") - fmt.Println(strings.Repeat("─", 100)) + fmt.Println(strings.Repeat("-", 100)) for _, c := range containers { fmt.Printf("%-15s %-40s %-20s %s\n", @@ -345,7 +345,7 @@ func runDrillCleanup(cmd *cobra.Command, args []string) error { return err } - fmt.Println("✅ Cleanup completed") + fmt.Println("[OK] Cleanup completed") return nil } @@ -369,32 +369,32 @@ func runDrillReport(cmd *cobra.Command, args []string) error { func printDrillResult(result *drill.DrillResult) { fmt.Printf("\n") - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("=====================================================\n") fmt.Printf(" DR Drill Report: %s\n", result.DrillID) - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n") + fmt.Printf("=====================================================\n\n") - status := "✅ PASSED" + status := "[OK] PASSED" if !result.Success { - status = "❌ FAILED" + status = "[FAIL] FAILED" } else if result.Status == drill.StatusPartial { - status = "⚠️ PARTIAL" + status = "[WARN] PARTIAL" } - fmt.Printf("📋 Status: %s\n", status) - fmt.Printf("💾 Backup: %s\n", filepath.Base(result.BackupPath)) - fmt.Printf("🗄️ Database: %s (%s)\n", result.DatabaseName, result.DatabaseType) - fmt.Printf("⏱️ Duration: %.2fs\n", result.Duration) + fmt.Printf("[LOG] Status: %s\n", status) + fmt.Printf("[SAVE] Backup: %s\n", filepath.Base(result.BackupPath)) + fmt.Printf("[DB] Database: %s (%s)\n", result.DatabaseName, result.DatabaseType) + fmt.Printf("[TIME] Duration: %.2fs\n", result.Duration) fmt.Printf("📅 Started: %s\n", result.StartTime.Format(time.RFC3339)) fmt.Printf("\n") // Phases - fmt.Printf("📊 Phases:\n") + fmt.Printf("[STATS] Phases:\n") for _, phase := range result.Phases { - icon := "✅" + icon := "[OK]" if phase.Status == "failed" { - icon = "❌" + icon = "[FAIL]" } else if phase.Status == "running" { - icon = "🔄" + icon = "[SYNC]" } fmt.Printf(" %s %-20s (%.2fs) %s\n", icon, phase.Name, phase.Duration, phase.Message) } @@ -412,10 +412,10 @@ func printDrillResult(result *drill.DrillResult) { fmt.Printf("\n") // RTO - fmt.Printf("⏱️ RTO Analysis:\n") - rtoIcon := "✅" + fmt.Printf("[TIME] RTO Analysis:\n") + rtoIcon := "[OK]" if !result.RTOMet { - rtoIcon = "❌" + rtoIcon = "[FAIL]" } fmt.Printf(" Actual RTO: %.2fs\n", result.ActualRTO) fmt.Printf(" Target RTO: %.0fs\n", result.TargetRTO) @@ -424,11 +424,11 @@ func printDrillResult(result *drill.DrillResult) { // Validation results if len(result.ValidationResults) > 0 { - fmt.Printf("🔍 Validation Queries:\n") + fmt.Printf("[SEARCH] Validation Queries:\n") for _, vr := range result.ValidationResults { - icon := "✅" + icon := "[OK]" if !vr.Success { - icon = "❌" + icon = "[FAIL]" } fmt.Printf(" %s %s: %s\n", icon, vr.Name, vr.Result) if vr.Error != "" { @@ -440,11 +440,11 @@ func printDrillResult(result *drill.DrillResult) { // Check results if len(result.CheckResults) > 0 { - fmt.Printf("✓ Checks:\n") + fmt.Printf("[OK] Checks:\n") for _, cr := range result.CheckResults { - icon := "✅" + icon := "[OK]" if !cr.Success { - icon = "❌" + icon = "[FAIL]" } fmt.Printf(" %s %s\n", icon, cr.Message) } @@ -453,7 +453,7 @@ func printDrillResult(result *drill.DrillResult) { // Errors and warnings if len(result.Errors) > 0 { - fmt.Printf("❌ Errors:\n") + fmt.Printf("[FAIL] Errors:\n") for _, e := range result.Errors { fmt.Printf(" • %s\n", e) } @@ -461,7 +461,7 @@ func printDrillResult(result *drill.DrillResult) { } if len(result.Warnings) > 0 { - fmt.Printf("⚠️ Warnings:\n") + fmt.Printf("[WARN] Warnings:\n") for _, w := range result.Warnings { fmt.Printf(" • %s\n", w) } @@ -470,14 +470,14 @@ func printDrillResult(result *drill.DrillResult) { // Container info if result.ContainerKept { - fmt.Printf("📦 Container kept: %s\n", result.ContainerID[:12]) + fmt.Printf("[PKG] Container kept: %s\n", result.ContainerID[:12]) fmt.Printf(" Connect with: docker exec -it %s bash\n", result.ContainerID[:12]) fmt.Printf("\n") } - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("=====================================================\n") fmt.Printf(" %s\n", result.Message) - fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + fmt.Printf("=====================================================\n") } func updateCatalogWithDrillResult(ctx context.Context, backupPath string, result *drill.DrillResult) { diff --git a/cmd/engine.go b/cmd/engine.go index f3326e1..e8bfa5b 100644 --- a/cmd/engine.go +++ b/cmd/engine.go @@ -63,9 +63,9 @@ func runEngineList(cmd *cobra.Command, args []string) error { continue } - status := "✓ Available" + status := "[Y] Available" if !avail.Available { - status = "✗ Not available" + status = "[N] Not available" } fmt.Printf("\n%s (%s)\n", info.Name, info.Description) diff --git a/cmd/install.go b/cmd/install.go index eaf2caf..c26d289 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -176,12 +176,12 @@ func runInstallStatus(ctx context.Context) error { } fmt.Println() - fmt.Println("📦 DBBackup Installation Status") - fmt.Println(strings.Repeat("═", 50)) + fmt.Println("[STATUS] DBBackup Installation Status") + fmt.Println(strings.Repeat("=", 50)) if clusterStatus.Installed { fmt.Println() - fmt.Println("🔹 Cluster Backup:") + fmt.Println(" * Cluster Backup:") fmt.Printf(" Service: %s\n", formatStatus(clusterStatus.Installed, clusterStatus.Active)) fmt.Printf(" Timer: %s\n", formatStatus(clusterStatus.TimerEnabled, clusterStatus.TimerActive)) if clusterStatus.NextRun != "" { @@ -192,7 +192,7 @@ func runInstallStatus(ctx context.Context) error { } } else { fmt.Println() - fmt.Println("❌ No systemd services installed") + fmt.Println("[NONE] No systemd services installed") fmt.Println() fmt.Println("Run 'sudo dbbackup install' to install as a systemd service") } @@ -200,13 +200,13 @@ func runInstallStatus(ctx context.Context) error { // Check for exporter if _, err := os.Stat("/etc/systemd/system/dbbackup-exporter.service"); err == nil { fmt.Println() - fmt.Println("🔹 Metrics Exporter:") + fmt.Println(" * Metrics Exporter:") // Check if exporter is active using systemctl cmd := exec.CommandContext(ctx, "systemctl", "is-active", "dbbackup-exporter") if err := cmd.Run(); err == nil { - fmt.Printf(" Service: ✅ active\n") + fmt.Printf(" Service: [OK] active\n") } else { - fmt.Printf(" Service: ⚪ inactive\n") + fmt.Printf(" Service: [-] inactive\n") } } @@ -219,9 +219,9 @@ func formatStatus(installed, active bool) string { return "not installed" } if active { - return "✅ active" + return "[OK] active" } - return "⚪ inactive" + return "[-] inactive" } func expandSchedule(schedule string) string { diff --git a/cmd/pitr.go b/cmd/pitr.go index 32509ad..54de8a1 100644 --- a/cmd/pitr.go +++ b/cmd/pitr.go @@ -436,7 +436,7 @@ func runPITREnable(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to enable PITR: %w", err) } - log.Info("✅ PITR enabled successfully!") + log.Info("[OK] PITR enabled successfully!") log.Info("") log.Info("Next steps:") log.Info("1. Restart PostgreSQL: sudo systemctl restart postgresql") @@ -463,7 +463,7 @@ func runPITRDisable(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to disable PITR: %w", err) } - log.Info("✅ PITR disabled successfully!") + log.Info("[OK] PITR disabled successfully!") log.Info("PostgreSQL restart required: sudo systemctl restart postgresql") return nil @@ -483,15 +483,15 @@ func runPITRStatus(cmd *cobra.Command, args []string) error { } // Display PITR configuration - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("======================================================") fmt.Println(" Point-in-Time Recovery (PITR) Status") - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("======================================================") fmt.Println() if config.Enabled { - fmt.Println("Status: ✅ ENABLED") + fmt.Println("Status: [OK] ENABLED") } else { - fmt.Println("Status: ❌ DISABLED") + fmt.Println("Status: [FAIL] DISABLED") } fmt.Printf("WAL Level: %s\n", config.WALLevel) @@ -510,7 +510,7 @@ func runPITRStatus(cmd *cobra.Command, args []string) error { // Extract archive dir from command (simple parsing) fmt.Println() fmt.Println("WAL Archive Statistics:") - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("======================================================") // TODO: Parse archive dir and show stats fmt.Println(" (Use 'dbbackup wal list --archive-dir ' to view archives)") } @@ -574,13 +574,13 @@ func runWALList(cmd *cobra.Command, args []string) error { } // Display archives - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("======================================================") fmt.Printf(" WAL Archives (%d files)\n", len(archives)) - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("======================================================") fmt.Println() fmt.Printf("%-28s %10s %10s %8s %s\n", "WAL Filename", "Timeline", "Segment", "Size", "Archived At") - fmt.Println("────────────────────────────────────────────────────────────────────────────────") + fmt.Println("--------------------------------------------------------------------------------") for _, archive := range archives { size := formatWALSize(archive.ArchivedSize) @@ -644,7 +644,7 @@ func runWALCleanup(cmd *cobra.Command, args []string) error { return fmt.Errorf("WAL cleanup failed: %w", err) } - log.Info("✅ WAL cleanup completed", "deleted", deleted, "retention_days", archiveConfig.RetentionDays) + log.Info("[OK] WAL cleanup completed", "deleted", deleted, "retention_days", archiveConfig.RetentionDays) return nil } @@ -671,7 +671,7 @@ func runWALTimeline(cmd *cobra.Command, args []string) error { // Display timeline details if len(history.Timelines) > 0 { fmt.Println("\nTimeline Details:") - fmt.Println("═════════════════") + fmt.Println("=================") for _, tl := range history.Timelines { fmt.Printf("\nTimeline %d:\n", tl.TimelineID) if tl.ParentTimeline > 0 { @@ -690,7 +690,7 @@ func runWALTimeline(cmd *cobra.Command, args []string) error { fmt.Printf(" Created: %s\n", tl.CreatedAt.Format("2006-01-02 15:04:05")) } if tl.TimelineID == history.CurrentTimeline { - fmt.Printf(" Status: ⚡ CURRENT\n") + fmt.Printf(" Status: [CURR] CURRENT\n") } } } @@ -759,15 +759,15 @@ func runBinlogList(cmd *cobra.Command, args []string) error { return nil } - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("=============================================================") fmt.Printf(" Binary Log Files (%s)\n", bm.ServerType()) - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("=============================================================") fmt.Println() if len(binlogs) > 0 { fmt.Println("Source Directory:") fmt.Printf("%-24s %10s %-19s %-19s %s\n", "Filename", "Size", "Start Time", "End Time", "Format") - fmt.Println("────────────────────────────────────────────────────────────────────────────────") + fmt.Println("--------------------------------------------------------------------------------") var totalSize int64 for _, b := range binlogs { @@ -797,7 +797,7 @@ func runBinlogList(cmd *cobra.Command, args []string) error { fmt.Println() fmt.Println("Archived Binlogs:") fmt.Printf("%-24s %10s %-19s %s\n", "Original", "Size", "Archived At", "Flags") - fmt.Println("────────────────────────────────────────────────────────────────────────────────") + fmt.Println("--------------------------------------------------------------------------------") var totalSize int64 for _, a := range archived { @@ -914,7 +914,7 @@ func runBinlogArchive(cmd *cobra.Command, args []string) error { bm.SaveArchiveMetadata(allArchived) } - log.Info("✅ Binlog archiving completed", "archived", len(newArchives)) + log.Info("[OK] Binlog archiving completed", "archived", len(newArchives)) return nil } @@ -1014,15 +1014,15 @@ func runBinlogValidate(cmd *cobra.Command, args []string) error { return fmt.Errorf("validating binlog chain: %w", err) } - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("=============================================================") fmt.Println(" Binlog Chain Validation") - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("=============================================================") fmt.Println() if validation.Valid { - fmt.Println("Status: ✅ VALID - Binlog chain is complete") + fmt.Println("Status: [OK] VALID - Binlog chain is complete") } else { - fmt.Println("Status: ❌ INVALID - Binlog chain has gaps") + fmt.Println("Status: [FAIL] INVALID - Binlog chain has gaps") } fmt.Printf("Files: %d binlog files\n", validation.LogCount) @@ -1055,7 +1055,7 @@ func runBinlogValidate(cmd *cobra.Command, args []string) error { fmt.Println() fmt.Println("Errors:") for _, e := range validation.Errors { - fmt.Printf(" ✗ %s\n", e) + fmt.Printf(" [FAIL] %s\n", e) } } @@ -1094,9 +1094,9 @@ func runBinlogPosition(cmd *cobra.Command, args []string) error { } defer rows.Close() - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("=============================================================") fmt.Println(" Current Binary Log Position") - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("=============================================================") fmt.Println() if rows.Next() { @@ -1178,24 +1178,24 @@ func runMySQLPITRStatus(cmd *cobra.Command, args []string) error { return fmt.Errorf("getting PITR status: %w", err) } - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("=============================================================") fmt.Printf(" MySQL/MariaDB PITR Status (%s)\n", status.DatabaseType) - fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println("=============================================================") fmt.Println() if status.Enabled { - fmt.Println("PITR Status: ✅ ENABLED") + fmt.Println("PITR Status: [OK] ENABLED") } else { - fmt.Println("PITR Status: ❌ NOT CONFIGURED") + fmt.Println("PITR Status: [FAIL] NOT CONFIGURED") } // Get binary logging status var logBin string db.QueryRowContext(ctx, "SELECT @@log_bin").Scan(&logBin) if logBin == "1" || logBin == "ON" { - fmt.Println("Binary Logging: ✅ ENABLED") + fmt.Println("Binary Logging: [OK] ENABLED") } else { - fmt.Println("Binary Logging: ❌ DISABLED") + fmt.Println("Binary Logging: [FAIL] DISABLED") } fmt.Printf("Binlog Format: %s\n", status.LogLevel) @@ -1205,14 +1205,14 @@ func runMySQLPITRStatus(cmd *cobra.Command, args []string) error { if status.DatabaseType == pitr.DatabaseMariaDB { db.QueryRowContext(ctx, "SELECT @@gtid_current_pos").Scan(>idMode) if gtidMode != "" { - fmt.Println("GTID Mode: ✅ ENABLED") + fmt.Println("GTID Mode: [OK] ENABLED") } else { - fmt.Println("GTID Mode: ❌ DISABLED") + fmt.Println("GTID Mode: [FAIL] DISABLED") } } else { db.QueryRowContext(ctx, "SELECT @@gtid_mode").Scan(>idMode) if gtidMode == "ON" { - fmt.Println("GTID Mode: ✅ ENABLED") + fmt.Println("GTID Mode: [OK] ENABLED") } else { fmt.Printf("GTID Mode: %s\n", gtidMode) } @@ -1237,12 +1237,12 @@ func runMySQLPITRStatus(cmd *cobra.Command, args []string) error { fmt.Println() fmt.Println("PITR Requirements:") if logBin == "1" || logBin == "ON" { - fmt.Println(" ✅ Binary logging enabled") + fmt.Println(" [OK] Binary logging enabled") } else { - fmt.Println(" ❌ Binary logging must be enabled (log_bin = mysql-bin)") + fmt.Println(" [FAIL] Binary logging must be enabled (log_bin = mysql-bin)") } if status.LogLevel == "ROW" { - fmt.Println(" ✅ Row-based logging (recommended)") + fmt.Println(" [OK] Row-based logging (recommended)") } else { fmt.Printf(" ⚠ binlog_format = %s (ROW recommended for PITR)\n", status.LogLevel) } @@ -1299,7 +1299,7 @@ func runMySQLPITREnable(cmd *cobra.Command, args []string) error { return fmt.Errorf("enabling PITR: %w", err) } - log.Info("✅ MySQL PITR enabled successfully!") + log.Info("[OK] MySQL PITR enabled successfully!") log.Info("") log.Info("Next steps:") log.Info("1. Start binlog archiving: dbbackup binlog watch --archive-dir " + mysqlArchiveDir) diff --git a/cmd/placeholder.go b/cmd/placeholder.go index 55ce025..c4622fe 100755 --- a/cmd/placeholder.go +++ b/cmd/placeholder.go @@ -141,7 +141,7 @@ func runList(ctx context.Context) error { continue } - fmt.Printf("📦 %s\n", file.Name) + fmt.Printf("[FILE] %s\n", file.Name) fmt.Printf(" Size: %s\n", formatFileSize(stat.Size())) fmt.Printf(" Modified: %s\n", stat.ModTime().Format("2006-01-02 15:04:05")) fmt.Printf(" Type: %s\n", getBackupType(file.Name)) @@ -237,56 +237,56 @@ func runPreflight(ctx context.Context) error { totalChecks := 6 // 1. Database connectivity check - fmt.Print("🔗 Database connectivity... ") + fmt.Print("[1] Database connectivity... ") if err := testDatabaseConnection(); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } // 2. Required tools check - fmt.Print("🛠️ Required tools (pg_dump/pg_restore)... ") + fmt.Print("[2] Required tools (pg_dump/pg_restore)... ") if err := checkRequiredTools(); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } // 3. Backup directory check - fmt.Print("📁 Backup directory access... ") + fmt.Print("[3] Backup directory access... ") if err := checkBackupDirectory(); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } // 4. Disk space check - fmt.Print("💾 Available disk space... ") + fmt.Print("[4] Available disk space... ") if err := checkDiskSpace(); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } // 5. Permissions check - fmt.Print("🔐 File permissions... ") + fmt.Print("[5] File permissions... ") if err := checkPermissions(); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } // 6. CPU/Memory resources check - fmt.Print("🖥️ System resources... ") + fmt.Print("[6] System resources... ") if err := checkSystemResources(); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } @@ -294,10 +294,10 @@ func runPreflight(ctx context.Context) error { fmt.Printf("Results: %d/%d checks passed\n", checksPassed, totalChecks) if checksPassed == totalChecks { - fmt.Println("🎉 All preflight checks passed! System is ready for backup operations.") + fmt.Println("[SUCCESS] All preflight checks passed! System is ready for backup operations.") return nil } else { - fmt.Printf("⚠️ %d check(s) failed. Please address the issues before running backups.\n", totalChecks-checksPassed) + fmt.Printf("[WARN] %d check(s) failed. Please address the issues before running backups.\n", totalChecks-checksPassed) return fmt.Errorf("preflight checks failed: %d/%d passed", checksPassed, totalChecks) } } @@ -414,44 +414,44 @@ func runRestore(ctx context.Context, archiveName string) error { fmt.Println() // Show warning - fmt.Println("⚠️ WARNING: This will restore data to the target database.") + fmt.Println("[WARN] WARNING: This will restore data to the target database.") fmt.Println(" Existing data may be overwritten or merged depending on the restore method.") fmt.Println() // For safety, show what would be done without actually doing it switch archiveType { case "Single Database (.dump)": - fmt.Println("🔄 Would execute: pg_restore to restore single database") + fmt.Println("[EXEC] Would execute: pg_restore to restore single database") fmt.Printf(" Command: pg_restore -h %s -p %d -U %s -d %s --verbose %s\n", cfg.Host, cfg.Port, cfg.User, cfg.Database, archivePath) case "Single Database (.dump.gz)": - fmt.Println("🔄 Would execute: gunzip and pg_restore to restore single database") + fmt.Println("[EXEC] Would execute: gunzip and pg_restore to restore single database") fmt.Printf(" Command: gunzip -c %s | pg_restore -h %s -p %d -U %s -d %s --verbose\n", archivePath, cfg.Host, cfg.Port, cfg.User, cfg.Database) case "SQL Script (.sql)": if cfg.IsPostgreSQL() { - fmt.Println("🔄 Would execute: psql to run SQL script") + fmt.Println("[EXEC] Would execute: psql to run SQL script") fmt.Printf(" Command: psql -h %s -p %d -U %s -d %s -f %s\n", cfg.Host, cfg.Port, cfg.User, cfg.Database, archivePath) } else if cfg.IsMySQL() { - fmt.Println("🔄 Would execute: mysql to run SQL script") + fmt.Println("[EXEC] Would execute: mysql to run SQL script") fmt.Printf(" Command: %s\n", mysqlRestoreCommand(archivePath, false)) } else { - fmt.Println("🔄 Would execute: SQL client to run script (database type unknown)") + fmt.Println("[EXEC] Would execute: SQL client to run script (database type unknown)") } case "SQL Script (.sql.gz)": if cfg.IsPostgreSQL() { - fmt.Println("🔄 Would execute: gunzip and psql to run SQL script") + fmt.Println("[EXEC] Would execute: gunzip and psql to run SQL script") fmt.Printf(" Command: gunzip -c %s | psql -h %s -p %d -U %s -d %s\n", archivePath, cfg.Host, cfg.Port, cfg.User, cfg.Database) } else if cfg.IsMySQL() { - fmt.Println("🔄 Would execute: gunzip and mysql to run SQL script") + fmt.Println("[EXEC] Would execute: gunzip and mysql to run SQL script") fmt.Printf(" Command: %s\n", mysqlRestoreCommand(archivePath, true)) } else { - fmt.Println("🔄 Would execute: gunzip and SQL client to run script (database type unknown)") + fmt.Println("[EXEC] Would execute: gunzip and SQL client to run script (database type unknown)") } case "Cluster Backup (.tar.gz)": - fmt.Println("🔄 Would execute: Extract and restore cluster backup") + fmt.Println("[EXEC] Would execute: Extract and restore cluster backup") fmt.Println(" Steps:") fmt.Println(" 1. Extract tar.gz archive") fmt.Println(" 2. Restore global objects (roles, tablespaces)") @@ -461,7 +461,7 @@ func runRestore(ctx context.Context, archiveName string) error { } fmt.Println() - fmt.Println("🛡️ SAFETY MODE: Restore command is in preview mode.") + fmt.Println("[SAFETY] SAFETY MODE: Restore command is in preview mode.") fmt.Println(" This shows what would be executed without making changes.") fmt.Println(" To enable actual restore, add --confirm flag (not yet implemented).") @@ -520,25 +520,25 @@ func runVerify(ctx context.Context, archiveName string) error { checksPassed := 0 // Basic file existence and readability - fmt.Print("📁 File accessibility... ") + fmt.Print("[CHK] File accessibility... ") if file, err := os.Open(archivePath); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { file.Close() - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } checksRun++ // File size sanity check - fmt.Print("📏 File size check... ") + fmt.Print("[CHK] File size check... ") if stat.Size() == 0 { - fmt.Println("❌ FAILED: File is empty") + fmt.Println("[FAIL] FAILED: File is empty") } else if stat.Size() < 100 { - fmt.Println("⚠️ WARNING: File is very small (< 100 bytes)") + fmt.Println("[WARN] WARNING: File is very small (< 100 bytes)") checksPassed++ } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } checksRun++ @@ -546,51 +546,51 @@ func runVerify(ctx context.Context, archiveName string) error { // Type-specific verification switch archiveType { case "Single Database (.dump)": - fmt.Print("🔍 PostgreSQL dump format check... ") + fmt.Print("[CHK] PostgreSQL dump format check... ") if err := verifyPgDump(archivePath); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } checksRun++ case "Single Database (.dump.gz)": - fmt.Print("🔍 PostgreSQL dump format check (gzip)... ") + fmt.Print("[CHK] PostgreSQL dump format check (gzip)... ") if err := verifyPgDumpGzip(archivePath); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } checksRun++ case "SQL Script (.sql)": - fmt.Print("📜 SQL script validation... ") + fmt.Print("[CHK] SQL script validation... ") if err := verifySqlScript(archivePath); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } checksRun++ case "SQL Script (.sql.gz)": - fmt.Print("📜 SQL script validation (gzip)... ") + fmt.Print("[CHK] SQL script validation (gzip)... ") if err := verifyGzipSqlScript(archivePath); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } checksRun++ case "Cluster Backup (.tar.gz)": - fmt.Print("📦 Archive extraction test... ") + fmt.Print("[CHK] Archive extraction test... ") if err := verifyTarGz(archivePath); err != nil { - fmt.Printf("❌ FAILED: %v\n", err) + fmt.Printf("[FAIL] FAILED: %v\n", err) } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } checksRun++ @@ -598,11 +598,11 @@ func runVerify(ctx context.Context, archiveName string) error { // Check for metadata file metadataPath := archivePath + ".info" - fmt.Print("📋 Metadata file check... ") + fmt.Print("[CHK] Metadata file check... ") if _, err := os.Stat(metadataPath); os.IsNotExist(err) { - fmt.Println("⚠️ WARNING: No metadata file found") + fmt.Println("[WARN] WARNING: No metadata file found") } else { - fmt.Println("✅ PASSED") + fmt.Println("[OK] PASSED") checksPassed++ } checksRun++ @@ -611,13 +611,13 @@ func runVerify(ctx context.Context, archiveName string) error { fmt.Printf("Verification Results: %d/%d checks passed\n", checksPassed, checksRun) if checksPassed == checksRun { - fmt.Println("🎉 Archive verification completed successfully!") + fmt.Println("[SUCCESS] Archive verification completed successfully!") return nil } else if float64(checksPassed)/float64(checksRun) >= 0.8 { - fmt.Println("⚠️ Archive verification completed with warnings.") + fmt.Println("[WARN] Archive verification completed with warnings.") return nil } else { - fmt.Println("❌ Archive verification failed. Archive may be corrupted.") + fmt.Println("[FAIL] Archive verification failed. Archive may be corrupted.") return fmt.Errorf("verification failed: %d/%d checks passed", checksPassed, checksRun) } } diff --git a/cmd/restore.go b/cmd/restore.go index 3c3a69c..a4ff28b 100755 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -342,7 +342,7 @@ func runRestoreDiagnose(cmd *cobra.Command, args []string) error { return fmt.Errorf("archive not found: %s", archivePath) } - log.Info("🔍 Diagnosing backup file", "path", archivePath) + log.Info("[DIAG] Diagnosing backup file", "path", archivePath) diagnoser := restore.NewDiagnoser(log, restoreVerbose) @@ -387,7 +387,7 @@ func runRestoreDiagnose(cmd *cobra.Command, args []string) error { // Summary if !diagnoseJSON { fmt.Println("\n" + strings.Repeat("=", 70)) - fmt.Printf("📊 CLUSTER SUMMARY: %d databases analyzed\n", len(results)) + fmt.Printf("[SUMMARY] CLUSTER SUMMARY: %d databases analyzed\n", len(results)) validCount := 0 for _, r := range results { @@ -397,9 +397,9 @@ func runRestoreDiagnose(cmd *cobra.Command, args []string) error { } if validCount == len(results) { - fmt.Println("✅ All dumps are valid") + fmt.Println("[OK] All dumps are valid") } else { - fmt.Printf("❌ %d/%d dumps have issues\n", len(results)-validCount, len(results)) + fmt.Printf("[FAIL] %d/%d dumps have issues\n", len(results)-validCount, len(results)) } fmt.Println(strings.Repeat("=", 70)) } @@ -426,7 +426,7 @@ func runRestoreDiagnose(cmd *cobra.Command, args []string) error { return fmt.Errorf("backup file has validation errors") } - log.Info("✅ Backup file appears valid") + log.Info("[OK] Backup file appears valid") return nil } @@ -545,7 +545,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error { isDryRun := restoreDryRun || !restoreConfirm if isDryRun { - fmt.Println("\n🔍 DRY-RUN MODE - No changes will be made") + fmt.Println("\n[DRY-RUN] DRY-RUN MODE - No changes will be made") fmt.Printf("\nWould restore:\n") fmt.Printf(" Archive: %s\n", archivePath) fmt.Printf(" Format: %s\n", format.String()) @@ -588,7 +588,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error { // Run pre-restore diagnosis if requested if restoreDiagnose { - log.Info("🔍 Running pre-restore diagnosis...") + log.Info("[DIAG] Running pre-restore diagnosis...") diagnoser := restore.NewDiagnoser(log, restoreVerbose) result, err := diagnoser.DiagnoseFile(archivePath) @@ -599,7 +599,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error { diagnoser.PrintDiagnosis(result) if !result.IsValid { - log.Error("❌ Pre-restore diagnosis found issues") + log.Error("[FAIL] Pre-restore diagnosis found issues") if result.IsTruncated { log.Error(" The backup file appears to be TRUNCATED") } @@ -613,7 +613,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error { } log.Warn("Continuing despite diagnosis errors (--force enabled)") } else { - log.Info("✅ Backup file passed diagnosis") + log.Info("[OK] Backup file passed diagnosis") } } @@ -633,7 +633,7 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error { // Audit log: restore success auditLogger.LogRestoreComplete(user, targetDB, time.Since(startTime)) - log.Info("✅ Restore completed successfully", "database", targetDB) + log.Info("[OK] Restore completed successfully", "database", targetDB) return nil } @@ -701,7 +701,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error { } } - log.Warn("⚠️ Using alternative working directory for extraction") + log.Warn("[WARN] Using alternative working directory for extraction") log.Warn(" This is recommended when system disk space is limited") log.Warn(" Location: " + restoreWorkdir) } @@ -754,7 +754,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error { isDryRun := restoreDryRun || !restoreConfirm if isDryRun { - fmt.Println("\n🔍 DRY-RUN MODE - No changes will be made") + fmt.Println("\n[DRY-RUN] DRY-RUN MODE - No changes will be made") fmt.Printf("\nWould restore cluster:\n") fmt.Printf(" Archive: %s\n", archivePath) fmt.Printf(" Parallel Jobs: %d (0 = auto)\n", restoreJobs) @@ -764,7 +764,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error { if restoreCleanCluster { fmt.Printf(" Clean Cluster: true (will drop %d existing database(s))\n", len(existingDBs)) if len(existingDBs) > 0 { - fmt.Printf("\n⚠️ Databases to be dropped:\n") + fmt.Printf("\n[WARN] Databases to be dropped:\n") for _, dbName := range existingDBs { fmt.Printf(" - %s\n", dbName) } @@ -776,7 +776,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error { // Warning for clean-cluster if restoreCleanCluster && len(existingDBs) > 0 { - log.Warn("🔥 Clean cluster mode enabled") + log.Warn("[!!] Clean cluster mode enabled") log.Warn(fmt.Sprintf(" %d existing database(s) will be DROPPED before restore!", len(existingDBs))) for _, dbName := range existingDBs { log.Warn(" - " + dbName) @@ -829,7 +829,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error { // Run pre-restore diagnosis if requested if restoreDiagnose { - log.Info("🔍 Running pre-restore diagnosis...") + log.Info("[DIAG] Running pre-restore diagnosis...") // Create temp directory for extraction in configured WorkDir workDir := cfg.GetEffectiveWorkDir() @@ -855,10 +855,10 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error { } if len(invalidDumps) > 0 { - log.Error("❌ Pre-restore diagnosis found issues", + log.Error("[FAIL] Pre-restore diagnosis found issues", "invalid_dumps", len(invalidDumps), "total_dumps", len(results)) - fmt.Println("\n⚠️ The following dumps have issues and will likely fail during restore:") + fmt.Println("\n[WARN] The following dumps have issues and will likely fail during restore:") for _, name := range invalidDumps { fmt.Printf(" - %s\n", name) } @@ -870,7 +870,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error { } log.Warn("Continuing despite diagnosis errors (--force enabled)") } else { - log.Info("✅ All dumps passed diagnosis", "count", len(results)) + log.Info("[OK] All dumps passed diagnosis", "count", len(results)) } } @@ -890,7 +890,7 @@ func runRestoreCluster(cmd *cobra.Command, args []string) error { // Audit log: restore success auditLogger.LogRestoreComplete(user, "all_databases", time.Since(startTime)) - log.Info("✅ Cluster restore completed successfully") + log.Info("[OK] Cluster restore completed successfully") return nil } @@ -939,7 +939,7 @@ func runRestoreList(cmd *cobra.Command, args []string) error { } // Print header - fmt.Printf("\n📦 Available backup archives in %s\n\n", backupDir) + fmt.Printf("\n[LIST] Available backup archives in %s\n\n", backupDir) fmt.Printf("%-40s %-25s %-12s %-20s %s\n", "FILENAME", "FORMAT", "SIZE", "MODIFIED", "DATABASE") fmt.Println(strings.Repeat("-", 120)) @@ -1056,9 +1056,9 @@ func runRestorePITR(cmd *cobra.Command, args []string) error { } // Display recovery target info - log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + log.Info("=====================================================") log.Info(" Point-in-Time Recovery (PITR)") - log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + log.Info("=====================================================") log.Info("") log.Info(target.String()) log.Info("") @@ -1082,6 +1082,6 @@ func runRestorePITR(cmd *cobra.Command, args []string) error { return fmt.Errorf("PITR restore failed: %w", err) } - log.Info("✅ PITR restore completed successfully") + log.Info("[OK] PITR restore completed successfully") return nil } diff --git a/cmd/rto.go b/cmd/rto.go index 8f90d45..0c05ee2 100644 --- a/cmd/rto.go +++ b/cmd/rto.go @@ -181,13 +181,13 @@ func runRTOStatus(cmd *cobra.Command, args []string) error { // Display status fmt.Println() - fmt.Println("╔═══════════════════════════════════════════════════════════╗") - fmt.Println("║ RTO/RPO STATUS SUMMARY ║") - fmt.Println("╠═══════════════════════════════════════════════════════════╣") - fmt.Printf("║ Target RTO: %-15s Target RPO: %-15s ║\n", + fmt.Println("+-----------------------------------------------------------+") + fmt.Println("| RTO/RPO STATUS SUMMARY |") + fmt.Println("+-----------------------------------------------------------+") + fmt.Printf("| Target RTO: %-15s Target RPO: %-15s |\n", formatDuration(config.TargetRTO), formatDuration(config.TargetRPO)) - fmt.Println("╠═══════════════════════════════════════════════════════════╣") + fmt.Println("+-----------------------------------------------------------+") // Compliance status rpoRate := 0.0 @@ -199,31 +199,31 @@ func runRTOStatus(cmd *cobra.Command, args []string) error { fullRate = float64(summary.FullyCompliant) / float64(summary.TotalDatabases) * 100 } - fmt.Printf("║ Databases: %-5d ║\n", summary.TotalDatabases) - fmt.Printf("║ RPO Compliant: %-5d (%.0f%%) ║\n", summary.RPOCompliant, rpoRate) - fmt.Printf("║ RTO Compliant: %-5d (%.0f%%) ║\n", summary.RTOCompliant, rtoRate) - fmt.Printf("║ Fully Compliant: %-3d (%.0f%%) ║\n", summary.FullyCompliant, fullRate) + fmt.Printf("| Databases: %-5d |\n", summary.TotalDatabases) + fmt.Printf("| RPO Compliant: %-5d (%.0f%%) |\n", summary.RPOCompliant, rpoRate) + fmt.Printf("| RTO Compliant: %-5d (%.0f%%) |\n", summary.RTOCompliant, rtoRate) + fmt.Printf("| Fully Compliant: %-3d (%.0f%%) |\n", summary.FullyCompliant, fullRate) if summary.CriticalIssues > 0 { - fmt.Printf("║ ⚠️ Critical Issues: %-3d ║\n", summary.CriticalIssues) + fmt.Printf("| [WARN] Critical Issues: %-3d |\n", summary.CriticalIssues) } - fmt.Println("╠═══════════════════════════════════════════════════════════╣") - fmt.Printf("║ Average RPO: %-15s Worst: %-15s ║\n", + fmt.Println("+-----------------------------------------------------------+") + fmt.Printf("| Average RPO: %-15s Worst: %-15s |\n", formatDuration(summary.AverageRPO), formatDuration(summary.WorstRPO)) - fmt.Printf("║ Average RTO: %-15s Worst: %-15s ║\n", + fmt.Printf("| Average RTO: %-15s Worst: %-15s |\n", formatDuration(summary.AverageRTO), formatDuration(summary.WorstRTO)) if summary.WorstRPODatabase != "" { - fmt.Printf("║ Worst RPO Database: %-38s║\n", summary.WorstRPODatabase) + fmt.Printf("| Worst RPO Database: %-38s|\n", summary.WorstRPODatabase) } if summary.WorstRTODatabase != "" { - fmt.Printf("║ Worst RTO Database: %-38s║\n", summary.WorstRTODatabase) + fmt.Printf("| Worst RTO Database: %-38s|\n", summary.WorstRTODatabase) } - fmt.Println("╚═══════════════════════════════════════════════════════════╝") + fmt.Println("+-----------------------------------------------------------+") fmt.Println() // Per-database status @@ -234,19 +234,19 @@ func runRTOStatus(cmd *cobra.Command, args []string) error { fmt.Println(strings.Repeat("-", 70)) for _, a := range analyses { - status := "✅" + status := "[OK]" if !a.RPOCompliant || !a.RTOCompliant { - status = "❌" + status = "[FAIL]" } rpoStr := formatDuration(a.CurrentRPO) rtoStr := formatDuration(a.CurrentRTO) if !a.RPOCompliant { - rpoStr = "⚠️ " + rpoStr + rpoStr = "[WARN] " + rpoStr } if !a.RTOCompliant { - rtoStr = "⚠️ " + rtoStr + rtoStr = "[WARN] " + rtoStr } fmt.Printf("%-25s %-12s %-12s %s\n", @@ -306,21 +306,21 @@ func runRTOCheck(cmd *cobra.Command, args []string) error { exitCode := 0 for _, a := range analyses { if !a.RPOCompliant { - fmt.Printf("❌ %s: RPO violation - current %s exceeds target %s\n", + fmt.Printf("[FAIL] %s: RPO violation - current %s exceeds target %s\n", a.Database, formatDuration(a.CurrentRPO), formatDuration(config.TargetRPO)) exitCode = 1 } if !a.RTOCompliant { - fmt.Printf("❌ %s: RTO violation - estimated %s exceeds target %s\n", + fmt.Printf("[FAIL] %s: RTO violation - estimated %s exceeds target %s\n", a.Database, formatDuration(a.CurrentRTO), formatDuration(config.TargetRTO)) exitCode = 1 } if a.RPOCompliant && a.RTOCompliant { - fmt.Printf("✅ %s: Compliant (RPO: %s, RTO: %s)\n", + fmt.Printf("[OK] %s: Compliant (RPO: %s, RTO: %s)\n", a.Database, formatDuration(a.CurrentRPO), formatDuration(a.CurrentRTO)) @@ -371,13 +371,13 @@ func outputAnalysisText(analyses []*rto.Analysis) error { fmt.Println(strings.Repeat("=", 60)) // Status - rpoStatus := "✅ Compliant" + rpoStatus := "[OK] Compliant" if !a.RPOCompliant { - rpoStatus = "❌ Violation" + rpoStatus = "[FAIL] Violation" } - rtoStatus := "✅ Compliant" + rtoStatus := "[OK] Compliant" if !a.RTOCompliant { - rtoStatus = "❌ Violation" + rtoStatus = "[FAIL] Violation" } fmt.Println() @@ -420,7 +420,7 @@ func outputAnalysisText(analyses []*rto.Analysis) error { fmt.Println(" Recommendations:") fmt.Println(strings.Repeat("-", 50)) for _, r := range a.Recommendations { - icon := "💡" + icon := "[TIP]" switch r.Priority { case rto.PriorityCritical: icon = "🔴" diff --git a/cmd/status.go b/cmd/status.go index 0224d83..43a2851 100755 --- a/cmd/status.go +++ b/cmd/status.go @@ -141,7 +141,7 @@ func testConnection(ctx context.Context) error { // Display results fmt.Println("Connection Test Results:") - fmt.Printf(" Status: Connected ✅\n") + fmt.Printf(" Status: Connected [OK]\n") fmt.Printf(" Version: %s\n", version) fmt.Printf(" Databases: %d found\n", len(databases)) @@ -167,7 +167,7 @@ func testConnection(ctx context.Context) error { } fmt.Println() - fmt.Println("✅ Status check completed successfully!") + fmt.Println("[OK] Status check completed successfully!") return nil } diff --git a/cmd/verify.go b/cmd/verify.go index 95f9dce..83aa0c0 100644 --- a/cmd/verify.go +++ b/cmd/verify.go @@ -96,17 +96,17 @@ func runVerifyBackup(cmd *cobra.Command, args []string) error { continue } - fmt.Printf("📁 %s\n", filepath.Base(backupFile)) + fmt.Printf("[FILE] %s\n", filepath.Base(backupFile)) if quickVerify { // Quick check: size only err := verification.QuickCheck(backupFile) if err != nil { - fmt.Printf(" ❌ FAILED: %v\n\n", err) + fmt.Printf(" [FAIL] FAILED: %v\n\n", err) failureCount++ continue } - fmt.Printf(" ✅ VALID (quick check)\n\n") + fmt.Printf(" [OK] VALID (quick check)\n\n") successCount++ } else { // Full verification with SHA-256 @@ -116,7 +116,7 @@ func runVerifyBackup(cmd *cobra.Command, args []string) error { } if result.Valid { - fmt.Printf(" ✅ VALID\n") + fmt.Printf(" [OK] VALID\n") if verboseVerify { meta, _ := metadata.Load(backupFile) fmt.Printf(" Size: %s\n", metadata.FormatSize(meta.SizeBytes)) @@ -127,7 +127,7 @@ func runVerifyBackup(cmd *cobra.Command, args []string) error { fmt.Println() successCount++ } else { - fmt.Printf(" ❌ FAILED: %v\n", result.Error) + fmt.Printf(" [FAIL] FAILED: %v\n", result.Error) if verboseVerify { if !result.FileExists { fmt.Printf(" File does not exist\n") @@ -147,11 +147,11 @@ func runVerifyBackup(cmd *cobra.Command, args []string) error { } // Summary - fmt.Println(strings.Repeat("─", 50)) + fmt.Println(strings.Repeat("-", 50)) fmt.Printf("Total: %d backups\n", len(backupFiles)) - fmt.Printf("✅ Valid: %d\n", successCount) + fmt.Printf("[OK] Valid: %d\n", successCount) if failureCount > 0 { - fmt.Printf("❌ Failed: %d\n", failureCount) + fmt.Printf("[FAIL] Failed: %d\n", failureCount) os.Exit(1) } @@ -195,16 +195,16 @@ func runVerifyCloudBackup(cmd *cobra.Command, args []string) error { for _, uri := range args { if !isCloudURI(uri) { - fmt.Printf("⚠️ Skipping non-cloud URI: %s\n", uri) + fmt.Printf("[WARN] Skipping non-cloud URI: %s\n", uri) continue } - fmt.Printf("☁️ %s\n", uri) + fmt.Printf("[CLOUD] %s\n", uri) // Download and verify result, err := verifyCloudBackup(cmd.Context(), uri, quickVerify, verboseVerify) if err != nil { - fmt.Printf(" ❌ FAILED: %v\n\n", err) + fmt.Printf(" [FAIL] FAILED: %v\n\n", err) failureCount++ continue } @@ -212,7 +212,7 @@ func runVerifyCloudBackup(cmd *cobra.Command, args []string) error { // Cleanup temp file defer result.Cleanup() - fmt.Printf(" ✅ VALID\n") + fmt.Printf(" [OK] VALID\n") if verboseVerify && result.MetadataPath != "" { meta, _ := metadata.Load(result.MetadataPath) if meta != nil { @@ -226,7 +226,7 @@ func runVerifyCloudBackup(cmd *cobra.Command, args []string) error { successCount++ } - fmt.Printf("\n✅ Summary: %d valid, %d failed\n", successCount, failureCount) + fmt.Printf("\n[OK] Summary: %d valid, %d failed\n", successCount, failureCount) if failureCount > 0 { os.Exit(1) diff --git a/internal/auth/helper.go b/internal/auth/helper.go index 89912c2..52c664e 100755 --- a/internal/auth/helper.go +++ b/internal/auth/helper.go @@ -204,13 +204,13 @@ func CheckAuthenticationMismatch(cfg *config.Config) (bool, string) { func buildAuthMismatchMessage(osUser, dbUser string, method AuthMethod) string { var msg strings.Builder - msg.WriteString("\n⚠️ Authentication Mismatch Detected\n") + msg.WriteString("\n[WARN] Authentication Mismatch Detected\n") msg.WriteString(strings.Repeat("=", 60) + "\n\n") msg.WriteString(fmt.Sprintf(" PostgreSQL is using '%s' authentication\n", method)) msg.WriteString(fmt.Sprintf(" OS user '%s' cannot authenticate as DB user '%s'\n\n", osUser, dbUser)) - msg.WriteString("💡 Solutions (choose one):\n\n") + msg.WriteString("[TIP] Solutions (choose one):\n\n") msg.WriteString(fmt.Sprintf(" 1. Run as matching user:\n")) msg.WriteString(fmt.Sprintf(" sudo -u %s %s\n\n", dbUser, getCommandLine())) @@ -226,7 +226,7 @@ func buildAuthMismatchMessage(osUser, dbUser string, method AuthMethod) string { msg.WriteString(" 4. Provide password via flag:\n") msg.WriteString(fmt.Sprintf(" %s --password your_password\n\n", getCommandLine())) - msg.WriteString("📝 Note: For production use, ~/.pgpass or PGPASSWORD are recommended\n") + msg.WriteString("[NOTE] Note: For production use, ~/.pgpass or PGPASSWORD are recommended\n") msg.WriteString(" to avoid exposing passwords in command history.\n\n") msg.WriteString(strings.Repeat("=", 60) + "\n") diff --git a/internal/backup/engine.go b/internal/backup/engine.go index f08626a..e51e46e 100755 --- a/internal/backup/engine.go +++ b/internal/backup/engine.go @@ -473,7 +473,7 @@ func (e *Engine) BackupCluster(ctx context.Context) error { mu.Lock() e.printf(" Database size: %s\n", sizeStr) if size > 10*1024*1024*1024 { // > 10GB - e.printf(" ⚠️ Large database detected - this may take a while\n") + e.printf(" [WARN] Large database detected - this may take a while\n") } mu.Unlock() } @@ -518,16 +518,16 @@ func (e *Engine) BackupCluster(ctx context.Context) error { if err != nil { e.log.Warn("Failed to backup database", "database", name, "error", err) mu.Lock() - e.printf(" ⚠️ WARNING: Failed to backup %s: %v\n", name, err) + e.printf(" [WARN] WARNING: Failed to backup %s: %v\n", name, err) mu.Unlock() atomic.AddInt32(&failCount, 1) } else { compressedCandidate := strings.TrimSuffix(dumpFile, ".dump") + ".sql.gz" mu.Lock() if info, err := os.Stat(compressedCandidate); err == nil { - e.printf(" ✅ Completed %s (%s)\n", name, formatBytes(info.Size())) + e.printf(" [OK] Completed %s (%s)\n", name, formatBytes(info.Size())) } else if info, err := os.Stat(dumpFile); err == nil { - e.printf(" ✅ Completed %s (%s)\n", name, formatBytes(info.Size())) + e.printf(" [OK] Completed %s (%s)\n", name, formatBytes(info.Size())) } mu.Unlock() atomic.AddInt32(&successCount, 1) diff --git a/internal/backup/incremental_test.go b/internal/backup/incremental_test.go index df3445f..871fa05 100644 --- a/internal/backup/incremental_test.go +++ b/internal/backup/incremental_test.go @@ -242,7 +242,7 @@ func TestIncrementalBackupRestore(t *testing.T) { t.Errorf("Unchanged file base/12345/1235 not found in restore: %v", err) } - t.Log("✅ Incremental backup and restore test completed successfully") + t.Log("[OK] Incremental backup and restore test completed successfully") } // TestIncrementalBackupErrors tests error handling diff --git a/internal/checks/disk_check.go b/internal/checks/disk_check.go index 721914d..254b93f 100755 --- a/internal/checks/disk_check.go +++ b/internal/checks/disk_check.go @@ -75,16 +75,16 @@ func FormatDiskSpaceMessage(check *DiskSpaceCheck) string { if check.Critical { status = "CRITICAL" - icon = "❌" + icon = "[X]" } else if check.Warning { status = "WARNING" - icon = "⚠️ " + icon = "[!]" } else { status = "OK" - icon = "✓" + icon = "[+]" } - msg := fmt.Sprintf(`📊 Disk Space Check (%s): + msg := fmt.Sprintf(`[DISK] Disk Space Check (%s): Path: %s Total: %s Available: %s (%.1f%% used) @@ -98,13 +98,13 @@ func FormatDiskSpaceMessage(check *DiskSpaceCheck) string { status) if check.Critical { - msg += "\n \n ⚠️ CRITICAL: Insufficient disk space!" + msg += "\n \n [!!] CRITICAL: Insufficient disk space!" msg += "\n Operation blocked. Free up space before continuing." } else if check.Warning { - msg += "\n \n ⚠️ WARNING: Low disk space!" + msg += "\n \n [!] WARNING: Low disk space!" msg += "\n Backup may fail if database is larger than estimated." } else { - msg += "\n \n ✓ Sufficient space available" + msg += "\n \n [+] Sufficient space available" } return msg diff --git a/internal/checks/disk_check_bsd.go b/internal/checks/disk_check_bsd.go index 28fa962..2716717 100755 --- a/internal/checks/disk_check_bsd.go +++ b/internal/checks/disk_check_bsd.go @@ -75,16 +75,16 @@ func FormatDiskSpaceMessage(check *DiskSpaceCheck) string { if check.Critical { status = "CRITICAL" - icon = "❌" + icon = "[X]" } else if check.Warning { status = "WARNING" - icon = "⚠️ " + icon = "[!]" } else { status = "OK" - icon = "✓" + icon = "[+]" } - msg := fmt.Sprintf(`📊 Disk Space Check (%s): + msg := fmt.Sprintf(`[DISK] Disk Space Check (%s): Path: %s Total: %s Available: %s (%.1f%% used) @@ -98,13 +98,13 @@ func FormatDiskSpaceMessage(check *DiskSpaceCheck) string { status) if check.Critical { - msg += "\n \n ⚠️ CRITICAL: Insufficient disk space!" + msg += "\n \n [!!] CRITICAL: Insufficient disk space!" msg += "\n Operation blocked. Free up space before continuing." } else if check.Warning { - msg += "\n \n ⚠️ WARNING: Low disk space!" + msg += "\n \n [!] WARNING: Low disk space!" msg += "\n Backup may fail if database is larger than estimated." } else { - msg += "\n \n ✓ Sufficient space available" + msg += "\n \n [+] Sufficient space available" } return msg diff --git a/internal/checks/disk_check_netbsd.go b/internal/checks/disk_check_netbsd.go index 7744621..45cec5c 100644 --- a/internal/checks/disk_check_netbsd.go +++ b/internal/checks/disk_check_netbsd.go @@ -58,16 +58,16 @@ func FormatDiskSpaceMessage(check *DiskSpaceCheck) string { if check.Critical { status = "CRITICAL" - icon = "❌" + icon = "[X]" } else if check.Warning { status = "WARNING" - icon = "⚠️ " + icon = "[!]" } else { status = "OK" - icon = "✓" + icon = "[+]" } - msg := fmt.Sprintf(`📊 Disk Space Check (%s): + msg := fmt.Sprintf(`[DISK] Disk Space Check (%s): Path: %s Total: %s Available: %s (%.1f%% used) @@ -81,13 +81,13 @@ func FormatDiskSpaceMessage(check *DiskSpaceCheck) string { status) if check.Critical { - msg += "\n \n ⚠️ CRITICAL: Insufficient disk space!" + msg += "\n \n [!!] CRITICAL: Insufficient disk space!" msg += "\n Operation blocked. Free up space before continuing." } else if check.Warning { - msg += "\n \n ⚠️ WARNING: Low disk space!" + msg += "\n \n [!] WARNING: Low disk space!" msg += "\n Backup may fail if database is larger than estimated." } else { - msg += "\n \n ✓ Sufficient space available" + msg += "\n \n [+] Sufficient space available" } return msg diff --git a/internal/checks/disk_check_windows.go b/internal/checks/disk_check_windows.go index 1a6a693..e709c5a 100755 --- a/internal/checks/disk_check_windows.go +++ b/internal/checks/disk_check_windows.go @@ -94,16 +94,16 @@ func FormatDiskSpaceMessage(check *DiskSpaceCheck) string { if check.Critical { status = "CRITICAL" - icon = "❌" + icon = "[X]" } else if check.Warning { status = "WARNING" - icon = "⚠️ " + icon = "[!]" } else { status = "OK" - icon = "✓" + icon = "[+]" } - msg := fmt.Sprintf(`📊 Disk Space Check (%s): + msg := fmt.Sprintf(`[DISK] Disk Space Check (%s): Path: %s Total: %s Available: %s (%.1f%% used) @@ -117,13 +117,13 @@ func FormatDiskSpaceMessage(check *DiskSpaceCheck) string { status) if check.Critical { - msg += "\n \n ⚠️ CRITICAL: Insufficient disk space!" + msg += "\n \n [!!] CRITICAL: Insufficient disk space!" msg += "\n Operation blocked. Free up space before continuing." } else if check.Warning { - msg += "\n \n ⚠️ WARNING: Low disk space!" + msg += "\n \n [!] WARNING: Low disk space!" msg += "\n Backup may fail if database is larger than estimated." } else { - msg += "\n \n ✓ Sufficient space available" + msg += "\n \n [+] Sufficient space available" } return msg diff --git a/internal/checks/error_hints.go b/internal/checks/error_hints.go index 4bb9bf4..a5f9561 100755 --- a/internal/checks/error_hints.go +++ b/internal/checks/error_hints.go @@ -234,22 +234,22 @@ func FormatErrorWithHint(errorMsg string) string { var icon string switch classification.Type { case "ignorable": - icon = "ℹ️ " + icon = "[i]" case "warning": - icon = "⚠️ " + icon = "[!]" case "critical": - icon = "❌" + icon = "[X]" case "fatal": - icon = "🛑" + icon = "[!!]" default: - icon = "⚠️ " + icon = "[!]" } output := fmt.Sprintf("%s %s Error\n\n", icon, strings.ToUpper(classification.Type)) output += fmt.Sprintf("Category: %s\n", classification.Category) output += fmt.Sprintf("Message: %s\n\n", classification.Message) - output += fmt.Sprintf("💡 Hint: %s\n\n", classification.Hint) - output += fmt.Sprintf("🔧 Action: %s\n", classification.Action) + output += fmt.Sprintf("[HINT] Hint: %s\n\n", classification.Hint) + output += fmt.Sprintf("[ACTION] Action: %s\n", classification.Action) return output } @@ -257,7 +257,7 @@ func FormatErrorWithHint(errorMsg string) string { // FormatMultipleErrors formats multiple errors with classification func FormatMultipleErrors(errors []string) string { if len(errors) == 0 { - return "✓ No errors" + return "[+] No errors" } ignorable := 0 @@ -285,22 +285,22 @@ func FormatMultipleErrors(errors []string) string { } } - output := "📊 Error Summary:\n\n" + output := "[SUMMARY] Error Summary:\n\n" if ignorable > 0 { - output += fmt.Sprintf(" ℹ️ %d ignorable (objects already exist)\n", ignorable) + output += fmt.Sprintf(" [i] %d ignorable (objects already exist)\n", ignorable) } if warnings > 0 { - output += fmt.Sprintf(" ⚠️ %d warnings\n", warnings) + output += fmt.Sprintf(" [!] %d warnings\n", warnings) } if critical > 0 { - output += fmt.Sprintf(" ❌ %d critical errors\n", critical) + output += fmt.Sprintf(" [X] %d critical errors\n", critical) } if fatal > 0 { - output += fmt.Sprintf(" 🛑 %d fatal errors\n", fatal) + output += fmt.Sprintf(" [!!] %d fatal errors\n", fatal) } if len(criticalErrors) > 0 { - output += "\n📝 Critical Issues:\n\n" + output += "\n[CRITICAL] Critical Issues:\n\n" for i, err := range criticalErrors { class := ClassifyError(err) output += fmt.Sprintf("%d. %s\n", i+1, class.Hint) diff --git a/internal/checks/preflight.go b/internal/checks/preflight.go index 3bc3caa..80abc30 100644 --- a/internal/checks/preflight.go +++ b/internal/checks/preflight.go @@ -49,15 +49,15 @@ func (s CheckStatus) String() string { func (s CheckStatus) Icon() string { switch s { case StatusPassed: - return "✓" + return "[+]" case StatusWarning: - return "⚠" + return "[!]" case StatusFailed: - return "✗" + return "[-]" case StatusSkipped: - return "○" + return "[ ]" default: - return "?" + return "[?]" } } diff --git a/internal/checks/report.go b/internal/checks/report.go index 695ad66..6cb13b4 100644 --- a/internal/checks/report.go +++ b/internal/checks/report.go @@ -11,9 +11,9 @@ func FormatPreflightReport(result *PreflightResult, dbName string, verbose bool) var sb strings.Builder sb.WriteString("\n") - sb.WriteString("╔══════════════════════════════════════════════════════════════╗\n") - sb.WriteString("║ [DRY RUN] Preflight Check Results ║\n") - sb.WriteString("╚══════════════════════════════════════════════════════════════╝\n") + sb.WriteString("+==============================================================+\n") + sb.WriteString("| [DRY RUN] Preflight Check Results |\n") + sb.WriteString("+==============================================================+\n") sb.WriteString("\n") // Database info @@ -29,7 +29,7 @@ func FormatPreflightReport(result *PreflightResult, dbName string, verbose bool) // Check results sb.WriteString(" Checks:\n") - sb.WriteString(" ─────────────────────────────────────────────────────────────\n") + sb.WriteString(" --------------------------------------------------------------\n") for _, check := range result.Checks { icon := check.Status.Icon() @@ -40,26 +40,26 @@ func FormatPreflightReport(result *PreflightResult, dbName string, verbose bool) color, icon, reset, check.Name+":", check.Message)) if verbose && check.Details != "" { - sb.WriteString(fmt.Sprintf(" └─ %s\n", check.Details)) + sb.WriteString(fmt.Sprintf(" +- %s\n", check.Details)) } } - sb.WriteString(" ─────────────────────────────────────────────────────────────\n") + sb.WriteString(" --------------------------------------------------------------\n") sb.WriteString("\n") // Summary if result.AllPassed { if result.HasWarnings { - sb.WriteString(" ⚠️ All checks passed with warnings\n") + sb.WriteString(" [!] All checks passed with warnings\n") sb.WriteString("\n") sb.WriteString(" Ready to backup. Remove --dry-run to execute.\n") } else { - sb.WriteString(" ✅ All checks passed\n") + sb.WriteString(" [OK] All checks passed\n") sb.WriteString("\n") sb.WriteString(" Ready to backup. Remove --dry-run to execute.\n") } } else { - sb.WriteString(fmt.Sprintf(" ❌ %d check(s) failed\n", result.FailureCount)) + sb.WriteString(fmt.Sprintf(" [FAIL] %d check(s) failed\n", result.FailureCount)) sb.WriteString("\n") sb.WriteString(" Fix the issues above before running backup.\n") } @@ -96,7 +96,7 @@ func FormatPreflightReportPlain(result *PreflightResult, dbName string) string { status := fmt.Sprintf("[%s]", check.Status.String()) sb.WriteString(fmt.Sprintf(" %-10s %-25s %s\n", status, check.Name+":", check.Message)) if check.Details != "" { - sb.WriteString(fmt.Sprintf(" └─ %s\n", check.Details)) + sb.WriteString(fmt.Sprintf(" +- %s\n", check.Details)) } } diff --git a/internal/drill/drill.go b/internal/drill/drill.go index e3bf2a8..08ea5ce 100644 --- a/internal/drill/drill.go +++ b/internal/drill/drill.go @@ -223,11 +223,11 @@ func (r *DrillResult) IsSuccess() bool { // Summary returns a human-readable summary of the drill func (r *DrillResult) Summary() string { - status := "✅ PASSED" + status := "[OK] PASSED" if !r.Success { - status = "❌ FAILED" + status = "[FAIL] FAILED" } else if r.Status == StatusPartial { - status = "⚠️ PARTIAL" + status = "[WARN] PARTIAL" } return fmt.Sprintf("%s - %s (%.2fs) - %d tables, %d rows", diff --git a/internal/drill/engine.go b/internal/drill/engine.go index 5ad6c80..bb7ea46 100644 --- a/internal/drill/engine.go +++ b/internal/drill/engine.go @@ -41,20 +41,20 @@ func (e *Engine) Run(ctx context.Context, config *DrillConfig) (*DrillResult, er TargetRTO: float64(config.MaxRestoreSeconds), } - e.log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - e.log.Info(" 🧪 DR Drill: " + result.DrillID) - e.log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + e.log.Info("=====================================================") + e.log.Info(" [TEST] DR Drill: " + result.DrillID) + e.log.Info("=====================================================") e.log.Info("") // Cleanup function for error cases var containerID string cleanup := func() { if containerID != "" && config.CleanupOnExit && (result.Success || !config.KeepOnFailure) { - e.log.Info("🗑️ Cleaning up container...") + e.log.Info("[DEL] Cleaning up container...") e.docker.RemoveContainer(context.Background(), containerID) } else if containerID != "" { result.ContainerKept = true - e.log.Info("📦 Container kept for debugging: " + containerID) + e.log.Info("[PKG] Container kept for debugging: " + containerID) } } defer cleanup() @@ -88,7 +88,7 @@ func (e *Engine) Run(ctx context.Context, config *DrillConfig) (*DrillResult, er } containerID = container.ID result.ContainerID = containerID - e.log.Info("📦 Container started: " + containerID[:12]) + e.log.Info("[PKG] Container started: " + containerID[:12]) // Wait for container to be healthy if err := e.docker.WaitForHealth(ctx, containerID, config.DatabaseType, config.ContainerTimeout); err != nil { @@ -118,7 +118,7 @@ func (e *Engine) Run(ctx context.Context, config *DrillConfig) (*DrillResult, er result.RestoreTime = time.Since(restoreStart).Seconds() e.completePhase(&phase, fmt.Sprintf("Restored in %.2fs", result.RestoreTime)) result.Phases = append(result.Phases, phase) - e.log.Info(fmt.Sprintf("✅ Backup restored in %.2fs", result.RestoreTime)) + e.log.Info(fmt.Sprintf("[OK] Backup restored in %.2fs", result.RestoreTime)) // Phase 4: Validate phase = e.startPhase("Validate Database") @@ -182,24 +182,24 @@ func (e *Engine) preflightChecks(ctx context.Context, config *DrillConfig) error if err := e.docker.CheckDockerAvailable(ctx); err != nil { return fmt.Errorf("docker not available: %w", err) } - e.log.Info("✓ Docker is available") + e.log.Info("[OK] Docker is available") // Check backup file exists if _, err := os.Stat(config.BackupPath); err != nil { return fmt.Errorf("backup file not found: %s", config.BackupPath) } - e.log.Info("✓ Backup file exists: " + filepath.Base(config.BackupPath)) + e.log.Info("[OK] Backup file exists: " + filepath.Base(config.BackupPath)) // Pull Docker image image := config.ContainerImage if image == "" { image = GetDefaultImage(config.DatabaseType, "") } - e.log.Info("⬇️ Pulling image: " + image) + e.log.Info("[DOWN] Pulling image: " + image) if err := e.docker.PullImage(ctx, image); err != nil { return fmt.Errorf("failed to pull image: %w", err) } - e.log.Info("✓ Image ready: " + image) + e.log.Info("[OK] Image ready: " + image) return nil } @@ -243,7 +243,7 @@ func (e *Engine) restoreBackup(ctx context.Context, config *DrillConfig, contain backupName := filepath.Base(config.BackupPath) containerBackupPath := "/tmp/" + backupName - e.log.Info("📁 Copying backup to container...") + e.log.Info("[DIR] Copying backup to container...") if err := e.docker.CopyToContainer(ctx, containerID, config.BackupPath, containerBackupPath); err != nil { return fmt.Errorf("failed to copy backup: %w", err) } @@ -256,7 +256,7 @@ func (e *Engine) restoreBackup(ctx context.Context, config *DrillConfig, contain } // Restore based on database type and format - e.log.Info("🔄 Restoring backup...") + e.log.Info("[EXEC] Restoring backup...") return e.executeRestore(ctx, config, containerID, containerBackupPath, containerConfig) } @@ -366,13 +366,13 @@ func (e *Engine) validateDatabase(ctx context.Context, config *DrillConfig, resu tables, err := validator.GetTableList(ctx) if err == nil { result.TableCount = len(tables) - e.log.Info(fmt.Sprintf("📊 Tables found: %d", result.TableCount)) + e.log.Info(fmt.Sprintf("[STATS] Tables found: %d", result.TableCount)) } totalRows, err := validator.GetTotalRowCount(ctx) if err == nil { result.TotalRows = totalRows - e.log.Info(fmt.Sprintf("📊 Total rows: %d", result.TotalRows)) + e.log.Info(fmt.Sprintf("[STATS] Total rows: %d", result.TotalRows)) } dbSize, err := validator.GetDatabaseSize(ctx, config.DatabaseName) @@ -387,9 +387,9 @@ func (e *Engine) validateDatabase(ctx context.Context, config *DrillConfig, resu result.CheckResults = append(result.CheckResults, tr) if !tr.Success { errorCount++ - e.log.Warn("❌ " + tr.Message) + e.log.Warn("[FAIL] " + tr.Message) } else { - e.log.Info("✓ " + tr.Message) + e.log.Info("[OK] " + tr.Message) } } } @@ -404,9 +404,9 @@ func (e *Engine) validateDatabase(ctx context.Context, config *DrillConfig, resu totalQueryTime += qr.Duration if !qr.Success { errorCount++ - e.log.Warn(fmt.Sprintf("❌ %s: %s", qr.Name, qr.Error)) + e.log.Warn(fmt.Sprintf("[FAIL] %s: %s", qr.Name, qr.Error)) } else { - e.log.Info(fmt.Sprintf("✓ %s: %s (%.0fms)", qr.Name, qr.Result, qr.Duration)) + e.log.Info(fmt.Sprintf("[OK] %s: %s (%.0fms)", qr.Name, qr.Result, qr.Duration)) } } if len(queryResults) > 0 { @@ -421,9 +421,9 @@ func (e *Engine) validateDatabase(ctx context.Context, config *DrillConfig, resu result.CheckResults = append(result.CheckResults, cr) if !cr.Success { errorCount++ - e.log.Warn("❌ " + cr.Message) + e.log.Warn("[FAIL] " + cr.Message) } else { - e.log.Info("✓ " + cr.Message) + e.log.Info("[OK] " + cr.Message) } } } @@ -433,7 +433,7 @@ func (e *Engine) validateDatabase(ctx context.Context, config *DrillConfig, resu errorCount++ msg := fmt.Sprintf("Total rows (%d) below minimum (%d)", result.TotalRows, config.MinRowCount) result.Warnings = append(result.Warnings, msg) - e.log.Warn("⚠️ " + msg) + e.log.Warn("[WARN] " + msg) } return errorCount @@ -441,7 +441,7 @@ func (e *Engine) validateDatabase(ctx context.Context, config *DrillConfig, resu // startPhase starts a new drill phase func (e *Engine) startPhase(name string) DrillPhase { - e.log.Info("▶️ " + name) + e.log.Info("[RUN] " + name) return DrillPhase{ Name: name, Status: "running", @@ -463,7 +463,7 @@ func (e *Engine) failPhase(phase *DrillPhase, message string) { phase.Duration = phase.EndTime.Sub(phase.StartTime).Seconds() phase.Status = "failed" phase.Message = message - e.log.Error("❌ Phase failed: " + message) + e.log.Error("[FAIL] Phase failed: " + message) } // finalize completes the drill result @@ -472,9 +472,9 @@ func (e *Engine) finalize(result *DrillResult) { result.Duration = result.EndTime.Sub(result.StartTime).Seconds() e.log.Info("") - e.log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + e.log.Info("=====================================================") e.log.Info(" " + result.Summary()) - e.log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + e.log.Info("=====================================================") if result.Success { e.log.Info(fmt.Sprintf(" RTO: %.2fs (target: %.0fs) %s", @@ -484,9 +484,9 @@ func (e *Engine) finalize(result *DrillResult) { func boolIcon(b bool) string { if b { - return "✅" + return "[OK]" } - return "❌" + return "[FAIL]" } // Cleanup removes drill resources @@ -498,7 +498,7 @@ func (e *Engine) Cleanup(ctx context.Context, drillID string) error { for _, c := range containers { if strings.Contains(c.Name, drillID) || (drillID == "" && strings.HasPrefix(c.Name, "drill_")) { - e.log.Info("🗑️ Removing container: " + c.Name) + e.log.Info("[DEL] Removing container: " + c.Name) if err := e.docker.RemoveContainer(ctx, c.ID); err != nil { e.log.Warn("Failed to remove container", "id", c.ID, "error", err) } diff --git a/internal/encryption/encryption_test.go b/internal/encryption/encryption_test.go index b1432b8..406160f 100644 --- a/internal/encryption/encryption_test.go +++ b/internal/encryption/encryption_test.go @@ -8,7 +8,7 @@ import ( func TestEncryptDecrypt(t *testing.T) { // Test data - original := []byte("This is a secret database backup that needs encryption! 🔒") + original := []byte("This is a secret database backup that needs encryption! [LOCK]") // Test with passphrase t.Run("Passphrase", func(t *testing.T) { @@ -57,7 +57,7 @@ func TestEncryptDecrypt(t *testing.T) { string(original), string(decrypted)) } - t.Log("✅ Encryption/decryption successful") + t.Log("[OK] Encryption/decryption successful") }) // Test with direct key @@ -102,7 +102,7 @@ func TestEncryptDecrypt(t *testing.T) { t.Errorf("Decrypted data doesn't match original") } - t.Log("✅ Direct key encryption/decryption successful") + t.Log("[OK] Direct key encryption/decryption successful") }) // Test wrong password @@ -133,7 +133,7 @@ func TestEncryptDecrypt(t *testing.T) { t.Error("Expected decryption to fail with wrong password, but it succeeded") } - t.Logf("✅ Wrong password correctly rejected: %v", err) + t.Logf("[OK] Wrong password correctly rejected: %v", err) }) } @@ -183,7 +183,7 @@ func TestLargeData(t *testing.T) { t.Errorf("Large data decryption failed") } - t.Log("✅ Large data encryption/decryption successful") + t.Log("[OK] Large data encryption/decryption successful") } func TestKeyGeneration(t *testing.T) { @@ -207,7 +207,7 @@ func TestKeyGeneration(t *testing.T) { t.Error("Generated keys are identical - randomness broken!") } - t.Log("✅ Key generation successful") + t.Log("[OK] Key generation successful") } func TestKeyDerivation(t *testing.T) { @@ -230,5 +230,5 @@ func TestKeyDerivation(t *testing.T) { t.Error("Different salts produced same key") } - t.Log("✅ Key derivation successful") + t.Log("[OK] Key derivation successful") } diff --git a/internal/installer/installer.go b/internal/installer/installer.go index e05b95c..05d7db0 100644 --- a/internal/installer/installer.go +++ b/internal/installer/installer.go @@ -658,9 +658,9 @@ func (i *Installer) printNextSteps(opts InstallOptions) { serviceName := strings.Replace(timerName, ".timer", ".service", 1) fmt.Println() - fmt.Println("✅ Installation successful!") + fmt.Println("[OK] Installation successful!") fmt.Println() - fmt.Println("📋 Next steps:") + fmt.Println("[NEXT] Next steps:") fmt.Println() fmt.Printf(" 1. Edit configuration: sudo nano %s\n", opts.ConfigPath) fmt.Printf(" 2. Set credentials: sudo nano /etc/dbbackup/env.d/%s.conf\n", opts.Instance) @@ -668,12 +668,12 @@ func (i *Installer) printNextSteps(opts InstallOptions) { fmt.Printf(" 4. Verify timer status: sudo systemctl status %s\n", timerName) fmt.Printf(" 5. Run backup manually: sudo systemctl start %s\n", serviceName) fmt.Println() - fmt.Println("📊 View backup logs:") + fmt.Println("[LOGS] View backup logs:") fmt.Printf(" journalctl -u %s -f\n", serviceName) fmt.Println() if opts.WithMetrics { - fmt.Println("📈 Prometheus metrics:") + fmt.Println("[METRICS] Prometheus metrics:") fmt.Printf(" curl http://localhost:%d/metrics\n", opts.MetricsPort) fmt.Println() } diff --git a/internal/notify/batch.go b/internal/notify/batch.go index 85ec5bf..ded11d0 100644 --- a/internal/notify/batch.go +++ b/internal/notify/batch.go @@ -202,9 +202,9 @@ func (b *Batcher) formatSummaryDigest(events []*Event, success, failure, dbCount func (b *Batcher) formatCompactDigest(events []*Event, success, failure int) string { if failure > 0 { - return fmt.Sprintf("⚠️ %d/%d operations failed", failure, len(events)) + return fmt.Sprintf("[WARN] %d/%d operations failed", failure, len(events)) } - return fmt.Sprintf("✅ All %d operations successful", success) + return fmt.Sprintf("[OK] All %d operations successful", success) } func (b *Batcher) formatDetailedDigest(events []*Event) string { @@ -215,9 +215,9 @@ func (b *Batcher) formatDetailedDigest(events []*Event) string { icon := "•" switch e.Severity { case SeverityError, SeverityCritical: - icon = "❌" + icon = "[FAIL]" case SeverityWarning: - icon = "⚠️" + icon = "[WARN]" } msg += fmt.Sprintf("%s [%s] %s: %s\n", diff --git a/internal/notify/notify.go b/internal/notify/notify.go index c5ab34e..d3fe287 100644 --- a/internal/notify/notify.go +++ b/internal/notify/notify.go @@ -183,43 +183,43 @@ func DefaultConfig() Config { // FormatEventSubject generates a subject line for notifications func FormatEventSubject(event *Event) string { - icon := "ℹ️" + icon := "[INFO]" switch event.Severity { case SeverityWarning: - icon = "⚠️" + icon = "[WARN]" case SeverityError, SeverityCritical: - icon = "❌" + icon = "[FAIL]" } verb := "Event" switch event.Type { case EventBackupStarted: verb = "Backup Started" - icon = "🔄" + icon = "[EXEC]" case EventBackupCompleted: verb = "Backup Completed" - icon = "✅" + icon = "[OK]" case EventBackupFailed: verb = "Backup Failed" - icon = "❌" + icon = "[FAIL]" case EventRestoreStarted: verb = "Restore Started" - icon = "🔄" + icon = "[EXEC]" case EventRestoreCompleted: verb = "Restore Completed" - icon = "✅" + icon = "[OK]" case EventRestoreFailed: verb = "Restore Failed" - icon = "❌" + icon = "[FAIL]" case EventCleanupCompleted: verb = "Cleanup Completed" - icon = "🗑️" + icon = "[DEL]" case EventVerifyCompleted: verb = "Verification Passed" - icon = "✅" + icon = "[OK]" case EventVerifyFailed: verb = "Verification Failed" - icon = "❌" + icon = "[FAIL]" case EventPITRRecovery: verb = "PITR Recovery" icon = "⏪" diff --git a/internal/notify/templates.go b/internal/notify/templates.go index 929d744..f979667 100644 --- a/internal/notify/templates.go +++ b/internal/notify/templates.go @@ -30,52 +30,52 @@ type Templates struct { func DefaultTemplates() map[EventType]Templates { return map[EventType]Templates{ EventBackupStarted: { - Subject: "🔄 Backup Started: {{.Database}} on {{.Hostname}}", + Subject: "[EXEC] Backup Started: {{.Database}} on {{.Hostname}}", TextBody: backupStartedText, HTMLBody: backupStartedHTML, }, EventBackupCompleted: { - Subject: "✅ Backup Completed: {{.Database}} on {{.Hostname}}", + Subject: "[OK] Backup Completed: {{.Database}} on {{.Hostname}}", TextBody: backupCompletedText, HTMLBody: backupCompletedHTML, }, EventBackupFailed: { - Subject: "❌ Backup FAILED: {{.Database}} on {{.Hostname}}", + Subject: "[FAIL] Backup FAILED: {{.Database}} on {{.Hostname}}", TextBody: backupFailedText, HTMLBody: backupFailedHTML, }, EventRestoreStarted: { - Subject: "🔄 Restore Started: {{.Database}} on {{.Hostname}}", + Subject: "[EXEC] Restore Started: {{.Database}} on {{.Hostname}}", TextBody: restoreStartedText, HTMLBody: restoreStartedHTML, }, EventRestoreCompleted: { - Subject: "✅ Restore Completed: {{.Database}} on {{.Hostname}}", + Subject: "[OK] Restore Completed: {{.Database}} on {{.Hostname}}", TextBody: restoreCompletedText, HTMLBody: restoreCompletedHTML, }, EventRestoreFailed: { - Subject: "❌ Restore FAILED: {{.Database}} on {{.Hostname}}", + Subject: "[FAIL] Restore FAILED: {{.Database}} on {{.Hostname}}", TextBody: restoreFailedText, HTMLBody: restoreFailedHTML, }, EventVerificationPassed: { - Subject: "✅ Verification Passed: {{.Database}}", + Subject: "[OK] Verification Passed: {{.Database}}", TextBody: verificationPassedText, HTMLBody: verificationPassedHTML, }, EventVerificationFailed: { - Subject: "❌ Verification FAILED: {{.Database}}", + Subject: "[FAIL] Verification FAILED: {{.Database}}", TextBody: verificationFailedText, HTMLBody: verificationFailedHTML, }, EventDRDrillPassed: { - Subject: "✅ DR Drill Passed: {{.Database}}", + Subject: "[OK] DR Drill Passed: {{.Database}}", TextBody: drDrillPassedText, HTMLBody: drDrillPassedHTML, }, EventDRDrillFailed: { - Subject: "❌ DR Drill FAILED: {{.Database}}", + Subject: "[FAIL] DR Drill FAILED: {{.Database}}", TextBody: drDrillFailedText, HTMLBody: drDrillFailedHTML, }, @@ -95,7 +95,7 @@ Started At: {{formatTime .Timestamp}} const backupStartedHTML = `
-

🔄 Backup Started

+

[EXEC] Backup Started

@@ -121,7 +121,7 @@ Completed: {{formatTime .Timestamp}} const backupCompletedHTML = `
-

✅ Backup Completed

+

[OK] Backup Completed

Database:{{.Database}}
Hostname:{{.Hostname}}
@@ -137,7 +137,7 @@ const backupCompletedHTML = ` ` const backupFailedText = ` -⚠️ BACKUP FAILED ⚠️ +[WARN] BACKUP FAILED [WARN] Database: {{.Database}} Hostname: {{.Hostname}} @@ -152,7 +152,7 @@ Please investigate immediately. const backupFailedHTML = `
-

❌ Backup FAILED

+

[FAIL] Backup FAILED

Database:{{.Database}}
Hostname:{{.Hostname}}
@@ -176,7 +176,7 @@ Started At: {{formatTime .Timestamp}} const restoreStartedHTML = `
-

🔄 Restore Started

+

[EXEC] Restore Started

Database:{{.Database}}
Hostname:{{.Hostname}}
@@ -200,7 +200,7 @@ Completed: {{formatTime .Timestamp}} const restoreCompletedHTML = `
-

✅ Restore Completed

+

[OK] Restore Completed

Database:{{.Database}}
Hostname:{{.Hostname}}
@@ -214,7 +214,7 @@ const restoreCompletedHTML = ` ` const restoreFailedText = ` -⚠️ RESTORE FAILED ⚠️ +[WARN] RESTORE FAILED [WARN] Database: {{.Database}} Hostname: {{.Hostname}} @@ -229,7 +229,7 @@ Please investigate immediately. const restoreFailedHTML = `
-

❌ Restore FAILED

+

[FAIL] Restore FAILED

Database:{{.Database}}
Hostname:{{.Hostname}}
@@ -255,7 +255,7 @@ Verified: {{formatTime .Timestamp}} const verificationPassedHTML = `
-

✅ Verification Passed

+

[OK] Verification Passed

Database:{{.Database}}
Hostname:{{.Hostname}}
@@ -269,7 +269,7 @@ const verificationPassedHTML = ` ` const verificationFailedText = ` -⚠️ VERIFICATION FAILED ⚠️ +[WARN] VERIFICATION FAILED [WARN] Database: {{.Database}} Hostname: {{.Hostname}} @@ -284,7 +284,7 @@ Backup integrity may be compromised. Please investigate. const verificationFailedHTML = `
-

❌ Verification FAILED

+

[FAIL] Verification FAILED

Database:{{.Database}}
Hostname:{{.Hostname}}
@@ -314,7 +314,7 @@ Backup restore capability verified. const drDrillPassedHTML = `
-

✅ DR Drill Passed

+

[OK] DR Drill Passed

Database:{{.Database}}
Hostname:{{.Hostname}}
@@ -326,12 +326,12 @@ const drDrillPassedHTML = ` {{end}}
Database:{{.Database}}
Hostname:{{.Hostname}}
{{if .Message}}

{{.Message}}

{{end}} -

✓ Backup restore capability verified

+

[OK] Backup restore capability verified

` const drDrillFailedText = ` -⚠️ DR DRILL FAILED ⚠️ +[WARN] DR DRILL FAILED [WARN] Database: {{.Database}} Hostname: {{.Hostname}} @@ -346,7 +346,7 @@ Backup may not be restorable. Please investigate immediately. const drDrillFailedHTML = `
-

❌ DR Drill FAILED

+

[FAIL] DR Drill FAILED

diff --git a/internal/pitr/restore.go b/internal/pitr/restore.go index 8fa8169..f9323f5 100644 --- a/internal/pitr/restore.go +++ b/internal/pitr/restore.go @@ -43,9 +43,9 @@ type RestoreOptions struct { // RestorePointInTime performs a Point-in-Time Recovery func (ro *RestoreOrchestrator) RestorePointInTime(ctx context.Context, opts *RestoreOptions) error { - ro.log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + ro.log.Info("=====================================================") ro.log.Info(" Point-in-Time Recovery (PITR)") - ro.log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + ro.log.Info("=====================================================") ro.log.Info("") ro.log.Info("Target:", "summary", opts.Target.Summary()) ro.log.Info("Base Backup:", "path", opts.BaseBackupPath) @@ -91,11 +91,11 @@ func (ro *RestoreOrchestrator) RestorePointInTime(ctx context.Context, opts *Res return fmt.Errorf("failed to generate recovery configuration: %w", err) } - ro.log.Info("✅ Recovery configuration generated successfully") + ro.log.Info("[OK] Recovery configuration generated successfully") ro.log.Info("") - ro.log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + ro.log.Info("=====================================================") ro.log.Info(" Next Steps:") - ro.log.Info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + ro.log.Info("=====================================================") ro.log.Info("") ro.log.Info("1. Start PostgreSQL to begin recovery:") ro.log.Info(fmt.Sprintf(" pg_ctl -D %s start", opts.TargetDataDir)) @@ -192,7 +192,7 @@ func (ro *RestoreOrchestrator) validateInputs(opts *RestoreOptions) error { } } - ro.log.Info("✅ Validation passed") + ro.log.Info("[OK] Validation passed") return nil } @@ -238,7 +238,7 @@ func (ro *RestoreOrchestrator) extractTarGzBackup(ctx context.Context, source, d return fmt.Errorf("tar extraction failed: %w", err) } - ro.log.Info("✅ Base backup extracted successfully") + ro.log.Info("[OK] Base backup extracted successfully") return nil } @@ -254,7 +254,7 @@ func (ro *RestoreOrchestrator) extractTarBackup(ctx context.Context, source, des return fmt.Errorf("tar extraction failed: %w", err) } - ro.log.Info("✅ Base backup extracted successfully") + ro.log.Info("[OK] Base backup extracted successfully") return nil } @@ -270,7 +270,7 @@ func (ro *RestoreOrchestrator) copyDirectoryBackup(ctx context.Context, source, return fmt.Errorf("directory copy failed: %w", err) } - ro.log.Info("✅ Base backup copied successfully") + ro.log.Info("[OK] Base backup copied successfully") return nil } @@ -291,7 +291,7 @@ func (ro *RestoreOrchestrator) startPostgreSQL(ctx context.Context, opts *Restor return fmt.Errorf("pg_ctl start failed: %w", err) } - ro.log.Info("✅ PostgreSQL started successfully") + ro.log.Info("[OK] PostgreSQL started successfully") ro.log.Info("PostgreSQL is now performing recovery...") return nil } @@ -320,7 +320,7 @@ func (ro *RestoreOrchestrator) monitorRecovery(ctx context.Context, opts *Restor // Check if recovery is complete by looking for postmaster.pid pidFile := filepath.Join(opts.TargetDataDir, "postmaster.pid") if _, err := os.Stat(pidFile); err == nil { - ro.log.Info("✅ PostgreSQL is running") + ro.log.Info("[OK] PostgreSQL is running") // Check if recovery files still exist recoverySignal := filepath.Join(opts.TargetDataDir, "recovery.signal") @@ -328,7 +328,7 @@ func (ro *RestoreOrchestrator) monitorRecovery(ctx context.Context, opts *Restor if _, err := os.Stat(recoverySignal); os.IsNotExist(err) { if _, err := os.Stat(recoveryConf); os.IsNotExist(err) { - ro.log.Info("✅ Recovery completed - PostgreSQL promoted to primary") + ro.log.Info("[OK] Recovery completed - PostgreSQL promoted to primary") return nil } } diff --git a/internal/progress/detailed.go b/internal/progress/detailed.go index d6211b7..ce32762 100755 --- a/internal/progress/detailed.go +++ b/internal/progress/detailed.go @@ -256,7 +256,7 @@ func (ot *OperationTracker) Complete(message string) { // Complete visual indicator if ot.reporter.indicator != nil { - ot.reporter.indicator.Complete(fmt.Sprintf("✅ %s", message)) + ot.reporter.indicator.Complete(fmt.Sprintf("[OK] %s", message)) } // Log completion with duration @@ -286,7 +286,7 @@ func (ot *OperationTracker) Fail(err error) { // Fail visual indicator if ot.reporter.indicator != nil { - ot.reporter.indicator.Fail(fmt.Sprintf("❌ %s", err.Error())) + ot.reporter.indicator.Fail(fmt.Sprintf("[FAIL] %s", err.Error())) } // Log failure @@ -427,7 +427,7 @@ type OperationSummary struct { // FormatSummary returns a formatted string representation of the summary func (os *OperationSummary) FormatSummary() string { return fmt.Sprintf( - "📊 Operations Summary:\n"+ + "[STATS] Operations Summary:\n"+ " Total: %d | Completed: %d | Failed: %d | Running: %d\n"+ " Total Duration: %s", os.TotalOperations, diff --git a/internal/progress/progress.go b/internal/progress/progress.go index 85805f4..e7f18b4 100755 --- a/internal/progress/progress.go +++ b/internal/progress/progress.go @@ -92,13 +92,13 @@ func (s *Spinner) Update(message string) { // Complete stops the spinner with a success message func (s *Spinner) Complete(message string) { s.Stop() - fmt.Fprintf(s.writer, "\n✅ %s\n", message) + fmt.Fprintf(s.writer, "\n[OK] %s\n", message) } // Fail stops the spinner with a failure message func (s *Spinner) Fail(message string) { s.Stop() - fmt.Fprintf(s.writer, "\n❌ %s\n", message) + fmt.Fprintf(s.writer, "\n[FAIL] %s\n", message) } // Stop stops the spinner @@ -167,13 +167,13 @@ func (d *Dots) Update(message string) { // Complete stops the dots with a success message func (d *Dots) Complete(message string) { d.Stop() - fmt.Fprintf(d.writer, " ✅ %s\n", message) + fmt.Fprintf(d.writer, " [OK] %s\n", message) } // Fail stops the dots with a failure message func (d *Dots) Fail(message string) { d.Stop() - fmt.Fprintf(d.writer, " ❌ %s\n", message) + fmt.Fprintf(d.writer, " [FAIL] %s\n", message) } // Stop stops the dots indicator @@ -239,14 +239,14 @@ func (p *ProgressBar) Complete(message string) { p.current = p.total p.message = message p.render() - fmt.Fprintf(p.writer, " ✅ %s\n", message) + fmt.Fprintf(p.writer, " [OK] %s\n", message) p.Stop() } // Fail stops the progress bar with failure func (p *ProgressBar) Fail(message string) { p.render() - fmt.Fprintf(p.writer, " ❌ %s\n", message) + fmt.Fprintf(p.writer, " [FAIL] %s\n", message) p.Stop() } @@ -298,12 +298,12 @@ func (s *Static) Update(message string) { // Complete shows completion message func (s *Static) Complete(message string) { - fmt.Fprintf(s.writer, " ✅ %s\n", message) + fmt.Fprintf(s.writer, " [OK] %s\n", message) } // Fail shows failure message func (s *Static) Fail(message string) { - fmt.Fprintf(s.writer, " ❌ %s\n", message) + fmt.Fprintf(s.writer, " [FAIL] %s\n", message) } // Stop does nothing for static indicator @@ -359,7 +359,7 @@ func (l *LineByLine) Start(message string) { if l.estimator != nil { displayMsg = l.estimator.GetFullStatus(message) } - fmt.Fprintf(l.writer, "\n🔄 %s\n", displayMsg) + fmt.Fprintf(l.writer, "\n[SYNC] %s\n", displayMsg) } // Update shows an update message @@ -380,12 +380,12 @@ func (l *LineByLine) SetEstimator(estimator *ETAEstimator) { // Complete shows completion message func (l *LineByLine) Complete(message string) { - fmt.Fprintf(l.writer, "✅ %s\n\n", message) + fmt.Fprintf(l.writer, "[OK] %s\n\n", message) } // Fail shows failure message func (l *LineByLine) Fail(message string) { - fmt.Fprintf(l.writer, "❌ %s\n\n", message) + fmt.Fprintf(l.writer, "[FAIL] %s\n\n", message) } // Stop does nothing for line-by-line (no cleanup needed) @@ -396,7 +396,7 @@ func (l *LineByLine) Stop() { // Light indicator methods - minimal output func (l *Light) Start(message string) { if !l.silent { - fmt.Fprintf(l.writer, "▶ %s\n", message) + fmt.Fprintf(l.writer, "> %s\n", message) } } @@ -408,13 +408,13 @@ func (l *Light) Update(message string) { func (l *Light) Complete(message string) { if !l.silent { - fmt.Fprintf(l.writer, "✓ %s\n", message) + fmt.Fprintf(l.writer, "[OK] %s\n", message) } } func (l *Light) Fail(message string) { if !l.silent { - fmt.Fprintf(l.writer, "✗ %s\n", message) + fmt.Fprintf(l.writer, "[FAIL] %s\n", message) } } diff --git a/internal/report/report.go b/internal/report/report.go index 57f6dd4..8f0300a 100644 --- a/internal/report/report.go +++ b/internal/report/report.go @@ -296,11 +296,11 @@ func generateID() string { func StatusIcon(s ComplianceStatus) string { switch s { case StatusCompliant: - return "✅" + return "[OK]" case StatusNonCompliant: - return "❌" + return "[FAIL]" case StatusPartial: - return "⚠️" + return "[WARN]" case StatusNotApplicable: return "➖" default: diff --git a/internal/restore/diagnose.go b/internal/restore/diagnose.go index 3eb4afc..15019a5 100644 --- a/internal/restore/diagnose.go +++ b/internal/restore/diagnose.go @@ -714,7 +714,7 @@ func (d *Diagnoser) DiagnoseClusterDumps(archivePath, tempDir string) ([]*Diagno // PrintDiagnosis outputs a human-readable diagnosis report func (d *Diagnoser) PrintDiagnosis(result *DiagnoseResult) { fmt.Println("\n" + strings.Repeat("=", 70)) - fmt.Printf("📋 DIAGNOSIS: %s\n", result.FileName) + fmt.Printf("[DIAG] DIAGNOSIS: %s\n", result.FileName) fmt.Println(strings.Repeat("=", 70)) // Basic info @@ -724,69 +724,69 @@ func (d *Diagnoser) PrintDiagnosis(result *DiagnoseResult) { // Status if result.IsValid { - fmt.Println("\n✅ STATUS: VALID") + fmt.Println("\n[OK] STATUS: VALID") } else { - fmt.Println("\n❌ STATUS: INVALID") + fmt.Println("\n[FAIL] STATUS: INVALID") } if result.IsTruncated { - fmt.Println("⚠️ TRUNCATED: Yes - file appears incomplete") + fmt.Println("[WARN] TRUNCATED: Yes - file appears incomplete") } if result.IsCorrupted { - fmt.Println("⚠️ CORRUPTED: Yes - file structure is damaged") + fmt.Println("[WARN] CORRUPTED: Yes - file structure is damaged") } // Details if result.Details != nil { - fmt.Println("\n📊 DETAILS:") + fmt.Println("\n[DETAILS]:") if result.Details.HasPGDMPSignature { - fmt.Println(" ✓ Has PGDMP signature (PostgreSQL custom format)") + fmt.Println(" [+] Has PGDMP signature (PostgreSQL custom format)") } if result.Details.HasSQLHeader { - fmt.Println(" ✓ Has PostgreSQL SQL header") + fmt.Println(" [+] Has PostgreSQL SQL header") } if result.Details.GzipValid { - fmt.Println(" ✓ Gzip compression valid") + fmt.Println(" [+] Gzip compression valid") } if result.Details.PgRestoreListable { - fmt.Printf(" ✓ pg_restore can list contents (%d tables)\n", result.Details.TableCount) + fmt.Printf(" [+] pg_restore can list contents (%d tables)\n", result.Details.TableCount) } if result.Details.CopyBlockCount > 0 { - fmt.Printf(" • Contains %d COPY blocks\n", result.Details.CopyBlockCount) + fmt.Printf(" [-] Contains %d COPY blocks\n", result.Details.CopyBlockCount) } if result.Details.UnterminatedCopy { - fmt.Printf(" ✗ Unterminated COPY block: %s (line %d)\n", + fmt.Printf(" [-] Unterminated COPY block: %s (line %d)\n", result.Details.LastCopyTable, result.Details.LastCopyLineNumber) } if result.Details.ProperlyTerminated { - fmt.Println(" ✓ All COPY blocks properly terminated") + fmt.Println(" [+] All COPY blocks properly terminated") } if result.Details.ExpandedSize > 0 { - fmt.Printf(" • Expanded size: %s (ratio: %.1fx)\n", + fmt.Printf(" [-] Expanded size: %s (ratio: %.1fx)\n", formatBytes(result.Details.ExpandedSize), result.Details.CompressionRatio) } } // Errors if len(result.Errors) > 0 { - fmt.Println("\n❌ ERRORS:") + fmt.Println("\n[ERRORS]:") for _, e := range result.Errors { - fmt.Printf(" • %s\n", e) + fmt.Printf(" - %s\n", e) } } // Warnings if len(result.Warnings) > 0 { - fmt.Println("\n⚠️ WARNINGS:") + fmt.Println("\n[WARNINGS]:") for _, w := range result.Warnings { - fmt.Printf(" • %s\n", w) + fmt.Printf(" - %s\n", w) } } // Recommendations if !result.IsValid { - fmt.Println("\n💡 RECOMMENDATIONS:") + fmt.Println("\n[HINT] RECOMMENDATIONS:") if result.IsTruncated { fmt.Println(" 1. Re-run the backup process for this database") fmt.Println(" 2. Check disk space on backup server during backup") diff --git a/internal/restore/engine.go b/internal/restore/engine.go index f6f7e1e..e838022 100755 --- a/internal/restore/engine.go +++ b/internal/restore/engine.go @@ -127,7 +127,7 @@ func (e *Engine) RestoreSingle(ctx context.Context, archivePath, targetDB string e.log.Warn("Checksum verification failed", "error", checksumErr) e.log.Warn("Continuing restore without checksum verification (use with caution)") } else { - e.log.Info("✓ Archive checksum verified successfully") + e.log.Info("[OK] Archive checksum verified successfully") } // Detect archive format @@ -461,7 +461,7 @@ func (e *Engine) executeRestoreCommandWithContext(ctx context.Context, cmdArgs [ e.log.Warn("Failed to save debug log", "error", saveErr) } else { e.log.Info("Debug log saved", "path", e.debugLogPath) - fmt.Printf("\n📋 Detailed error report saved to: %s\n", e.debugLogPath) + fmt.Printf("\n[LOG] Detailed error report saved to: %s\n", e.debugLogPath) } } } @@ -612,7 +612,7 @@ func (e *Engine) previewRestore(archivePath, targetDB string, format ArchiveForm fmt.Printf(" 1. Execute: mysql %s < %s\n", targetDB, archivePath) } - fmt.Println("\n⚠️ WARNING: This will restore data to the target database.") + fmt.Println("\n[WARN] WARNING: This will restore data to the target database.") fmt.Println(" Existing data may be overwritten or merged.") fmt.Println("\nTo execute this restore, add the --confirm flag.") fmt.Println(strings.Repeat("=", 60) + "\n") @@ -643,7 +643,7 @@ func (e *Engine) RestoreCluster(ctx context.Context, archivePath string) error { e.log.Warn("Checksum verification failed", "error", checksumErr) e.log.Warn("Continuing restore without checksum verification (use with caution)") } else { - e.log.Info("✓ Cluster archive checksum verified successfully") + e.log.Info("[OK] Cluster archive checksum verified successfully") } format := DetectArchiveFormat(archivePath) @@ -703,7 +703,7 @@ func (e *Engine) RestoreCluster(ctx context.Context, archivePath string) error { if !isSuperuser { e.log.Warn("Current user is not a superuser - database ownership may not be fully restored") - e.progress.Update("⚠️ Warning: Non-superuser - ownership restoration limited") + e.progress.Update("[WARN] Warning: Non-superuser - ownership restoration limited") time.Sleep(2 * time.Second) // Give user time to see warning } else { e.log.Info("Superuser privileges confirmed - full ownership restoration enabled") @@ -835,7 +835,7 @@ func (e *Engine) RestoreCluster(ctx context.Context, archivePath string) error { e.log.Warn("Large objects detected in dump files - reducing parallelism to avoid lock contention", "original_parallelism", parallelism, "adjusted_parallelism", 1) - e.progress.Update("⚠️ Large objects detected - using sequential restore to avoid lock conflicts") + e.progress.Update("[WARN] Large objects detected - using sequential restore to avoid lock conflicts") time.Sleep(2 * time.Second) // Give user time to see warning parallelism = 1 } @@ -1339,7 +1339,7 @@ func (e *Engine) previewClusterRestore(archivePath string) error { fmt.Println(" 3. Restore all databases found in archive") fmt.Println(" 4. Cleanup temporary files") - fmt.Println("\n⚠️ WARNING: This will restore multiple databases.") + fmt.Println("\n[WARN] WARNING: This will restore multiple databases.") fmt.Println(" Existing databases may be overwritten or merged.") fmt.Println("\nTo execute this restore, add the --confirm flag.") fmt.Println(strings.Repeat("=", 60) + "\n") diff --git a/internal/restore/error_report.go b/internal/restore/error_report.go index f5e5711..a3b3c5d 100644 --- a/internal/restore/error_report.go +++ b/internal/restore/error_report.go @@ -397,20 +397,20 @@ func (ec *ErrorCollector) SaveReport(report *RestoreErrorReport, outputPath stri // PrintReport prints a human-readable summary of the error report func (ec *ErrorCollector) PrintReport(report *RestoreErrorReport) { fmt.Println() - fmt.Println(strings.Repeat("═", 70)) - fmt.Println(" 🔴 RESTORE ERROR REPORT") - fmt.Println(strings.Repeat("═", 70)) + fmt.Println(strings.Repeat("=", 70)) + fmt.Println(" [ERROR] RESTORE ERROR REPORT") + fmt.Println(strings.Repeat("=", 70)) - fmt.Printf("\n📅 Timestamp: %s\n", report.Timestamp.Format("2006-01-02 15:04:05")) - fmt.Printf("📦 Archive: %s\n", filepath.Base(report.ArchivePath)) - fmt.Printf("📊 Format: %s\n", report.ArchiveFormat) - fmt.Printf("🎯 Target DB: %s\n", report.TargetDB) - fmt.Printf("⚠️ Exit Code: %d\n", report.ExitCode) - fmt.Printf("❌ Total Errors: %d\n", report.TotalErrors) + fmt.Printf("\n[TIME] Timestamp: %s\n", report.Timestamp.Format("2006-01-02 15:04:05")) + fmt.Printf("[FILE] Archive: %s\n", filepath.Base(report.ArchivePath)) + fmt.Printf("[FMT] Format: %s\n", report.ArchiveFormat) + fmt.Printf("[TGT] Target DB: %s\n", report.TargetDB) + fmt.Printf("[CODE] Exit Code: %d\n", report.ExitCode) + fmt.Printf("[ERR] Total Errors: %d\n", report.TotalErrors) - fmt.Println("\n" + strings.Repeat("─", 70)) + fmt.Println("\n" + strings.Repeat("-", 70)) fmt.Println("ERROR DETAILS:") - fmt.Println(strings.Repeat("─", 70)) + fmt.Println(strings.Repeat("-", 70)) fmt.Printf("\nType: %s\n", report.ErrorType) fmt.Printf("Message: %s\n", report.ErrorMessage) @@ -420,9 +420,9 @@ func (ec *ErrorCollector) PrintReport(report *RestoreErrorReport) { // Show failure context if report.FailureContext != nil && report.FailureContext.FailedLine > 0 { - fmt.Println("\n" + strings.Repeat("─", 70)) + fmt.Println("\n" + strings.Repeat("-", 70)) fmt.Println("FAILURE CONTEXT:") - fmt.Println(strings.Repeat("─", 70)) + fmt.Println(strings.Repeat("-", 70)) fmt.Printf("\nFailed at line: %d\n", report.FailureContext.FailedLine) if report.FailureContext.InCopyBlock { @@ -439,9 +439,9 @@ func (ec *ErrorCollector) PrintReport(report *RestoreErrorReport) { // Show first few errors if len(report.FirstErrors) > 0 { - fmt.Println("\n" + strings.Repeat("─", 70)) + fmt.Println("\n" + strings.Repeat("-", 70)) fmt.Println("FIRST ERRORS:") - fmt.Println(strings.Repeat("─", 70)) + fmt.Println(strings.Repeat("-", 70)) for i, err := range report.FirstErrors { if i >= 5 { @@ -454,15 +454,15 @@ func (ec *ErrorCollector) PrintReport(report *RestoreErrorReport) { // Show diagnosis summary if report.DiagnosisResult != nil && !report.DiagnosisResult.IsValid { - fmt.Println("\n" + strings.Repeat("─", 70)) + fmt.Println("\n" + strings.Repeat("-", 70)) fmt.Println("DIAGNOSIS:") - fmt.Println(strings.Repeat("─", 70)) + fmt.Println(strings.Repeat("-", 70)) if report.DiagnosisResult.IsTruncated { - fmt.Println(" ❌ File is TRUNCATED") + fmt.Println(" [FAIL] File is TRUNCATED") } if report.DiagnosisResult.IsCorrupted { - fmt.Println(" ❌ File is CORRUPTED") + fmt.Println(" [FAIL] File is CORRUPTED") } for i, err := range report.DiagnosisResult.Errors { if i >= 3 { @@ -473,18 +473,18 @@ func (ec *ErrorCollector) PrintReport(report *RestoreErrorReport) { } // Show recommendations - fmt.Println("\n" + strings.Repeat("─", 70)) - fmt.Println("💡 RECOMMENDATIONS:") - fmt.Println(strings.Repeat("─", 70)) + fmt.Println("\n" + strings.Repeat("-", 70)) + fmt.Println("[HINT] RECOMMENDATIONS:") + fmt.Println(strings.Repeat("-", 70)) for _, rec := range report.Recommendations { - fmt.Printf(" • %s\n", rec) + fmt.Printf(" - %s\n", rec) } // Show tool versions - fmt.Println("\n" + strings.Repeat("─", 70)) + fmt.Println("\n" + strings.Repeat("-", 70)) fmt.Println("ENVIRONMENT:") - fmt.Println(strings.Repeat("─", 70)) + fmt.Println(strings.Repeat("-", 70)) fmt.Printf(" OS: %s/%s\n", report.OS, report.Arch) fmt.Printf(" Go: %s\n", report.GoVersion) @@ -495,7 +495,7 @@ func (ec *ErrorCollector) PrintReport(report *RestoreErrorReport) { fmt.Printf(" psql: %s\n", report.PsqlVersion) } - fmt.Println(strings.Repeat("═", 70)) + fmt.Println(strings.Repeat("=", 70)) } // Helper functions diff --git a/internal/security/privileges.go b/internal/security/privileges.go index 72b762c..ef81f94 100755 --- a/internal/security/privileges.go +++ b/internal/security/privileges.go @@ -25,7 +25,7 @@ func (pc *PrivilegeChecker) CheckAndWarn(allowRoot bool) error { isRoot, user := pc.isRunningAsRoot() if isRoot { - pc.log.Warn("⚠️ Running with elevated privileges (root/Administrator)") + pc.log.Warn("[WARN] Running with elevated privileges (root/Administrator)") pc.log.Warn("Security recommendation: Create a dedicated backup user with minimal privileges") if !allowRoot { diff --git a/internal/security/resources.go b/internal/security/resources.go index 6cf2866..c79c602 100755 --- a/internal/security/resources.go +++ b/internal/security/resources.go @@ -64,7 +64,7 @@ func (rc *ResourceChecker) ValidateResourcesForBackup(estimatedSize int64) error if len(warnings) > 0 { for _, warning := range warnings { - rc.log.Warn("⚠️ Resource constraint: " + warning) + rc.log.Warn("[WARN] Resource constraint: " + warning) } rc.log.Info("Continuing backup operation (warnings are informational)") } diff --git a/internal/security/resources_unix.go b/internal/security/resources_unix.go index 2e1ac58..8edeffa 100644 --- a/internal/security/resources_unix.go +++ b/internal/security/resources_unix.go @@ -22,7 +22,7 @@ func (rc *ResourceChecker) checkPlatformLimits() (*ResourceLimits, error) { rc.log.Debug("Resource limit: max open files", "limit", rLimit.Cur, "max", rLimit.Max) if rLimit.Cur < 1024 { - rc.log.Warn("⚠️ Low file descriptor limit detected", + rc.log.Warn("[WARN] Low file descriptor limit detected", "current", rLimit.Cur, "recommended", 4096, "hint", "Increase with: ulimit -n 4096") diff --git a/internal/tui/archive_browser.go b/internal/tui/archive_browser.go index 4988426..9932101 100755 --- a/internal/tui/archive_browser.go +++ b/internal/tui/archive_browser.go @@ -209,12 +209,12 @@ func (m ArchiveBrowserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Validate selection based on mode if m.mode == "restore-cluster" && !selected.Format.IsClusterBackup() { - m.message = errorStyle.Render("❌ Please select a cluster backup (.tar.gz)") + m.message = errorStyle.Render("[FAIL] Please select a cluster backup (.tar.gz)") return m, nil } if m.mode == "restore-single" && selected.Format.IsClusterBackup() { - m.message = errorStyle.Render("❌ Please select a single database backup") + m.message = errorStyle.Render("[FAIL] Please select a single database backup") return m, nil } @@ -227,7 +227,7 @@ func (m ArchiveBrowserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Show detailed info if len(m.archives) > 0 && m.cursor < len(m.archives) { selected := m.archives[m.cursor] - m.message = fmt.Sprintf("📦 %s | Format: %s | Size: %s | Modified: %s", + m.message = fmt.Sprintf("[PKG] %s | Format: %s | Size: %s | Modified: %s", selected.Name, selected.Format.String(), formatSize(selected.Size), @@ -251,13 +251,13 @@ func (m ArchiveBrowserModel) View() string { var s strings.Builder // Header - title := "📦 Backup Archives" + title := "[PKG] Backup Archives" if m.mode == "restore-single" { - title = "📦 Select Archive to Restore (Single Database)" + title = "[PKG] Select Archive to Restore (Single Database)" } else if m.mode == "restore-cluster" { - title = "📦 Select Archive to Restore (Cluster)" + title = "[PKG] Select Archive to Restore (Cluster)" } else if m.mode == "diagnose" { - title = "🔍 Select Archive to Diagnose" + title = "[SEARCH] Select Archive to Diagnose" } s.WriteString(titleStyle.Render(title)) @@ -269,7 +269,7 @@ func (m ArchiveBrowserModel) View() string { } if m.err != nil { - s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Error: %v", m.err))) + s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Error: %v", m.err))) s.WriteString("\n\n") s.WriteString(infoStyle.Render("Press Esc to go back")) return s.String() @@ -293,7 +293,7 @@ func (m ArchiveBrowserModel) View() string { s.WriteString(archiveHeaderStyle.Render(fmt.Sprintf("%-40s %-25s %-12s %-20s", "FILENAME", "FORMAT", "SIZE", "MODIFIED"))) s.WriteString("\n") - s.WriteString(strings.Repeat("─", 100)) + s.WriteString(strings.Repeat("-", 100)) s.WriteString("\n") // Show archives (limit to visible area) @@ -317,13 +317,13 @@ func (m ArchiveBrowserModel) View() string { } // Color code based on validity and age - statusIcon := "✓" + statusIcon := "[+]" if !archive.Valid { - statusIcon = "✗" + statusIcon = "[-]" style = archiveInvalidStyle } else if time.Since(archive.Modified) > 30*24*time.Hour { style = archiveOldStyle - statusIcon = "⚠" + statusIcon = "[WARN]" } filename := truncate(archive.Name, 38) @@ -351,7 +351,7 @@ func (m ArchiveBrowserModel) View() string { s.WriteString(infoStyle.Render(fmt.Sprintf("Total: %d archive(s) | Selected: %d/%d", len(m.archives), m.cursor+1, len(m.archives)))) s.WriteString("\n") - s.WriteString(infoStyle.Render("⌨️ ↑/↓: Navigate | Enter: Select | d: Diagnose | f: Filter | i: Info | Esc: Back")) + s.WriteString(infoStyle.Render("[KEY] ↑/↓: Navigate | Enter: Select | d: Diagnose | f: Filter | i: Info | Esc: Back")) return s.String() } diff --git a/internal/tui/backup_exec.go b/internal/tui/backup_exec.go index e4b515a..6bf82fb 100755 --- a/internal/tui/backup_exec.go +++ b/internal/tui/backup_exec.go @@ -136,11 +136,11 @@ func executeBackupWithTUIProgress(parentCtx context.Context, cfg *config.Config, var result string switch backupType { case "single": - result = fmt.Sprintf("✓ Single database backup of '%s' completed successfully in %v", dbName, elapsed) + result = fmt.Sprintf("[+] Single database backup of '%s' completed successfully in %v", dbName, elapsed) case "sample": - result = fmt.Sprintf("✓ Sample backup of '%s' (ratio: %d) completed successfully in %v", dbName, ratio, elapsed) + result = fmt.Sprintf("[+] Sample backup of '%s' (ratio: %d) completed successfully in %v", dbName, ratio, elapsed) case "cluster": - result = fmt.Sprintf("✓ Cluster backup completed successfully in %v", elapsed) + result = fmt.Sprintf("[+] Cluster backup completed successfully in %v", elapsed) } return backupCompleteMsg{ @@ -200,9 +200,9 @@ func (m BackupExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.err = msg.err m.result = msg.result if m.err == nil { - m.status = "✅ Backup completed successfully!" + m.status = "[OK] Backup completed successfully!" } else { - m.status = fmt.Sprintf("❌ Backup failed: %v", m.err) + m.status = fmt.Sprintf("[FAIL] Backup failed: %v", m.err) } // Auto-forward in debug/auto-confirm mode if m.config.TUIAutoConfirm { @@ -216,7 +216,7 @@ func (m BackupExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if !m.done && !m.cancelling { // User requested cancellation - cancel the context m.cancelling = true - m.status = "⏹️ Cancelling backup... (please wait)" + m.status = "[STOP] Cancelling backup... (please wait)" if m.cancel != nil { m.cancel() } @@ -240,7 +240,7 @@ func (m BackupExecutionModel) View() string { // Clear screen with newlines and render header s.WriteString("\n\n") - header := titleStyle.Render("🔄 Backup Execution") + header := titleStyle.Render("[EXEC] Backup Execution") s.WriteString(header) s.WriteString("\n\n") @@ -261,13 +261,13 @@ func (m BackupExecutionModel) View() string { s.WriteString(fmt.Sprintf(" %s %s\n", spinnerFrames[m.spinnerFrame], m.status)) } else { s.WriteString(fmt.Sprintf(" %s %s\n", spinnerFrames[m.spinnerFrame], m.status)) - s.WriteString("\n ⌨️ Press Ctrl+C or ESC to cancel\n") + s.WriteString("\n [KEY] Press Ctrl+C or ESC to cancel\n") } } else { s.WriteString(fmt.Sprintf(" %s\n\n", m.status)) if m.err != nil { - s.WriteString(fmt.Sprintf(" ❌ Error: %v\n", m.err)) + s.WriteString(fmt.Sprintf(" [FAIL] Error: %v\n", m.err)) } else if m.result != "" { // Parse and display result cleanly lines := strings.Split(m.result, "\n") @@ -278,7 +278,7 @@ func (m BackupExecutionModel) View() string { } } } - s.WriteString("\n ⌨️ Press Enter or ESC to return to menu\n") + s.WriteString("\n [KEY] Press Enter or ESC to return to menu\n") } return s.String() diff --git a/internal/tui/backup_manager.go b/internal/tui/backup_manager.go index ccddd27..6439e2c 100755 --- a/internal/tui/backup_manager.go +++ b/internal/tui/backup_manager.go @@ -86,7 +86,7 @@ func (m BackupManagerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Verify archive if len(m.archives) > 0 && m.cursor < len(m.archives) { selected := m.archives[m.cursor] - m.message = fmt.Sprintf("🔍 Verifying %s...", selected.Name) + m.message = fmt.Sprintf("[SEARCH] Verifying %s...", selected.Name) // In real implementation, would run verification } @@ -96,16 +96,16 @@ func (m BackupManagerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { selected := m.archives[m.cursor] archivePath := selected.Path confirm := NewConfirmationModelWithAction(m.config, m.logger, m, - "🗑️ Delete Archive", + "[DELETE] Delete Archive", fmt.Sprintf("Delete archive '%s'? This cannot be undone.", selected.Name), func() (tea.Model, tea.Cmd) { // Delete the archive err := deleteArchive(archivePath) if err != nil { m.err = fmt.Errorf("failed to delete archive: %v", err) - m.message = fmt.Sprintf("❌ Failed to delete: %v", err) + m.message = fmt.Sprintf("[FAIL] Failed to delete: %v", err) } else { - m.message = fmt.Sprintf("✅ Deleted: %s", selected.Name) + m.message = fmt.Sprintf("[OK] Deleted: %s", selected.Name) } // Refresh the archive list m.loading = true @@ -118,7 +118,7 @@ func (m BackupManagerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Show info if len(m.archives) > 0 && m.cursor < len(m.archives) { selected := m.archives[m.cursor] - m.message = fmt.Sprintf("📦 %s | %s | %s | Modified: %s", + m.message = fmt.Sprintf("[PKG] %s | %s | %s | Modified: %s", selected.Name, selected.Format.String(), formatSize(selected.Size), @@ -152,7 +152,7 @@ func (m BackupManagerModel) View() string { var s strings.Builder // Title - s.WriteString(titleStyle.Render("🗄️ Backup Archive Manager")) + s.WriteString(titleStyle.Render("[DB] Backup Archive Manager")) s.WriteString("\n\n") if m.loading { @@ -161,7 +161,7 @@ func (m BackupManagerModel) View() string { } if m.err != nil { - s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Error: %v", m.err))) + s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Error: %v", m.err))) s.WriteString("\n\n") s.WriteString(infoStyle.Render("Press Esc to go back")) return s.String() @@ -184,7 +184,7 @@ func (m BackupManagerModel) View() string { s.WriteString(archiveHeaderStyle.Render(fmt.Sprintf("%-35s %-25s %-12s %-20s", "FILENAME", "FORMAT", "SIZE", "MODIFIED"))) s.WriteString("\n") - s.WriteString(strings.Repeat("─", 95)) + s.WriteString(strings.Repeat("-", 95)) s.WriteString("\n") // Show archives (limit to visible area) @@ -208,12 +208,12 @@ func (m BackupManagerModel) View() string { } // Status icon - statusIcon := "✓" + statusIcon := "[+]" if !archive.Valid { - statusIcon = "✗" + statusIcon = "[-]" style = archiveInvalidStyle } else if time.Since(archive.Modified) > 30*24*time.Hour { - statusIcon = "⚠" + statusIcon = "[WARN]" } filename := truncate(archive.Name, 33) @@ -240,7 +240,7 @@ func (m BackupManagerModel) View() string { s.WriteString(infoStyle.Render(fmt.Sprintf("Selected: %d/%d", m.cursor+1, len(m.archives)))) s.WriteString("\n") - s.WriteString(infoStyle.Render("⌨️ ↑/↓: Navigate | r: Restore | v: Verify | d: Delete | i: Info | R: Refresh | Esc: Back")) + s.WriteString(infoStyle.Render("[KEY] ↑/↓: Navigate | r: Restore | v: Verify | d: Delete | i: Info | R: Refresh | Esc: Back")) return s.String() } diff --git a/internal/tui/confirmation.go b/internal/tui/confirmation.go index 4a91dd7..edb4f83 100755 --- a/internal/tui/confirmation.go +++ b/internal/tui/confirmation.go @@ -129,7 +129,7 @@ func (m ConfirmationModel) View() string { s.WriteString(" ") } - s.WriteString("\n\n⌨️ ←/→: Select • Enter/y: Confirm • n/ESC: Cancel\n") + s.WriteString("\n\n[KEYS] <-/->: Select | Enter/y: Confirm | n/ESC: Cancel\n") return s.String() } diff --git a/internal/tui/dbselector.go b/internal/tui/dbselector.go index 25096e1..81baafa 100755 --- a/internal/tui/dbselector.go +++ b/internal/tui/dbselector.go @@ -109,7 +109,7 @@ func (m DatabaseSelectorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return executor, executor.Init() } inputModel := NewInputModel(m.config, m.logger, m, - "📊 Sample Ratio", + "[STATS] Sample Ratio", "Enter sample ratio (1-100):", "10", ValidateInt(1, 100)) @@ -152,7 +152,7 @@ func (m DatabaseSelectorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // If sample backup, ask for ratio first if m.backupType == "sample" { inputModel := NewInputModel(m.config, m.logger, m, - "📊 Sample Ratio", + "[STATS] Sample Ratio", "Enter sample ratio (1-100):", "10", ValidateInt(1, 100)) @@ -176,12 +176,12 @@ func (m DatabaseSelectorModel) View() string { s.WriteString(fmt.Sprintf("\n%s\n\n", header)) if m.loading { - s.WriteString("⏳ Loading databases...\n") + s.WriteString("[WAIT] Loading databases...\n") return s.String() } if m.err != nil { - s.WriteString(fmt.Sprintf("❌ Error: %v\n", m.err)) + s.WriteString(fmt.Sprintf("[FAIL] Error: %v\n", m.err)) s.WriteString("\nPress ESC to go back\n") return s.String() } @@ -203,7 +203,7 @@ func (m DatabaseSelectorModel) View() string { s.WriteString(fmt.Sprintf("\n%s\n", m.message)) } - s.WriteString("\n⌨️ ↑/↓: Navigate • Enter: Select • ESC: Back • q: Quit\n") + s.WriteString("\n[KEYS] Up/Down: Navigate | Enter: Select | ESC: Back | q: Quit\n") return s.String() } diff --git a/internal/tui/diagnose_view.go b/internal/tui/diagnose_view.go index 41a0e72..2683abc 100644 --- a/internal/tui/diagnose_view.go +++ b/internal/tui/diagnose_view.go @@ -160,7 +160,7 @@ func (m DiagnoseViewModel) View() string { var s strings.Builder // Header - s.WriteString(titleStyle.Render("🔍 Backup Diagnosis")) + s.WriteString(titleStyle.Render("[SEARCH] Backup Diagnosis")) s.WriteString("\n\n") // Archive info @@ -175,14 +175,14 @@ func (m DiagnoseViewModel) View() string { s.WriteString("\n\n") if m.running { - s.WriteString(infoStyle.Render("⏳ " + m.progress)) + s.WriteString(infoStyle.Render("[WAIT] " + m.progress)) s.WriteString("\n\n") s.WriteString(diagnoseInfoStyle.Render("This may take a while for large archives...")) return s.String() } if m.err != nil { - s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Diagnosis failed: %v", m.err))) + s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Diagnosis failed: %v", m.err))) s.WriteString("\n\n") s.WriteString(infoStyle.Render("Press Enter or Esc to go back")) return s.String() @@ -205,72 +205,72 @@ func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) st var s strings.Builder // Status - s.WriteString(strings.Repeat("─", 60)) + s.WriteString(strings.Repeat("-", 60)) s.WriteString("\n") if result.IsValid { - s.WriteString(diagnosePassStyle.Render("✅ STATUS: VALID")) + s.WriteString(diagnosePassStyle.Render("[OK] STATUS: VALID")) } else { - s.WriteString(diagnoseFailStyle.Render("❌ STATUS: INVALID")) + s.WriteString(diagnoseFailStyle.Render("[FAIL] STATUS: INVALID")) } s.WriteString("\n") if result.IsTruncated { - s.WriteString(diagnoseFailStyle.Render("⚠️ TRUNCATED: File appears incomplete")) + s.WriteString(diagnoseFailStyle.Render("[WARN] TRUNCATED: File appears incomplete")) s.WriteString("\n") } if result.IsCorrupted { - s.WriteString(diagnoseFailStyle.Render("⚠️ CORRUPTED: File structure is damaged")) + s.WriteString(diagnoseFailStyle.Render("[WARN] CORRUPTED: File structure is damaged")) s.WriteString("\n") } - s.WriteString(strings.Repeat("─", 60)) + s.WriteString(strings.Repeat("-", 60)) s.WriteString("\n\n") // Details if result.Details != nil { - s.WriteString(diagnoseHeaderStyle.Render("📊 DETAILS:")) + s.WriteString(diagnoseHeaderStyle.Render("[STATS] DETAILS:")) s.WriteString("\n") if result.Details.HasPGDMPSignature { - s.WriteString(diagnosePassStyle.Render(" ✓ ")) + s.WriteString(diagnosePassStyle.Render(" [+] ")) s.WriteString("Has PGDMP signature (custom format)\n") } if result.Details.HasSQLHeader { - s.WriteString(diagnosePassStyle.Render(" ✓ ")) + s.WriteString(diagnosePassStyle.Render(" [+] ")) s.WriteString("Has PostgreSQL SQL header\n") } if result.Details.GzipValid { - s.WriteString(diagnosePassStyle.Render(" ✓ ")) + s.WriteString(diagnosePassStyle.Render(" [+] ")) s.WriteString("Gzip compression valid\n") } if result.Details.PgRestoreListable { - s.WriteString(diagnosePassStyle.Render(" ✓ ")) + s.WriteString(diagnosePassStyle.Render(" [+] ")) s.WriteString(fmt.Sprintf("pg_restore can list contents (%d tables)\n", result.Details.TableCount)) } if result.Details.CopyBlockCount > 0 { - s.WriteString(diagnoseInfoStyle.Render(" • ")) + s.WriteString(diagnoseInfoStyle.Render(" - ")) s.WriteString(fmt.Sprintf("Contains %d COPY blocks\n", result.Details.CopyBlockCount)) } if result.Details.UnterminatedCopy { - s.WriteString(diagnoseFailStyle.Render(" ✗ ")) + s.WriteString(diagnoseFailStyle.Render(" [-] ")) s.WriteString(fmt.Sprintf("Unterminated COPY block: %s (line %d)\n", result.Details.LastCopyTable, result.Details.LastCopyLineNumber)) } if result.Details.ProperlyTerminated { - s.WriteString(diagnosePassStyle.Render(" ✓ ")) + s.WriteString(diagnosePassStyle.Render(" [+] ")) s.WriteString("All COPY blocks properly terminated\n") } if result.Details.ExpandedSize > 0 { - s.WriteString(diagnoseInfoStyle.Render(" • ")) + s.WriteString(diagnoseInfoStyle.Render(" - ")) s.WriteString(fmt.Sprintf("Expanded size: %s (ratio: %.1fx)\n", formatSize(result.Details.ExpandedSize), result.Details.CompressionRatio)) } @@ -279,14 +279,14 @@ func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) st // Errors if len(result.Errors) > 0 { s.WriteString("\n") - s.WriteString(diagnoseFailStyle.Render("❌ ERRORS:")) + s.WriteString(diagnoseFailStyle.Render("[FAIL] ERRORS:")) s.WriteString("\n") for i, e := range result.Errors { if i >= 5 { s.WriteString(diagnoseInfoStyle.Render(fmt.Sprintf(" ... and %d more\n", len(result.Errors)-5))) break } - s.WriteString(diagnoseFailStyle.Render(" • ")) + s.WriteString(diagnoseFailStyle.Render(" - ")) s.WriteString(truncate(e, 70)) s.WriteString("\n") } @@ -295,14 +295,14 @@ func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) st // Warnings if len(result.Warnings) > 0 { s.WriteString("\n") - s.WriteString(diagnoseWarnStyle.Render("⚠️ WARNINGS:")) + s.WriteString(diagnoseWarnStyle.Render("[WARN] WARNINGS:")) s.WriteString("\n") for i, w := range result.Warnings { if i >= 3 { s.WriteString(diagnoseInfoStyle.Render(fmt.Sprintf(" ... and %d more\n", len(result.Warnings)-3))) break } - s.WriteString(diagnoseWarnStyle.Render(" • ")) + s.WriteString(diagnoseWarnStyle.Render(" - ")) s.WriteString(truncate(w, 70)) s.WriteString("\n") } @@ -311,7 +311,7 @@ func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) st // Recommendations if !result.IsValid { s.WriteString("\n") - s.WriteString(diagnoseHeaderStyle.Render("💡 RECOMMENDATIONS:")) + s.WriteString(diagnoseHeaderStyle.Render("[HINT] RECOMMENDATIONS:")) s.WriteString("\n") if result.IsTruncated { s.WriteString(" 1. Re-run the backup process for this database\n") @@ -341,17 +341,17 @@ func (m DiagnoseViewModel) renderClusterResults() string { } } - s.WriteString(strings.Repeat("─", 60)) + s.WriteString(strings.Repeat("-", 60)) s.WriteString("\n") - s.WriteString(diagnoseHeaderStyle.Render(fmt.Sprintf("📊 CLUSTER SUMMARY: %d databases\n", len(m.results)))) - s.WriteString(strings.Repeat("─", 60)) + s.WriteString(diagnoseHeaderStyle.Render(fmt.Sprintf("[STATS] CLUSTER SUMMARY: %d databases\n", len(m.results)))) + s.WriteString(strings.Repeat("-", 60)) s.WriteString("\n\n") if invalidCount == 0 { - s.WriteString(diagnosePassStyle.Render("✅ All dumps are valid")) + s.WriteString(diagnosePassStyle.Render("[OK] All dumps are valid")) s.WriteString("\n\n") } else { - s.WriteString(diagnoseFailStyle.Render(fmt.Sprintf("❌ %d/%d dumps have issues", invalidCount, len(m.results)))) + s.WriteString(diagnoseFailStyle.Render(fmt.Sprintf("[FAIL] %d/%d dumps have issues", invalidCount, len(m.results)))) s.WriteString("\n\n") } @@ -378,13 +378,13 @@ func (m DiagnoseViewModel) renderClusterResults() string { var status string if r.IsValid { - status = diagnosePassStyle.Render("✓") + status = diagnosePassStyle.Render("[+]") } else if r.IsTruncated { - status = diagnoseFailStyle.Render("✗ TRUNCATED") + status = diagnoseFailStyle.Render("[-] TRUNCATED") } else if r.IsCorrupted { - status = diagnoseFailStyle.Render("✗ CORRUPTED") + status = diagnoseFailStyle.Render("[-] CORRUPTED") } else { - status = diagnoseFailStyle.Render("✗ INVALID") + status = diagnoseFailStyle.Render("[-] INVALID") } line := fmt.Sprintf("%s %s %-35s %s", @@ -405,7 +405,7 @@ func (m DiagnoseViewModel) renderClusterResults() string { if m.cursor < len(m.results) { selected := m.results[m.cursor] s.WriteString("\n") - s.WriteString(strings.Repeat("─", 60)) + s.WriteString(strings.Repeat("-", 60)) s.WriteString("\n") s.WriteString(diagnoseHeaderStyle.Render("Selected: " + selected.FileName)) s.WriteString("\n\n") @@ -413,7 +413,7 @@ func (m DiagnoseViewModel) renderClusterResults() string { // Show condensed details for selected if selected.Details != nil { if selected.Details.UnterminatedCopy { - s.WriteString(diagnoseFailStyle.Render(" ✗ Unterminated COPY: ")) + s.WriteString(diagnoseFailStyle.Render(" [-] Unterminated COPY: ")) s.WriteString(selected.Details.LastCopyTable) s.WriteString(fmt.Sprintf(" (line %d)\n", selected.Details.LastCopyLineNumber)) } @@ -429,7 +429,7 @@ func (m DiagnoseViewModel) renderClusterResults() string { if i >= 2 { break } - s.WriteString(diagnoseFailStyle.Render(" • ")) + s.WriteString(diagnoseFailStyle.Render(" - ")) s.WriteString(truncate(e, 55)) s.WriteString("\n") } diff --git a/internal/tui/dirpicker.go b/internal/tui/dirpicker.go index c5115cc..c7b60a2 100755 --- a/internal/tui/dirpicker.go +++ b/internal/tui/dirpicker.go @@ -208,7 +208,7 @@ func (dp *DirectoryPicker) View() string { if dp.allowFiles { pickerType = "File/Directory" } - header := fmt.Sprintf("📁 %s Picker - %s", pickerType, dp.currentPath) + header := fmt.Sprintf("[DIR] %s Picker - %s", pickerType, dp.currentPath) content.WriteString(dp.styles.Header.Render(header)) content.WriteString("\n\n") @@ -216,13 +216,13 @@ func (dp *DirectoryPicker) View() string { for i, item := range dp.items { var prefix string if item.Name == ".." { - prefix = "⬆️ " + prefix = "[UP] " } else if item.Name == "Error reading directory" { - prefix = "❌ " + prefix = "[X] " } else if item.IsDir { - prefix = "📁 " + prefix = "[DIR] " } else { - prefix = "📄 " + prefix = "[FILE] " } line := prefix + item.Name @@ -235,9 +235,9 @@ func (dp *DirectoryPicker) View() string { } // Help text - help := "\n↑/↓: Navigate • Enter: Open/Select File • s: Select Directory • q/Esc: Cancel" + help := "\nUp/Down: Navigate | Enter: Open/Select File | s: Select Directory | q/Esc: Cancel" if !dp.allowFiles { - help = "\n↑/↓: Navigate • Enter: Open • s: Select Directory • q/Esc: Cancel" + help = "\nUp/Down: Navigate | Enter: Open | s: Select Directory | q/Esc: Cancel" } content.WriteString(dp.styles.Help.Render(help)) diff --git a/internal/tui/history.go b/internal/tui/history.go index 2af2e17..71bc78f 100755 --- a/internal/tui/history.go +++ b/internal/tui/history.go @@ -104,7 +104,7 @@ func loadHistory(cfg *config.Config) []HistoryEntry { Type: backupType, Database: database, Timestamp: info.ModTime(), - Status: "✅ Completed", + Status: "[OK] Completed", Filename: name, }) } @@ -191,11 +191,11 @@ func (m HistoryViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m HistoryViewModel) View() string { var s strings.Builder - header := titleStyle.Render("📜 Operation History") + header := titleStyle.Render("[HISTORY] Operation History") s.WriteString(fmt.Sprintf("\n%s\n\n", header)) if len(m.history) == 0 { - s.WriteString("📭 No backup history found\n\n") + s.WriteString("[EMPTY] No backup history found\n\n") } else { maxVisible := 15 // Show max 15 items at once @@ -211,7 +211,7 @@ func (m HistoryViewModel) View() string { // Show scroll indicators if start > 0 { - s.WriteString(" ▲ More entries above...\n") + s.WriteString(" [^] More entries above...\n") } // Display only visible entries @@ -233,13 +233,13 @@ func (m HistoryViewModel) View() string { // Show scroll indicator if more entries below if end < len(m.history) { - s.WriteString(fmt.Sprintf(" ▼ %d more entries below...\n", len(m.history)-end)) + s.WriteString(fmt.Sprintf(" [v] %d more entries below...\n", len(m.history)-end)) } s.WriteString("\n") } - s.WriteString("⌨️ ↑/↓: Navigate • PgUp/PgDn: Jump • Home/End: First/Last • ESC: Back • q: Quit\n") + s.WriteString("[KEYS] Up/Down: Navigate - PgUp/PgDn: Jump - Home/End: First/Last - ESC: Back - q: Quit\n") return s.String() } diff --git a/internal/tui/input.go b/internal/tui/input.go index 65083ef..e61ebd8 100755 --- a/internal/tui/input.go +++ b/internal/tui/input.go @@ -137,10 +137,10 @@ func (m InputModel) View() string { s.WriteString("\n\n") if m.err != nil { - s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Error: %v\n\n", m.err))) + s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Error: %v\n\n", m.err))) } - s.WriteString("⌨️ Type value • Enter: Confirm • ESC: Cancel\n") + s.WriteString("[KEYS] Type value | Enter: Confirm | ESC: Cancel\n") return s.String() } diff --git a/internal/tui/menu.go b/internal/tui/menu.go index 8853534..d3d2ae0 100755 --- a/internal/tui/menu.go +++ b/internal/tui/menu.go @@ -89,12 +89,12 @@ func NewMenuModel(cfg *config.Config, log logger.Logger) *MenuModel { "Single Database Backup", "Sample Database Backup (with ratio)", "Cluster Backup (all databases)", - "────────────────────────────────", + "--------------------------------", "Restore Single Database", "Restore Cluster Backup", "Diagnose Backup File", "List & Manage Backups", - "────────────────────────────────", + "--------------------------------", "View Active Operations", "Show Operation History", "Database Status & Health Check", @@ -177,7 +177,7 @@ func (m *MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case 12: // Settings return m.handleSettings() case 13: // Clear History - m.message = "🗑️ History cleared" + m.message = "[DEL] History cleared" case 14: // Quit if m.cancel != nil { m.cancel() @@ -262,7 +262,7 @@ func (m *MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case 12: // Settings return m.handleSettings() case 13: // Clear History - m.message = "🗑️ History cleared" + m.message = "[DEL] History cleared" case 14: // Quit if m.cancel != nil { m.cancel() @@ -285,7 +285,7 @@ func (m *MenuModel) View() string { var s string // Header - header := titleStyle.Render("🗄️ Database Backup Tool - Interactive Menu") + header := titleStyle.Render("[DB] Database Backup Tool - Interactive Menu") s += fmt.Sprintf("\n%s\n\n", header) if len(m.dbTypes) > 0 { @@ -299,7 +299,7 @@ func (m *MenuModel) View() string { } selector := fmt.Sprintf("Target Engine: %s", strings.Join(options, menuStyle.Render(" | "))) s += dbSelectorLabelStyle.Render(selector) + "\n" - hint := infoStyle.Render("Switch with ←/→ or t • Cluster backup requires PostgreSQL") + hint := infoStyle.Render("Switch with <-/-> or t | Cluster backup requires PostgreSQL") s += hint + "\n" } @@ -326,7 +326,7 @@ func (m *MenuModel) View() string { } // Footer - footer := infoStyle.Render("\n⌨️ Press ↑/↓ to navigate • Enter to select • q to quit") + footer := infoStyle.Render("\n[KEYS] Press Up/Down to navigate | Enter to select | q to quit") s += footer return s @@ -334,20 +334,20 @@ func (m *MenuModel) View() string { // handleSingleBackup opens database selector for single backup func (m *MenuModel) handleSingleBackup() (tea.Model, tea.Cmd) { - selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "🗄️ Single Database Backup", "single") + selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "[DB] Single Database Backup", "single") return selector, selector.Init() } // handleSampleBackup opens database selector for sample backup func (m *MenuModel) handleSampleBackup() (tea.Model, tea.Cmd) { - selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "📊 Sample Database Backup", "sample") + selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "[STATS] Sample Database Backup", "sample") return selector, selector.Init() } // handleClusterBackup shows confirmation and executes cluster backup func (m *MenuModel) handleClusterBackup() (tea.Model, tea.Cmd) { if !m.config.IsPostgreSQL() { - m.message = errorStyle.Render("❌ Cluster backup is available only for PostgreSQL targets") + m.message = errorStyle.Render("[FAIL] Cluster backup is available only for PostgreSQL targets") return m, nil } // Skip confirmation in auto-confirm mode @@ -356,7 +356,7 @@ func (m *MenuModel) handleClusterBackup() (tea.Model, tea.Cmd) { return executor, executor.Init() } confirm := NewConfirmationModelWithAction(m.config, m.logger, m, - "🗄️ Cluster Backup", + "[DB] Cluster Backup", "This will backup ALL databases in the cluster. Continue?", func() (tea.Model, tea.Cmd) { executor := NewBackupExecution(m.config, m.logger, m, m.ctx, "cluster", "", 0) @@ -399,7 +399,7 @@ func (m *MenuModel) handleRestoreSingle() (tea.Model, tea.Cmd) { // handleRestoreCluster opens archive browser for cluster restore func (m *MenuModel) handleRestoreCluster() (tea.Model, tea.Cmd) { if !m.config.IsPostgreSQL() { - m.message = errorStyle.Render("❌ Cluster restore is available only for PostgreSQL") + m.message = errorStyle.Render("[FAIL] Cluster restore is available only for PostgreSQL") return m, nil } browser := NewArchiveBrowser(m.config, m.logger, m, m.ctx, "restore-cluster") @@ -428,7 +428,7 @@ func (m *MenuModel) applyDatabaseSelection() { selection := m.dbTypes[m.dbTypeCursor] if err := m.config.SetDatabaseType(selection.value); err != nil { - m.message = errorStyle.Render(fmt.Sprintf("❌ %v", err)) + m.message = errorStyle.Render(fmt.Sprintf("[FAIL] %v", err)) return } @@ -437,7 +437,7 @@ func (m *MenuModel) applyDatabaseSelection() { m.config.Port = m.config.GetDefaultPort() } - m.message = successStyle.Render(fmt.Sprintf("🔀 Target database set to %s", m.config.DisplayDatabaseType())) + m.message = successStyle.Render(fmt.Sprintf("[SWITCH] Target database set to %s", m.config.DisplayDatabaseType())) if m.logger != nil { m.logger.Info("updated target database type", "type", m.config.DatabaseType, "port", m.config.Port) } diff --git a/internal/tui/operations.go b/internal/tui/operations.go index 6c9eadd..2d1898d 100755 --- a/internal/tui/operations.go +++ b/internal/tui/operations.go @@ -49,14 +49,14 @@ func (m OperationsViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m OperationsViewModel) View() string { var s strings.Builder - header := titleStyle.Render("📊 Active Operations") + header := titleStyle.Render("[STATS] Active Operations") s.WriteString(fmt.Sprintf("\n%s\n\n", header)) s.WriteString("Currently running operations:\n\n") - s.WriteString(infoStyle.Render("📭 No active operations")) + s.WriteString(infoStyle.Render("[NONE] No active operations")) s.WriteString("\n\n") - s.WriteString("⌨️ Press any key to return to menu\n") + s.WriteString("[KEYS] Press any key to return to menu\n") return s.String() } diff --git a/internal/tui/restore_exec.go b/internal/tui/restore_exec.go index 6a820e3..517419f 100755 --- a/internal/tui/restore_exec.go +++ b/internal/tui/restore_exec.go @@ -285,7 +285,7 @@ func (m RestoreExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if !m.done && !m.cancelling { // User requested cancellation - cancel the context m.cancelling = true - m.status = "⏹️ Cancelling restore... (please wait)" + m.status = "[STOP] Cancelling restore... (please wait)" m.phase = "Cancelling" if m.cancel != nil { m.cancel() @@ -297,7 +297,7 @@ func (m RestoreExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "q": if !m.done && !m.cancelling { m.cancelling = true - m.status = "⏹️ Cancelling restore... (please wait)" + m.status = "[STOP] Cancelling restore... (please wait)" m.phase = "Cancelling" if m.cancel != nil { m.cancel() @@ -321,9 +321,9 @@ func (m RestoreExecutionModel) View() string { s.Grow(512) // Pre-allocate estimated capacity for better performance // Title - title := "💾 Restoring Database" + title := "[RESTORE] Restoring Database" if m.restoreType == "restore-cluster" { - title = "💾 Restoring Cluster" + title = "[RESTORE] Restoring Cluster" } s.WriteString(titleStyle.Render(title)) s.WriteString("\n\n") @@ -338,12 +338,12 @@ func (m RestoreExecutionModel) View() string { if m.done { // Show result if m.err != nil { - s.WriteString(errorStyle.Render("❌ Restore Failed")) + s.WriteString(errorStyle.Render("[FAIL] Restore Failed")) s.WriteString("\n\n") s.WriteString(errorStyle.Render(fmt.Sprintf("Error: %v", m.err))) s.WriteString("\n") } else { - s.WriteString(successStyle.Render("✅ Restore Completed Successfully")) + s.WriteString(successStyle.Render("[OK] Restore Completed Successfully")) s.WriteString("\n\n") s.WriteString(successStyle.Render(m.result)) s.WriteString("\n") @@ -351,7 +351,7 @@ func (m RestoreExecutionModel) View() string { s.WriteString(fmt.Sprintf("\nElapsed Time: %s\n", formatDuration(m.elapsed))) s.WriteString("\n") - s.WriteString(infoStyle.Render("⌨️ Press Enter to continue")) + s.WriteString(infoStyle.Render("[KEYS] Press Enter to continue")) } else { // Show progress s.WriteString(fmt.Sprintf("Phase: %s\n", m.phase)) @@ -373,7 +373,7 @@ func (m RestoreExecutionModel) View() string { // Elapsed time s.WriteString(fmt.Sprintf("Elapsed: %s\n", formatDuration(m.elapsed))) s.WriteString("\n") - s.WriteString(infoStyle.Render("⌨️ Press Ctrl+C to cancel")) + s.WriteString(infoStyle.Render("[KEYS] Press Ctrl+C to cancel")) } return s.String() diff --git a/internal/tui/restore_preview.go b/internal/tui/restore_preview.go index f4075ca..af4ed30 100755 --- a/internal/tui/restore_preview.go +++ b/internal/tui/restore_preview.go @@ -264,7 +264,7 @@ func (m RestorePreviewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Toggle cluster cleanup m.cleanClusterFirst = !m.cleanClusterFirst if m.cleanClusterFirst { - m.message = checkWarningStyle.Render(fmt.Sprintf("⚠️ 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 { m.message = fmt.Sprintf("Clean cluster first: disabled") } @@ -278,7 +278,7 @@ func (m RestorePreviewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Toggle debug log saving m.saveDebugLog = !m.saveDebugLog if m.saveDebugLog { - m.message = infoStyle.Render("📋 Debug log: enabled (will save detailed report on failure)") + m.message = infoStyle.Render("[DEBUG] Debug log: enabled (will save detailed report on failure)") } else { m.message = "Debug log: disabled" } @@ -288,7 +288,7 @@ func (m RestorePreviewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.workDir == "" { // Set to backup directory as default alternative m.workDir = m.config.BackupDir - m.message = infoStyle.Render(fmt.Sprintf("📁 Work directory set to: %s", m.workDir)) + m.message = infoStyle.Render(fmt.Sprintf("[DIR] Work directory set to: %s", m.workDir)) } else { // Clear work directory (use system temp) m.workDir = "" @@ -302,7 +302,7 @@ func (m RestorePreviewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } if !m.canProceed { - m.message = errorStyle.Render("❌ Cannot proceed - critical safety checks failed") + m.message = errorStyle.Render("[FAIL] Cannot proceed - critical safety checks failed") return m, nil } @@ -319,15 +319,15 @@ func (m RestorePreviewModel) View() string { var s strings.Builder // Title - title := "🔍 Restore Preview" + title := "Restore Preview" if m.mode == "restore-cluster" { - title = "🔍 Cluster Restore Preview" + title = "Cluster Restore Preview" } s.WriteString(titleStyle.Render(title)) s.WriteString("\n\n") // Archive Information - s.WriteString(archiveHeaderStyle.Render("📦 Archive Information")) + s.WriteString(archiveHeaderStyle.Render("[ARCHIVE] Information")) s.WriteString("\n") s.WriteString(fmt.Sprintf(" File: %s\n", m.archive.Name)) s.WriteString(fmt.Sprintf(" Format: %s\n", m.archive.Format.String())) @@ -340,25 +340,25 @@ func (m RestorePreviewModel) View() string { // Target Information if m.mode == "restore-single" { - s.WriteString(archiveHeaderStyle.Render("🎯 Target Information")) + s.WriteString(archiveHeaderStyle.Render("[TARGET] Information")) s.WriteString("\n") s.WriteString(fmt.Sprintf(" Database: %s\n", m.targetDB)) s.WriteString(fmt.Sprintf(" Host: %s:%d\n", m.config.Host, m.config.Port)) - cleanIcon := "✗" + cleanIcon := "[N]" if m.cleanFirst { - cleanIcon = "✓" + cleanIcon = "[Y]" } s.WriteString(fmt.Sprintf(" Clean First: %s %v\n", cleanIcon, m.cleanFirst)) - createIcon := "✗" + createIcon := "[N]" if m.createIfMissing { - createIcon = "✓" + createIcon = "[Y]" } s.WriteString(fmt.Sprintf(" Create If Missing: %s %v\n", createIcon, m.createIfMissing)) s.WriteString("\n") } else if m.mode == "restore-cluster" { - s.WriteString(archiveHeaderStyle.Render("🎯 Cluster Restore Options")) + s.WriteString(archiveHeaderStyle.Render("[CLUSTER] Restore Options")) s.WriteString("\n") s.WriteString(fmt.Sprintf(" Host: %s:%d\n", m.config.Host, m.config.Port)) @@ -376,10 +376,10 @@ func (m RestorePreviewModel) View() string { s.WriteString(fmt.Sprintf(" - %s\n", db)) } - cleanIcon := "✗" + cleanIcon := "[N]" cleanStyle := infoStyle if m.cleanClusterFirst { - cleanIcon = "✓" + cleanIcon = "[Y]" cleanStyle = checkWarningStyle } s.WriteString(cleanStyle.Render(fmt.Sprintf(" Clean All First: %s %v (press 'c' to toggle)\n", cleanIcon, m.cleanClusterFirst))) @@ -390,7 +390,7 @@ func (m RestorePreviewModel) View() string { } // Safety Checks - s.WriteString(archiveHeaderStyle.Render("🛡️ Safety Checks")) + s.WriteString(archiveHeaderStyle.Render("[SAFETY] Checks")) s.WriteString("\n") if m.checking { @@ -398,21 +398,21 @@ func (m RestorePreviewModel) View() string { s.WriteString("\n") } else { for _, check := range m.safetyChecks { - icon := "○" + icon := "[ ]" style := checkPendingStyle switch check.Status { case "passed": - icon = "✓" + icon = "[+]" style = checkPassedStyle case "failed": - icon = "✗" + icon = "[-]" style = checkFailedStyle case "warning": - icon = "⚠" + icon = "[!]" style = checkWarningStyle case "checking": - icon = "⟳" + icon = "[~]" style = checkPendingStyle } @@ -428,13 +428,13 @@ func (m RestorePreviewModel) View() string { // Warnings if m.cleanFirst { - s.WriteString(checkWarningStyle.Render("⚠️ Warning: Clean-first enabled")) + s.WriteString(checkWarningStyle.Render("[WARN] Warning: Clean-first enabled")) s.WriteString("\n") s.WriteString(infoStyle.Render(" All existing data in target database will be dropped!")) s.WriteString("\n\n") } if m.cleanClusterFirst && m.existingDBCount > 0 { - s.WriteString(checkWarningStyle.Render("🔥 WARNING: Cluster cleanup enabled")) + s.WriteString(checkWarningStyle.Render("[DANGER] WARNING: Cluster cleanup enabled")) s.WriteString("\n") s.WriteString(checkWarningStyle.Render(fmt.Sprintf(" %d existing database(s) will be DROPPED before restore!", m.existingDBCount))) s.WriteString("\n") @@ -443,30 +443,30 @@ func (m RestorePreviewModel) View() string { } // Advanced Options - s.WriteString(archiveHeaderStyle.Render("⚙️ Advanced Options")) + s.WriteString(archiveHeaderStyle.Render("[OPTIONS] Advanced")) s.WriteString("\n") // Work directory option - workDirIcon := "✗" + workDirIcon := "[-]" workDirStyle := infoStyle workDirValue := "(system temp)" if m.workDir != "" { - workDirIcon = "✓" + workDirIcon = "[+]" workDirStyle = checkPassedStyle workDirValue = m.workDir } s.WriteString(workDirStyle.Render(fmt.Sprintf(" %s Work Dir: %s (press 'w' to toggle)", workDirIcon, workDirValue))) s.WriteString("\n") if m.workDir == "" { - s.WriteString(infoStyle.Render(" ⚠️ Large archives need more space than /tmp may have")) + s.WriteString(infoStyle.Render(" [WARN] Large archives need more space than /tmp may have")) s.WriteString("\n") } // Debug log option - debugIcon := "✗" + debugIcon := "[-]" debugStyle := infoStyle if m.saveDebugLog { - debugIcon = "✓" + debugIcon = "[+]" debugStyle = checkPassedStyle } s.WriteString(debugStyle.Render(fmt.Sprintf(" %s Debug Log: %v (press 'd' to toggle)", debugIcon, m.saveDebugLog))) @@ -485,25 +485,25 @@ func (m RestorePreviewModel) View() string { // Footer if m.checking { - s.WriteString(infoStyle.Render("⌨️ Please wait...")) + s.WriteString(infoStyle.Render("Please wait...")) } else if m.canProceed { - s.WriteString(successStyle.Render("✅ Ready to restore")) + s.WriteString(successStyle.Render("[OK] Ready to restore")) s.WriteString("\n") if m.mode == "restore-single" { - s.WriteString(infoStyle.Render("⌨️ t: Clean-first | c: Create | w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel")) + s.WriteString(infoStyle.Render("t: Clean-first | c: Create | w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel")) } else if m.mode == "restore-cluster" { if m.existingDBCount > 0 { - s.WriteString(infoStyle.Render("⌨️ c: Cleanup | w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel")) + s.WriteString(infoStyle.Render("c: Cleanup | w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel")) } else { - s.WriteString(infoStyle.Render("⌨️ w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel")) + s.WriteString(infoStyle.Render("w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel")) } } else { - s.WriteString(infoStyle.Render("⌨️ w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel")) + s.WriteString(infoStyle.Render("w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel")) } } else { - s.WriteString(errorStyle.Render("❌ Cannot proceed - please fix errors above")) + s.WriteString(errorStyle.Render("[FAIL] Cannot proceed - please fix errors above")) s.WriteString("\n") - s.WriteString(infoStyle.Render("⌨️ Esc: Go back")) + s.WriteString(infoStyle.Render("Esc: Go back")) } return s.String() diff --git a/internal/tui/settings.go b/internal/tui/settings.go index 0a32023..9e2af10 100755 --- a/internal/tui/settings.go +++ b/internal/tui/settings.go @@ -459,9 +459,9 @@ func (m SettingsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.cursor < len(m.settings) { setting := m.settings[m.cursor] if err := setting.Update(m.config, selectedPath); err != nil { - m.message = "❌ Error: " + err.Error() + m.message = "[FAIL] Error: " + err.Error() } else { - m.message = "✅ Directory updated: " + selectedPath + m.message = "[OK] Directory updated: " + selectedPath } } m.browsingDir = false @@ -500,9 +500,9 @@ func (m SettingsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { currentSetting := m.settings[m.cursor] if currentSetting.Type == "selector" { if err := currentSetting.Update(m.config, ""); err != nil { - m.message = errorStyle.Render(fmt.Sprintf("❌ %s", err.Error())) + m.message = errorStyle.Render(fmt.Sprintf("[FAIL] %s", err.Error())) } else { - m.message = successStyle.Render(fmt.Sprintf("✅ Updated %s", currentSetting.DisplayName)) + m.message = successStyle.Render(fmt.Sprintf("[OK] Updated %s", currentSetting.DisplayName)) } return m, nil } @@ -515,11 +515,11 @@ func (m SettingsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.settings[m.cursor].Type == "path" { return m.openDirectoryBrowser() } else { - m.message = "❌ Tab key only works on directory path fields" + m.message = "[FAIL] Tab key only works on directory path fields" return m, nil } } else { - m.message = "❌ Invalid selection" + m.message = "[FAIL] Invalid selection" return m, nil } @@ -597,18 +597,18 @@ func (m SettingsModel) saveEditedValue() (tea.Model, tea.Cmd) { } if setting == nil { - m.message = errorStyle.Render("❌ Setting not found") + m.message = errorStyle.Render("[FAIL] Setting not found") m.editing = false return m, nil } // Update the configuration if err := setting.Update(m.config, m.editingValue); err != nil { - m.message = errorStyle.Render(fmt.Sprintf("❌ %s", err.Error())) + m.message = errorStyle.Render(fmt.Sprintf("[FAIL] %s", err.Error())) return m, nil } - m.message = successStyle.Render(fmt.Sprintf("✅ Updated %s", setting.DisplayName)) + m.message = successStyle.Render(fmt.Sprintf("[OK] Updated %s", setting.DisplayName)) m.editing = false m.editingField = "" m.editingValue = "" @@ -628,7 +628,7 @@ func (m SettingsModel) resetToDefaults() (tea.Model, tea.Cmd) { newConfig.DatabaseType = m.config.DatabaseType *m.config = *newConfig - m.message = successStyle.Render("✅ Settings reset to defaults") + m.message = successStyle.Render("[OK] Settings reset to defaults") return m, nil } @@ -636,19 +636,19 @@ func (m SettingsModel) resetToDefaults() (tea.Model, tea.Cmd) { // saveSettings validates and saves current settings func (m SettingsModel) saveSettings() (tea.Model, tea.Cmd) { if err := m.config.Validate(); err != nil { - m.message = errorStyle.Render(fmt.Sprintf("❌ Validation failed: %s", err.Error())) + m.message = errorStyle.Render(fmt.Sprintf("[FAIL] Validation failed: %s", err.Error())) return m, nil } // Optimize CPU settings if auto-detect is enabled if m.config.AutoDetectCores { if err := m.config.OptimizeForCPU(); err != nil { - m.message = errorStyle.Render(fmt.Sprintf("❌ CPU optimization failed: %s", err.Error())) + m.message = errorStyle.Render(fmt.Sprintf("[FAIL] CPU optimization failed: %s", err.Error())) return m, nil } } - m.message = successStyle.Render("✅ Settings validated and saved") + m.message = successStyle.Render("[OK] Settings validated and saved") return m, nil } @@ -671,11 +671,11 @@ func (m SettingsModel) cycleDatabaseType() (tea.Model, tea.Cmd) { // Update config if err := m.config.SetDatabaseType(newType); err != nil { - m.message = errorStyle.Render(fmt.Sprintf("❌ Failed to set database type: %s", err.Error())) + m.message = errorStyle.Render(fmt.Sprintf("[FAIL] Failed to set database type: %s", err.Error())) return m, nil } - m.message = successStyle.Render(fmt.Sprintf("✅ Database type set to %s", m.config.DisplayDatabaseType())) + m.message = successStyle.Render(fmt.Sprintf("[OK] Database type set to %s", m.config.DisplayDatabaseType())) return m, nil } @@ -688,7 +688,7 @@ func (m SettingsModel) View() string { var b strings.Builder // Header - header := titleStyle.Render("⚙️ Configuration Settings") + header := titleStyle.Render("[CFG] Configuration Settings") b.WriteString(fmt.Sprintf("\n%s\n\n", header)) // Settings list @@ -710,7 +710,7 @@ func (m SettingsModel) View() string { } line := fmt.Sprintf("%s %s: %s", cursor, setting.DisplayName, editValue) b.WriteString(selectedStyle.Render(line)) - b.WriteString(" ✏️") + b.WriteString(" [EDIT]") } else { line := fmt.Sprintf("%s %s: %s", cursor, setting.DisplayName, displayValue) b.WriteString(selectedStyle.Render(line)) @@ -747,7 +747,7 @@ func (m SettingsModel) View() string { // Current configuration summary if !m.editing { b.WriteString("\n") - b.WriteString(infoStyle.Render("📋 Current Configuration:")) + b.WriteString(infoStyle.Render("[LOG] Current Configuration:")) b.WriteString("\n") summary := []string{ @@ -775,16 +775,16 @@ func (m SettingsModel) View() string { // Footer with instructions var footer string if m.editing { - footer = infoStyle.Render("\n⌨️ Type new value • Enter to save • Esc to cancel") + footer = infoStyle.Render("\n[KEYS] Type new value | Enter to save | Esc to cancel") } else { if m.browsingDir { - footer = infoStyle.Render("\n⌨️ ↑/↓ navigate directories • Enter open • Space select • Tab/Esc back to settings") + footer = infoStyle.Render("\n[KEYS] Up/Down navigate directories | Enter open | Space select | Tab/Esc back to settings") } else { // Show different help based on current selection if m.cursor >= 0 && m.cursor < len(m.settings) && m.settings[m.cursor].Type == "path" { - footer = infoStyle.Render("\n⌨️ ↑/↓ navigate • Enter edit • Tab browse directories • 's' save • 'r' reset • 'q' menu") + footer = infoStyle.Render("\n[KEYS] Up/Down navigate | Enter edit | Tab browse directories | 's' save | 'r' reset | 'q' menu") } else { - footer = infoStyle.Render("\n⌨️ ↑/↓ navigate • Enter edit • 's' save • 'r' reset • 'q' menu • Tab=dirs on path fields only") + footer = infoStyle.Render("\n[KEYS] Up/Down navigate | Enter edit | 's' save | 'r' reset | 'q' menu | Tab=dirs on path fields only") } } } diff --git a/internal/tui/status.go b/internal/tui/status.go index aa479b3..c46790d 100755 --- a/internal/tui/status.go +++ b/internal/tui/status.go @@ -160,25 +160,25 @@ func (m StatusViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m StatusViewModel) View() string { var s strings.Builder - header := titleStyle.Render("📊 Database Status & Health Check") + header := titleStyle.Render("[STATS] Database Status & Health Check") s.WriteString(fmt.Sprintf("\n%s\n\n", header)) if m.loading { - spinner := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} + spinner := []string{"-", "\\", "|", "/"} frame := int(time.Now().UnixMilli()/100) % len(spinner) s.WriteString(fmt.Sprintf("%s Loading status information...\n", spinner[frame])) return s.String() } if m.err != nil { - s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Error: %v\n", m.err))) + s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Error: %v\n", m.err))) s.WriteString("\n") } else { s.WriteString("Connection Status:\n") if m.connected { - s.WriteString(successStyle.Render(" ✓ Connected\n")) + s.WriteString(successStyle.Render(" [+] Connected\n")) } else { - s.WriteString(errorStyle.Render(" ✗ Disconnected\n")) + s.WriteString(errorStyle.Render(" [-] Disconnected\n")) } s.WriteString("\n") @@ -193,9 +193,9 @@ func (m StatusViewModel) View() string { } s.WriteString("\n") - s.WriteString(successStyle.Render("✓ All systems operational\n")) + s.WriteString(successStyle.Render("[+] All systems operational\n")) } - s.WriteString("\n⌨️ Press any key to return to menu\n") + s.WriteString("\n[KEYS] Press any key to return to menu\n") return s.String() } diff --git a/internal/wal/pitr_config.go b/internal/wal/pitr_config.go index 1046de6..20e2bad 100644 --- a/internal/wal/pitr_config.go +++ b/internal/wal/pitr_config.go @@ -99,8 +99,8 @@ func (pm *PITRManager) EnablePITR(ctx context.Context, archiveDir string) error return fmt.Errorf("failed to update postgresql.conf: %w", err) } - pm.log.Info("✅ PITR configuration updated successfully") - pm.log.Warn("⚠️ PostgreSQL restart required for changes to take effect") + pm.log.Info("[OK] PITR configuration updated successfully") + pm.log.Warn("[WARN] PostgreSQL restart required for changes to take effect") pm.log.Info("To restart PostgreSQL:") pm.log.Info(" sudo systemctl restart postgresql") pm.log.Info(" OR: sudo pg_ctlcluster restart") @@ -132,8 +132,8 @@ func (pm *PITRManager) DisablePITR(ctx context.Context) error { return fmt.Errorf("failed to update postgresql.conf: %w", err) } - pm.log.Info("✅ PITR disabled successfully") - pm.log.Warn("⚠️ PostgreSQL restart required") + pm.log.Info("[OK] PITR disabled successfully") + pm.log.Warn("[WARN] PostgreSQL restart required") return nil } diff --git a/internal/wal/timeline.go b/internal/wal/timeline.go index f0d6f83..3d91ca0 100644 --- a/internal/wal/timeline.go +++ b/internal/wal/timeline.go @@ -361,7 +361,7 @@ func (tm *TimelineManager) FormatTimelineTree(history *TimelineHistory) string { var sb strings.Builder sb.WriteString("Timeline Branching Structure:\n") - sb.WriteString("═════════════════════════════\n\n") + sb.WriteString("=============================\n\n") // Build tree recursively tm.formatTimelineNode(&sb, history, 1, 0, "") @@ -378,9 +378,9 @@ func (tm *TimelineManager) formatTimelineNode(sb *strings.Builder, history *Time // Format current node indent := strings.Repeat(" ", depth) - marker := "├─" + marker := "+-" if depth == 0 { - marker = "●" + marker = "*" } sb.WriteString(fmt.Sprintf("%s%s Timeline %d", indent, marker, tl.TimelineID)) diff --git a/main.go b/main.go index 24dc2ca..e0d682a 100755 --- a/main.go +++ b/main.go @@ -52,7 +52,7 @@ func main() { if metrics.GlobalMetrics != nil { avgs := metrics.GlobalMetrics.GetAverages() if ops, ok := avgs["total_operations"].(int); ok && ops > 0 { - fmt.Printf("\n📊 Session Summary: %d operations, %.1f%% success rate\n", + fmt.Printf("\n[INFO] Session Summary: %d operations, %.1f%% success rate\n", ops, avgs["success_rate"]) } } diff --git a/scripts/remove_all_unicode.sh b/scripts/remove_all_unicode.sh new file mode 100755 index 0000000..89f1353 --- /dev/null +++ b/scripts/remove_all_unicode.sh @@ -0,0 +1,171 @@ +#!/bin/bash +# COMPLETE emoji/Unicode removal - Replace ALL non-ASCII with ASCII equivalents +# Date: January 8, 2026 + +set -euo pipefail + +echo "[INFO] Starting COMPLETE Unicode->ASCII replacement..." +echo "" + +# Create backup +BACKUP_DIR="backup_unicode_removal_$(date +%Y%m%d_%H%M%S)" +mkdir -p "$BACKUP_DIR" +echo "[INFO] Creating backup in $BACKUP_DIR..." +find . -name "*.go" -type f -not -path "*/vendor/*" -not -path "*/.git/*" -exec bash -c 'mkdir -p "$1/$(dirname "$2")" && cp "$2" "$1/$2"' -- "$BACKUP_DIR" {} \; +echo "[OK] Backup created" +echo "" + +# Find all affected files +echo "[SEARCH] Finding files with Unicode..." +FILES=$(find . -name "*.go" -type f -not -path "*/vendor/*" -not -path "*/.git/*") + +PROCESSED=0 +TOTAL=$(echo "$FILES" | wc -l) + +for file in $FILES; do + PROCESSED=$((PROCESSED + 1)) + + if ! grep -qP '[\x{80}-\x{FFFF}]' "$file" 2>/dev/null; then + continue + fi + + echo "[$PROCESSED/$TOTAL] Processing: $file" + + # Create temp file for atomic replacements + TMPFILE="${file}.tmp" + cp "$file" "$TMPFILE" + + # Box drawing / decorative (used in TUI borders) + sed -i 's/─/-/g' "$TMPFILE" + sed -i 's/━/-/g' "$TMPFILE" + sed -i 's/│/|/g' "$TMPFILE" + sed -i 's/║/|/g' "$TMPFILE" + sed -i 's/├/+/g' "$TMPFILE" + sed -i 's/└/+/g' "$TMPFILE" + sed -i 's/╔/+/g' "$TMPFILE" + sed -i 's/╗/+/g' "$TMPFILE" + sed -i 's/╚/+/g' "$TMPFILE" + sed -i 's/╝/+/g' "$TMPFILE" + sed -i 's/╠/+/g' "$TMPFILE" + sed -i 's/╣/+/g' "$TMPFILE" + sed -i 's/═/=/g' "$TMPFILE" + + # Status symbols + sed -i 's/✅/[OK]/g' "$TMPFILE" + sed -i 's/❌/[FAIL]/g' "$TMPFILE" + sed -i 's/✓/[+]/g' "$TMPFILE" + sed -i 's/✗/[-]/g' "$TMPFILE" + sed -i 's/⚠️/[WARN]/g' "$TMPFILE" + sed -i 's/⚠/[!]/g' "$TMPFILE" + sed -i 's/❓/[?]/g' "$TMPFILE" + + # Arrows + sed -i 's/←//g' "$TMPFILE" + sed -i 's/↑/^/g' "$TMPFILE" + sed -i 's/↓/v/g' "$TMPFILE" + sed -i 's/▲/^/g' "$TMPFILE" + sed -i 's/▼/v/g' "$TMPFILE" + sed -i 's/▶/>/g' "$TMPFILE" + + # Shapes + sed -i 's/●/*\*/g' "$TMPFILE" + sed -i 's/○/o/g' "$TMPFILE" + sed -i 's/⚪/o/g' "$TMPFILE" + sed -i 's/•/-/g' "$TMPFILE" + sed -i 's/█/#/g' "$TMPFILE" + sed -i 's/▎/|/g' "$TMPFILE" + sed -i 's/░/./g' "$TMPFILE" + sed -i 's/➖/-/g' "$TMPFILE" + + # Emojis - Info/Data + sed -i 's/📊/[INFO]/g' "$TMPFILE" + sed -i 's/📋/[LIST]/g' "$TMPFILE" + sed -i 's/📁/[DIR]/g' "$TMPFILE" + sed -i 's/📦/[PKG]/g' "$TMPFILE" + sed -i 's/📜/[LOG]/g' "$TMPFILE" + sed -i 's/📭/[EMPTY]/g' "$TMPFILE" + sed -i 's/📝/[NOTE]/g' "$TMPFILE" + sed -i 's/💡/[TIP]/g' "$TMPFILE" + + # Emojis - Actions/Objects + sed -i 's/🎯/[TARGET]/g' "$TMPFILE" + sed -i 's/🛡️/[SECURE]/g' "$TMPFILE" + sed -i 's/🔒/[LOCK]/g' "$TMPFILE" + sed -i 's/🔓/[UNLOCK]/g' "$TMPFILE" + sed -i 's/🔍/[SEARCH]/g' "$TMPFILE" + sed -i 's/🔀/[SWITCH]/g' "$TMPFILE" + sed -i 's/🔥/[FIRE]/g' "$TMPFILE" + sed -i 's/💾/[SAVE]/g' "$TMPFILE" + sed -i 's/🗄️/[DB]/g' "$TMPFILE" + sed -i 's/🗄/[DB]/g' "$TMPFILE" + + # Emojis - Time/Status + sed -i 's/⏱️/[TIME]/g' "$TMPFILE" + sed -i 's/⏱/[TIME]/g' "$TMPFILE" + sed -i 's/⏳/[WAIT]/g' "$TMPFILE" + sed -i 's/⏪/[REW]/g' "$TMPFILE" + sed -i 's/⏹️/[STOP]/g' "$TMPFILE" + sed -i 's/⏹/[STOP]/g' "$TMPFILE" + sed -i 's/⟳/[SYNC]/g' "$TMPFILE" + + # Emojis - Cloud + sed -i 's/☁️/[CLOUD]/g' "$TMPFILE" + sed -i 's/☁/[CLOUD]/g' "$TMPFILE" + sed -i 's/📤/[UPLOAD]/g' "$TMPFILE" + sed -i 's/📥/[DOWNLOAD]/g' "$TMPFILE" + sed -i 's/🗑️/[DELETE]/g' "$TMPFILE" + + # Emojis - Misc + sed -i 's/📈/[UP]/g' "$TMPFILE" + sed -i 's/📉/[DOWN]/g' "$TMPFILE" + sed -i 's/⌨️/[KEY]/g' "$TMPFILE" + sed -i 's/⌨/[KEY]/g' "$TMPFILE" + sed -i 's/⚙️/[CONFIG]/g' "$TMPFILE" + sed -i 's/⚙/[CONFIG]/g' "$TMPFILE" + sed -i 's/✏️/[EDIT]/g' "$TMPFILE" + sed -i 's/✏/[EDIT]/g' "$TMPFILE" + sed -i 's/⚡/[FAST]/g' "$TMPFILE" + + # Spinner characters (braille patterns for loading animations) + sed -i 's/⠋/|/g' "$TMPFILE" + sed -i 's/⠙/\//g' "$TMPFILE" + sed -i 's/⠹/-/g' "$TMPFILE" + sed -i 's/⠸/\\/g' "$TMPFILE" + sed -i 's/⠼/|/g' "$TMPFILE" + sed -i 's/⠴/\//g' "$TMPFILE" + sed -i 's/⠦/-/g' "$TMPFILE" + sed -i 's/⠧/\\/g' "$TMPFILE" + sed -i 's/⠇/|/g' "$TMPFILE" + sed -i 's/⠏/\//g' "$TMPFILE" + + # Move temp file over original + mv "$TMPFILE" "$file" +done + +echo "" +echo "[OK] Replacement complete!" +echo "" + +# Verify +REMAINING=$(grep -roP '[\x{80}-\x{FFFF}]' --include="*.go" . 2>/dev/null | wc -l || echo "0") + +echo "[INFO] Unicode characters remaining: $REMAINING" +if [ "$REMAINING" -gt 0 ]; then + echo "[WARN] Some Unicode still exists (might be in comments or safe locations)" + echo "[INFO] Unique remaining characters:" + grep -roP '[\x{80}-\x{FFFF}]' --include="*.go" . 2>/dev/null | grep -oP '[\x{80}-\x{FFFF}]' | sort -u | head -20 +else + echo "[OK] All Unicode characters replaced with ASCII!" +fi + +echo "" +echo "[INFO] Backup: $BACKUP_DIR" +echo "[INFO] To restore: cp -r $BACKUP_DIR/* ." +echo "" +echo "[INFO] Next steps:" +echo " 1. go build" +echo " 2. go test ./..." +echo " 3. Test TUI: ./dbbackup" +echo " 4. Commit: git add . && git commit -m 'v3.42.11: Replace all Unicode with ASCII'" +echo "" diff --git a/scripts/remove_emojis.sh b/scripts/remove_emojis.sh new file mode 100755 index 0000000..afffda4 --- /dev/null +++ b/scripts/remove_emojis.sh @@ -0,0 +1,130 @@ +#!/bin/bash +# Remove ALL emojis/unicode symbols from Go code and replace with ASCII +# Date: January 8, 2026 +# Issue: 638 lines contain Unicode emojis causing display issues + +set -euo pipefail + +echo "[INFO] Starting emoji removal process..." +echo "" + +# Find all Go files with emojis (expanded emoji list) +echo "[SEARCH] Finding affected files..." +FILES=$(find . -name "*.go" -type f -not -path "*/vendor/*" -not -path "*/.git/*" | xargs grep -l -P '[\x{1F000}-\x{1FFFF}]|[\x{2300}-\x{27BF}]|[\x{2600}-\x{26FF}]' 2>/dev/null || true) + +if [ -z "$FILES" ]; then + echo "[WARN] No files with emojis found!" + exit 0 +fi + +FILECOUNT=$(echo "$FILES" | wc -l) +echo "[INFO] Found $FILECOUNT files containing emojis" +echo "" + +# Count total emojis before +BEFORE=$(find . -name "*.go" -type f -not -path "*/vendor/*" | xargs grep -oP '[\x{1F000}-\x{1FFFF}]|[\x{2300}-\x{27BF}]|[\x{2600}-\x{26FF}]' 2>/dev/null | wc -l || echo "0") +echo "[INFO] Total emojis found: $BEFORE" +echo "" + +# Create backup +BACKUP_DIR="backup_before_emoji_removal_$(date +%Y%m%d_%H%M%S)" +mkdir -p "$BACKUP_DIR" +echo "[INFO] Creating backup in $BACKUP_DIR..." +for file in $FILES; do + mkdir -p "$BACKUP_DIR/$(dirname "$file")" + cp "$file" "$BACKUP_DIR/$file" +done +echo "[OK] Backup created" +echo "" + +# Process each file +echo "[INFO] Replacing emojis with ASCII equivalents..." +PROCESSED=0 + +for file in $FILES; do + PROCESSED=$((PROCESSED + 1)) + echo "[$PROCESSED/$FILECOUNT] Processing: $file" + + # Create temp file + TMPFILE="${file}.tmp" + + # Status indicators + sed 's/✅/[OK]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/❌/[FAIL]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/✓/[+]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/✗/[-]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + + # Warning symbols (⚠️ has variant selector, handle both) + sed 's/⚠️/[WARN]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/⚠/[!]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + + # Info/Data symbols + sed 's/📊/[INFO]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/📋/[LIST]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/📁/[DIR]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/📦/[PKG]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + + # Target/Security + sed 's/🎯/[TARGET]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/🛡️/[SECURE]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/🔒/[LOCK]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/🔓/[UNLOCK]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + + # Actions + sed 's/🔍/[SEARCH]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/⏱️/[TIME]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + + # Cloud operations (☁️ has variant selector, handle both) + sed 's/☁️/[CLOUD]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/☁/[CLOUD]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/📤/[UPLOAD]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/📥/[DOWNLOAD]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/🗑️/[DELETE]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + + # Other + sed 's/📈/[UP]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/📉/[DOWN]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + + # Additional emojis found + sed 's/⌨️/[KEY]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/⌨/[KEY]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/🗄️/[DB]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/🗄/[DB]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/⚙️/[CONFIG]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/⚙/[CONFIG]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/✏️/[EDIT]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" + sed 's/✏/[EDIT]/g' "$file" > "$TMPFILE" && mv "$TMPFILE" "$file" +done + +echo "" +echo "[OK] Replacement complete!" +echo "" + +# Count remaining emojis +AFTER=$(find . -name "*.go" -type f -not -path "*/vendor/*" | xargs grep -oP '[\x{1F000}-\x{1FFFF}]|[\x{2300}-\x{27BF}]|[\x{2600}-\x{26FF}]' 2>/dev/null | wc -l || echo "0") + +echo "[INFO] Emojis before: $BEFORE" +echo "[INFO] Emojis after: $AFTER" +echo "[INFO] Emojis removed: $((BEFORE - AFTER))" +echo "" + +if [ "$AFTER" -gt 0 ]; then + echo "[WARN] $AFTER emojis still remaining!" + echo "[INFO] Listing remaining emojis:" + find . -name "*.go" -type f -not -path "*/vendor/*" | xargs grep -nP '[\x{1F000}-\x{1FFFF}]|[\x{2300}-\x{27BF}]|[\x{2600}-\x{26FF}]' 2>/dev/null | head -20 +else + echo "[OK] All emojis successfully removed!" +fi + +echo "" +echo "[INFO] Backup location: $BACKUP_DIR" +echo "[INFO] To restore: cp -r $BACKUP_DIR/* ." +echo "" +echo "[INFO] Next steps:" +echo " 1. Build: go build" +echo " 2. Test: go test ./..." +echo " 3. Manual testing: ./dbbackup status" +echo " 4. If OK, commit: git add . && git commit -m 'Replace emojis with ASCII'" +echo " 5. If broken, restore: cp -r $BACKUP_DIR/* ." +echo "" +echo "[OK] Emoji removal script completed!"
Database:{{.Database}}
Hostname:{{.Hostname}}