With an off-site destination set up, the library's files are sent to the
bucket's media/ folder as media/ab/cd/<sha256><ext>, beside the database
dumps under database/. Each file goes once: a new backup_blob table records
what each destination holds (migration 0121), and a run's work list is every
image and attachment sha with no row yet, so runs are incremental and a new
bucket starts its copy from the beginning.
- The bucket checks each file against the sha recorded at import; a file
over 1 GiB goes in parts and is checked before the upload is completed.
A file missing or changed on disk is reported, not stored.
- A run is chunked: each 45-minute chunk commits after every file and queues
the next, so other long-maintenance jobs get turns. One run at a time.
The stall sweep judges a media run by its progress heartbeat
(backup_run.last_progress_at), not its start.
- A file that can't be sent is listed on the run (status ok, with the error
line) and tried again by the next run. A refused put for an object
already in an append-only bucket counts as sent.
- Nightly, with a destination set, runs the dump and then the media.
- Weekly reconcile lists media/ and re-sends what the bucket lost, and
adopts what it already holds (a restored database sends nothing twice).
- Uploads use the bandwidth limit; read transactions end before transfers.
- Backups card: with a destination, an "Images and attachments" section
shows coverage (files and bytes in the bucket), "Send new files now", and
the media runs. Without one, the local tarball section is unchanged.
- Media run history keeps the latest 60 rows.
Task #6879, milestone 586.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The nightly database backup (and "Run DB backup now") now sends pg_dump's
output straight to the off-site bucket as a multipart upload, with no copy
staged on this server. The object is named database/fabledcurator-<UTC
time>.dump and appears only once pg_dump has exited cleanly and every part
has landed; a failed dump aborts the upload and records pg_dump's last line.
Without a destination, dumps are written to /images/_backups as before.
- backup_s3: Upload (start / add part / complete / abort), with a busy or
silent bucket rested and asked again (15 s, 60 s, Retry-After obeyed), and
a Pacer that holds uploads to the new bandwidth limit.
- Settings: "Upload limit (Mbit/s)" on the destination card, 200 by default,
0 for none (migration 0120).
- pg_dump runs under nice (and ionice where installed), locally too.
- Backups card says where dumps go; the runs table gains "Saved to" and
shows a failed run's error. Off-site rows have no restore/tag/delete here:
restoring from the bucket is a later step (#6880).
Task #6878, milestone 586.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Goal (operator 2026-06-18): the overview of a Settings tab fits one unscrolled
viewport; expanding a tile to read into it is the only reason to scroll.
- Every Maintenance card converted to the collapsible MaintenanceTile (collapsed
by default = icon + short title + one-line blurb). Task cards (ML backfill,
centroids, thumbnails, archive re-extract, missing-file repair, DB maintenance)
sit in a responsive grid; running tasks auto-expand. Tagging config (suggestion
thresholds, allowlist, aliases) grouped in one Tagging section as collapsible
tiles; Backup is its own collapsible tile.
- Three labeled sections mirror the Cleanup tab: Backfills and reprocessing /
Tagging / Storage.
- Center the whole Settings surface: SettingsView is now a centered, width-capped
(1140px) column so the tab strip and every panel sit in a tidy centered measure
(was full-width). CleanupView drops its own left-aligned max-width to fill it.
All card logic unchanged - only the chrome.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The icon+title v-card-title heading (d-flex align-center + gap + <v-icon size=small> +
<span>) was hand-rolled identically in 13 cards/dialogs (15 heading instances).
Consolidate to <CardHeading icon title> (components/common) with an iconColor
prop (error headings) and a default slot for trailing content (spacer+actions,
inline status chip). Adopted everywhere the pattern appears — all-or-nothing per
the hardened DRY process.
Over-DRY guard: plain text-only <v-card-title> one-liners are NOT this pattern
and stay; DownloadDetailModal leads with a status CHIP (not an icon), a different
concept, left alone. §8b: the only remaining d-flex align-center v-card-title is
that intentional variant. Catalog updated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The muted-text token was redefined identically in 12 component <style scoped>
blocks. Consolidate to one global utility in styles/app.css; remove the 12
copies. Keeps the explicit on-surface-variant (vellum) token, NOT Vuetify's
opacity-based text-medium-emphasis (per the muted-text-token rule). Behavior-
preserving: every class=fc-muted usage now resolves to the single source.
§8b exhaustiveness caught (and I fixed) my own sed clobbering the new app.css
rule — now exactly one .fc-muted definition exists, zero component-local.
Catalog updated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>