feat(snapshots): capture app data-volume snapshots
Build / build (push) Successful in 10m59s

Add per-workload capture of host-bind data volumes as downloadable tar.gz archives: a new internal/volsnap engine (enumerate host-bind volumes via the computeMounts merge, archive with archive/tar+gzip skipping symlinks/special files, per-workload retention + startup orphan cleanup), a volume_snapshots table + store CRUD, admin-gated API (list/snapshotable/create/download/delete), and a Snapshots panel on /apps/[id] that shows coverage and which volumes are skipped (and why). Scope: image-source apps, host-bind scopes (absolute/stage/project); Docker named volumes, tmpfs, and instance scope are surfaced as not-yet-supported. Restore is a separate later phase. Download/FilePath are containment-checked; create returns a typed no-data error (400) vs generic 500. Covered by archiver unit tests + full API e2e.
This commit is contained in:
2026-06-02 14:56:10 +03:00
parent 2ba49b9bb6
commit 6b45ed62bb
16 changed files with 1565 additions and 4 deletions
+27
View File
@@ -1670,6 +1670,33 @@
"confirmDeleteTitle": "Delete notification route?",
"confirmDeleteMessage": "This route will stop firing immediately. The workload's legacy notification URL (if set) will resume catching events when no routes match."
},
"snapshots": {
"title": "Data snapshots",
"sub": "Capture this app's host-bind data volumes as a downloadable archive.",
"loading": "Loading snapshots…",
"empty": "No snapshots yet.",
"coverage": "{count} volume(s) will be captured.",
"noCoverage": "No host-bind volumes to snapshot. Snapshots cover image-based apps with absolute, stage, or project volume scopes.",
"liveWarning": "Snapshots are taken live — stop the app first for a fully consistent copy of databases.",
"skippedTitle": "{count} volume(s) can't be snapshotted yet:",
"labelPlaceholder": "Optional label (e.g. before upgrade)",
"create": "Snapshot now",
"creating": "Capturing…",
"created": "Snapshot created.",
"createFailed": "Failed to create snapshot",
"colLabel": "Label",
"colCreated": "Created",
"colVolumes": "Volumes",
"colSize": "Size",
"unlabeled": "(unlabeled)",
"download": "Download",
"delete": "Delete",
"deleted": "Snapshot deleted.",
"downloadFailed": "Failed to download snapshot",
"deleteFailed": "Failed to delete snapshot",
"confirmDeleteTitle": "Delete snapshot?",
"confirmDeleteMessage": "This permanently deletes the snapshot archive. This cannot be undone."
},
"toolbar": {
"stop": "Stop",
"start": "Start",