refactor: retire the images tarball backup
CI and images / lint (push) Successful in 2s
CI and images / extension-version (push) Successful in 2s
CI and images / extension-test (push) Successful in 20s
CI and images / frontend-build (push) Successful in 25s
CI and images / backend-lint-and-test (push) Successful in 50s
CI and images / integration (push) Successful in 2m38s
CI and images / build-agent (push) Successful in 5s
CI and images / sign-extension (push) Successful in 8s
CI and images / build-web (push) Successful in 4m10s
CI and images / smoke-web (push) Successful in 1m3s
CI and images / promote (push) Successful in 1s
CI and images / refresh-dev (push) Skipped
CI and images / unattended-failure (push) Skipped
CI and images / lint (push) Successful in 2s
CI and images / extension-version (push) Successful in 2s
CI and images / extension-test (push) Successful in 20s
CI and images / frontend-build (push) Successful in 25s
CI and images / backend-lint-and-test (push) Successful in 50s
CI and images / integration (push) Successful in 2m38s
CI and images / build-agent (push) Successful in 5s
CI and images / sign-extension (push) Successful in 8s
CI and images / build-web (push) Successful in 4m10s
CI and images / smoke-web (push) Successful in 1m3s
CI and images / promote (push) Successful in 1s
CI and images / refresh-dev (push) Skipped
CI and images / unattended-failure (push) Skipped
Images and attachments are backed up off-site, one object per file, so the tar+zstd of /images goes: its service and restore, both tasks, the trigger and restore routes, the Keep last setting, the screen's Images section, and the tests. Migration 0122 drops backup_run.tar_path and import_settings.backup_images_keep_last_n and deletes any images runs. The local database dump stays for an install with no off-site destination, and the Backups card now says plainly that images and attachments are backed up only off-site. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -58,7 +58,7 @@ import { computed, ref, watch } from 'vue'
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, required: true },
|
||||
action: { type: String, required: true }, // 'restore' | 'delete'
|
||||
kind: { type: String, required: true }, // 'db' | 'images' | 'artist' | 'tag' | 'images-selection' | 'audit' | 'min-dim'
|
||||
kind: { type: String, required: true }, // 'db' | 'offsite' | 'artist' | 'tag' | 'images-selection' | 'audit' | 'min-dim'
|
||||
runId: { type: [Number, String], default: '' }, // numeric id or sha8 string
|
||||
description: { type: String, default: '' },
|
||||
tier: { type: String, default: 'C' }, // 'B' | 'C'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<MaintenanceTile
|
||||
icon="mdi-database-export"
|
||||
title="Backups"
|
||||
blurb="Database + image backups, schedules and restore."
|
||||
blurb="Database and library backups, schedules and restore."
|
||||
>
|
||||
<p v-if="offsite" class="fc-muted text-body-2 mb-4">
|
||||
Database dumps stream to the <strong>{{ offsite.bucket }}</strong> bucket
|
||||
@@ -11,11 +11,10 @@
|
||||
bucket's <code>media/</code> folder, below.
|
||||
</p>
|
||||
<p v-else class="fc-muted text-body-2 mb-4">
|
||||
pg_dump for the database (fast, nightly-schedulable);
|
||||
tar+zstd for the images (slow, manual only). Files live in
|
||||
<code>/images/_backups/</code>. Tag a backup to protect it
|
||||
from autoprune. Set up an off-site destination above to send
|
||||
database dumps off this server instead.
|
||||
Database dumps are written to <code>/images/_backups/</code> on this
|
||||
server; tag one to keep it past Keep last. Images and attachments are
|
||||
backed up only to an off-site destination: set one up above to back up
|
||||
the whole library, database included, off this server.
|
||||
</p>
|
||||
|
||||
<v-alert
|
||||
@@ -171,34 +170,6 @@
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<!-- Images section -->
|
||||
<h3 class="fc-section-title">Images</h3>
|
||||
<div class="fc-settings-row mb-3">
|
||||
<v-text-field
|
||||
:model-value="settings.backup_images_keep_last_n"
|
||||
label="Keep last" type="number" min="1" max="100"
|
||||
density="compact" hide-details style="max-width: 110px;"
|
||||
@update:model-value="onSettingChange('backup_images_keep_last_n', Number($event))"
|
||||
/>
|
||||
</div>
|
||||
<v-btn
|
||||
color="accent" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-folder-zip" class="mb-3"
|
||||
:loading="imagesTriggering"
|
||||
@click="onTrigger('images')"
|
||||
>Run images backup now</v-btn>
|
||||
<span class="text-caption fc-muted ml-2">
|
||||
ⓘ Hours for a 200GB+ library.
|
||||
</span>
|
||||
|
||||
<BackupRunsTable
|
||||
:runs="store.imagesRuns"
|
||||
@restore="onRestore"
|
||||
@delete="onDelete"
|
||||
@tag="onTag"
|
||||
/>
|
||||
</template>
|
||||
<BackupConfirmModal
|
||||
v-model="confirmOpen"
|
||||
:action="confirmAction"
|
||||
@@ -248,7 +219,6 @@ const settings = computed(() => store.settings || {
|
||||
backup_db_nightly_enabled: false,
|
||||
backup_db_nightly_hour_utc: 3,
|
||||
backup_db_keep_last_n: 14,
|
||||
backup_images_keep_last_n: 3,
|
||||
})
|
||||
|
||||
// The off-site destination, once it is fully set up: database dumps go there.
|
||||
@@ -267,7 +237,6 @@ const runningSent = computed(() => runningMedia.value?.manifest?.sent || 0)
|
||||
|
||||
const mediaTriggering = ref(false)
|
||||
const dbTriggering = ref(false)
|
||||
const imagesTriggering = ref(false)
|
||||
const confirmOpen = ref(false)
|
||||
const confirmAction = ref('restore')
|
||||
const confirmKind = ref('db')
|
||||
@@ -356,8 +325,6 @@ function pollOnce() {
|
||||
store.loadMediaCoverage()
|
||||
store.loadRuns('reconcile')
|
||||
}
|
||||
} else {
|
||||
store.loadRuns('images')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +340,7 @@ onUnmounted(() => {
|
||||
})
|
||||
|
||||
async function onTrigger(kind) {
|
||||
const triggeringRef = { db: dbTriggering, images: imagesTriggering, media: mediaTriggering }[kind]
|
||||
const triggeringRef = { db: dbTriggering, media: mediaTriggering }[kind]
|
||||
triggeringRef.value = true
|
||||
try {
|
||||
await store.triggerBackup(kind)
|
||||
@@ -395,7 +362,7 @@ function _openConfirm(action, run) {
|
||||
confirmToken.value = ''
|
||||
confirmDescription.value = (
|
||||
action === 'restore'
|
||||
? `Source artifact: ${run.kind === 'db' ? run.sql_path : run.tar_path}`
|
||||
? `Source artifact: ${run.sql_path}`
|
||||
: 'Removes the BackupRun row and unlinks the artifact files.'
|
||||
)
|
||||
confirmOpen.value = true
|
||||
|
||||
@@ -7,7 +7,6 @@ export const useBackupStore = defineStore('backup', () => {
|
||||
const api = useApi()
|
||||
|
||||
const dbRuns = ref([])
|
||||
const imagesRuns = ref([])
|
||||
const mediaRuns = ref([])
|
||||
const restoreRuns = ref([])
|
||||
// The weekly check of the bucket against what was sent.
|
||||
@@ -16,12 +15,12 @@ export const useBackupStore = defineStore('backup', () => {
|
||||
const testRuns = ref([])
|
||||
const settings = ref(null)
|
||||
const loading = ref({
|
||||
dbRuns: false, imagesRuns: false, mediaRuns: false, restoreRuns: false,
|
||||
dbRuns: false, mediaRuns: false, restoreRuns: false,
|
||||
reconcileRuns: false, testRuns: false, settings: false,
|
||||
})
|
||||
const lastError = ref(null)
|
||||
const RUNS = {
|
||||
db: dbRuns, images: imagesRuns, media: mediaRuns, restore: restoreRuns,
|
||||
db: dbRuns, media: mediaRuns, restore: restoreRuns,
|
||||
reconcile: reconcileRuns, test: testRuns,
|
||||
}
|
||||
|
||||
@@ -200,7 +199,7 @@ export const useBackupStore = defineStore('backup', () => {
|
||||
}
|
||||
|
||||
return {
|
||||
dbRuns, imagesRuns, mediaRuns, restoreRuns, reconcileRuns, testRuns,
|
||||
dbRuns, mediaRuns, restoreRuns, reconcileRuns, testRuns,
|
||||
mediaCoverage, loadMediaCoverage,
|
||||
listOffsiteDumps, databaseBytes, restoreFromOffsite, fetchMissingFiles, testDump,
|
||||
settings, loading, lastError,
|
||||
|
||||
Reference in New Issue
Block a user