A single Start Backup action now captures both the MongoDB database AND the site's S3 objects in one timestamp-aligned operation, surfaced through the existing Backup/Restore admin tab
A single Start Restore action rolls both Mongo and the S3 bucket back to the chosen snapshot, automatically walking cross-environment when the selected backup originated from a different environment
Backup/Restore History gains an S3 Objects column showing per-session copied/skipped counts, bytes transferred, and status
Backup and restore completion emails now include a structured S3 summary table (per-site status, totals, copied/restored and skipped counts, copied and total size) so notifications reflect the full scope of the consolidated operation
S3 backups are incremental after the first run: unchanged objects (matched by ETag against the previous manifest) are recorded as SKIPPED rather than re-copied, dramatically reducing cost and time
Each backup site has its own Manifest History view with Restore, Delete, and per-row status indicators
The Delete button for a historical manifest is automatically disabled with an explanatory tooltip when deleting it would orphan SKIPPED references in any later manifest
Legacy /api/backup route renamed to /api/mongo-backup and new /api/s3-backup introduced, so the two services are peer top-level endpoints rather than nested; frontend service uses matching MONGO_BASE_URL and S3_BASE_URL constants
Technical — new service and data model
Add s3-backup-service with:
Incremental backup (ETag comparison against the previous manifest)
Point-in-time restore that walks back through prior manifests via $elemMatch on key + eTag + action to find the physical source of SKIPPED entries, bounded by timestamp <= snapshot
Cross-site restore via a separate target SiteConfig that supplies destination credentials and bucket
Add s3-backup-controller, s3-backup-routes, and the s3BackupManifest Mongoose model with a (site, timestamp) compound index
Add startS3Backup / startS3Restore with per-site SiteConfig resolution that supports global-credentials inheritance (target env inherits global credentials when per-env credentials absent) and falls back to <bucket>-backups when no global backup bucket
Technical — consolidated flow
Extend BackupAndRestoreService.executeBackup / executeRestore to trigger the S3 phase after Mongo succeeds (honouring options.includeS3, defaulting true) and store the summary on BackupSession as s3Backups / s3Restores
Derive source environment from Mongo backup filenames via extractSourceEnvironmentFromBackupName(name, dbName) which uses the database name as a hint to disambiguate multi-segment environment names and multi-segment databases (e.g. ngx-ramblers)
BackupSession model and Mongoose schema gain s3Backups?, s3Restores? (S3BackupSummary[]) and options.includeS3?
CLI backup / restore commands inherit consolidation automatically because they share BackupAndRestoreService; CLI still exposes a dedicated s3-backup sub-command for S3-only operations
Technical — deletability guard
collectCopiedKeys + countBlockingLaterManifests compute whether deleting a candidate manifest would orphan SKIPPED references in any later manifest on the same site
manifests() and manifest() enrich returned docs with deletable and blockReason
deleteManifests returns {deleted, blocked} and refuses blocked ones
S3BackupManifest model gains runtime-only deletable? and blockReason? fields populated by the enrichment helpers
Technical — notifications
BackupNotificationService renders a structured S3 summary HTML table inside the completion emails for both backup and restore, driven by session.s3Backups / session.s3Restores
New buildS3SummarySection helper keeps the formatting consistent across backup and restore emails
Technical — route and script cleanup
Rename backup-routes.ts to mongo-backup-routes.ts and export mongoBackupRoutes to match the /api/mongo-backup mount; mount new s3BackupRoutes at /api/s3-backup
Delete standalone aws-backup/backup-s3-images.ts script plus its npm script since the new service fully supersedes it
Technical — UI
Admin UI pulls in ngx-bootstrap TooltipDirective and switches the per-manifest Delete button to [disabled] + [tooltip] when blocked, with inline button-swap confirmation (no native confirm() dialogs)
Technical — type safety
Replace all stringly-typed BackupSessionStatus and BackupSessionType comparisons with enum references (BackupSessionStatus.COMPLETED / .FAILED / .IN_PROGRESS / .PENDING and BackupSessionType.BACKUP / .RESTORE) across the service, ws handler, notification builder, and admin component
The admin component exposes both enums as protected readonly members so the template can reference them directly
Remove dry-run persistence bug where manifests were saved to Mongo even when dryRun was true; dry runs now return an in-memory manifest
Technical — tests
8 new unit tests (backup-paths: 6 extractSourceEnvironmentFromBackupName cases including path prefix stripping, multi-segment db, and missing timestamp; s3-backup-service: 2 collectCopiedKeys cases)