diff --git a/internal/cli/snapshot.go b/internal/cli/snapshot.go index 013a039..36b835c 100644 --- a/internal/cli/snapshot.go +++ b/internal/cli/snapshot.go @@ -487,6 +487,12 @@ func newSnapshotVerifyCommand() *cobra.Command { // List lists all snapshots func (app *SnapshotApp) List(ctx context.Context, jsonOutput bool) error { + // First, sync with remote snapshots + if err := app.syncWithRemote(ctx); err != nil { + return fmt.Errorf("syncing with remote: %w", err) + } + + // Now get snapshots from S3 snapshots, err := app.getSnapshots(ctx) if err != nil { return err @@ -774,6 +780,54 @@ func (app *SnapshotApp) deleteSnapshot(ctx context.Context, snapshotID string) e return nil } +// syncWithRemote syncs local database with remote snapshots +func (app *SnapshotApp) syncWithRemote(ctx context.Context) error { + log.Info("Syncing with remote snapshots") + + // Get all remote snapshot IDs + remoteSnapshots := make(map[string]bool) + objectCh := app.S3Client.ListObjectsStream(ctx, "metadata/", false) + + for object := range objectCh { + if object.Err != nil { + return fmt.Errorf("listing remote snapshots: %w", object.Err) + } + + // Extract snapshot ID from paths like metadata/hostname-20240115-143052Z/ + parts := strings.Split(object.Key, "/") + if len(parts) >= 2 && parts[0] == "metadata" && parts[1] != "" { + remoteSnapshots[parts[1]] = true + } + } + + log.Debug("Found remote snapshots", "count", len(remoteSnapshots)) + + // Get all local snapshots (use a high limit to get all) + localSnapshots, err := app.Repositories.Snapshots.ListRecent(ctx, 10000) + if err != nil { + return fmt.Errorf("listing local snapshots: %w", err) + } + + // Remove local snapshots that don't exist remotely + removedCount := 0 + for _, snapshot := range localSnapshots { + if !remoteSnapshots[snapshot.ID] { + log.Info("Removing local snapshot not found in remote", "snapshot_id", snapshot.ID) + if err := app.Repositories.Snapshots.Delete(ctx, snapshot.ID); err != nil { + log.Error("Failed to delete local snapshot", "snapshot_id", snapshot.ID, "error", err) + } else { + removedCount++ + } + } + } + + if removedCount > 0 { + log.Info("Removed local snapshots not found in remote", "count", removedCount) + } + + return nil +} + // parseSnapshotTimestamp extracts timestamp from snapshot ID // Format: hostname-20240115-143052Z func parseSnapshotTimestamp(snapshotID string) (time.Time, error) {