copy(backup): user-facing "snapshot" → "backup" across the UI
"Snapshot" is restic's term and leaks the tool's vocabulary into the
WebUI. Users think in "backups" — the on-page label even says "Backups"
already; only the secondary copy still said "snapshot". Renames the
remaining user-visible mentions while leaving code identifiers, API
keys, data attributes, CSS class names, and the ?snapshot= deep-link
param untouched (those are internal contracts and changing them would
churn for no user-visible win).
Renamed surfaces:
- Per-app Backups tab header:
"Snapshots for <app>" → "Backups for <app>"
"across all configured repositories" → "across all configured locations"
- BackupAppCard:
"No snapshots yet" → "No backups yet"
"No snapshots found" → "No backups found"
"Showing the most recent 50 of N snapshots" → "...of N backups"
ID-chip tooltip "Snapshot ID" → "Backup ID"
Detail panel "Snapshot ID:" → "Backup ID:"
- Backup retention preset descriptions (KEEP_LAST/DAILY/WEEKLY/MONTHLY/
YEARLY) — "snapshot per day/week/..." → "backup per day/week/..."
- Personal preset hint: "6 monthly snapshots" → "6 monthly backups"
- Restore confirmation modal hint: "snapshot restored in place" →
"backup restored in place"
- Config-warning banner copy adjusted so it doesn't introduce
"snapshots" as a noun
- Retention "Keep last" input suffix: "snapshots" → "backups"
- Cross-host migrate tooltip: "snapshot" → "backup"
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
e0e4bddd57
commit
989123322b
@ -181,7 +181,7 @@
|
||||
<div class="backup-section" id="backup-section">
|
||||
<div class="backup-title">
|
||||
<h3>💾 Backups</h3>
|
||||
<p>Snapshots for <span id="backup-app-name">this app</span> across all configured repositories.</p>
|
||||
<p>Backups for <span id="backup-app-name">this app</span> across all configured locations.</p>
|
||||
</div>
|
||||
<div class="backup-app-card" id="backup-app-card">
|
||||
<div class="backup-app-card-status" id="backup-app-card-status">Loading…</div>
|
||||
|
||||
@ -134,7 +134,7 @@
|
||||
<section class="backup-tabpanel" id="backup-panel-migrate">
|
||||
<div class="backup-card backup-migrate-card">
|
||||
<div class="backup-card-header">
|
||||
<h2>Cross-host migrate <span class="tooltip" title="Pulls a snapshot taken on another host out of a shared backup location and lays it down here. The destination's existing copy of the app is snapshotted first (rollback safety), then replaced." style="font-size:.75em;opacity:.7;cursor:help">ℹ️</span></h2>
|
||||
<h2>Cross-host migrate <span class="tooltip" title="Pulls a backup taken on another host out of a shared backup location and lays it down here. The destination's existing copy of the app is backed up first (rollback safety), then replaced." style="font-size:.75em;opacity:.7;cursor:help">ℹ️</span></h2>
|
||||
<span class="backup-card-hint">Restore an app from another LibrePortal</span>
|
||||
</div>
|
||||
<div class="backup-migrate-empty" id="backup-migrate-empty" hidden>
|
||||
|
||||
@ -62,8 +62,8 @@ class BackupAppCard {
|
||||
|
||||
const allSnaps = this.flattenSnapshots();
|
||||
if (!allSnaps.length) {
|
||||
statusEl.innerHTML = `<span class="backup-status-dot none"></span> No snapshots yet`;
|
||||
snapsEl.innerHTML = `<div class="backup-empty-state">No snapshots found for <strong>${this.escape(this.appName)}</strong>. Click "Backup now" to create the first one.</div>`;
|
||||
statusEl.innerHTML = `<span class="backup-status-dot none"></span> No backups yet`;
|
||||
snapsEl.innerHTML = `<div class="backup-empty-state">No backups found for <strong>${this.escape(this.appName)}</strong>. Click "Backup now" to create the first one.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ class BackupAppCard {
|
||||
<div class="backup-snapshot-rows">
|
||||
${allSnaps.slice(0, 50).map(s => this._renderRow(s, iconUrl)).join('')}
|
||||
</div>
|
||||
${allSnaps.length > 50 ? `<div class="backup-snapshot-overflow">Showing the most recent 50 of ${allSnaps.length} snapshots. Use the <a href="/backup">backup center</a> for the full list.</div>` : ''}
|
||||
${allSnaps.length > 50 ? `<div class="backup-snapshot-overflow">Showing the most recent 50 of ${allSnaps.length} backups. Use the <a href="/backup">backup center</a> for the full list.</div>` : ''}
|
||||
`;
|
||||
|
||||
// Deep-link: /app/<name>/backups?snapshot=<id> auto-expands that row
|
||||
@ -99,7 +99,7 @@ class BackupAppCard {
|
||||
<span class="task-title">${this.escape(this.formatRelative(s.time))}</span>
|
||||
<span class="task-status backup-snapshot-loc-pill">${this.escape(s.locName)}</span>
|
||||
<span class="task-time" title="${this.escape(this._fmtFull(s.time))}">${this.escape(this._fmtShort(s.time))}</span>
|
||||
<span class="backup-snapshot-id-chip" title="Snapshot ID">${this.escape(sid)}</span>
|
||||
<span class="backup-snapshot-id-chip" title="Backup ID">${this.escape(sid)}</span>
|
||||
</div>
|
||||
<div class="task-actions">
|
||||
<button class="task-btn" data-action="restore-app-snapshot" data-loc="${this.escape(String(s.locIdx))}" data-snapshot="${this.escape(sid)}" title="Restore from this snapshot">
|
||||
@ -114,7 +114,7 @@ class BackupAppCard {
|
||||
</div>
|
||||
<div class="task-details">
|
||||
<div class="task-meta">
|
||||
<div class="meta-item"><strong>Snapshot ID:</strong> <code>${this.escape(sid)}</code></div>
|
||||
<div class="meta-item"><strong>Backup ID:</strong> <code>${this.escape(sid)}</code></div>
|
||||
<div class="meta-item"><strong>Location:</strong> ${this.escape(s.locName)}</div>
|
||||
<div class="meta-item"><strong>When:</strong> ${this.escape(this._fmtFull(s.time))}</div>
|
||||
${s.hostname ? `<div class="meta-item"><strong>Host:</strong> ${this.escape(s.hostname)}</div>` : ''}
|
||||
@ -213,7 +213,7 @@ class BackupAppCard {
|
||||
|
||||
async restoreSnapshot(locIdx, snapshot) {
|
||||
const locName = this.locationsByIdx[locIdx]?.name || `Location ${locIdx}`;
|
||||
if (!confirm(`Restore ${this.appName} from backup ${snapshot} at ${locName}? The app will be stopped, its folder wiped, the backup restored in place, then the app started again.`)) return;
|
||||
if (!confirm(`Restore ${this.appName} from backup ${snapshot} at ${locName}?\n\nThe app will be stopped, its folder wiped, the backup restored in place, then the app started again.`)) return;
|
||||
if (!this.taskManager) return;
|
||||
await this.taskManager.createTask(`libreportal restore app start ${this.appName} ${snapshot} ${locIdx}`, 'restore', this.appName);
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ const BACKUP_RETENTION_PRESETS = {
|
||||
const BACKUP_RETENTION_PRESET_META = {
|
||||
'inherit-global': { label: 'Inherit global retention', hint: 'Use whatever the Configuration tab specifies. Pick something else here only when this location needs a different policy.' },
|
||||
'self-hosting': { label: 'Self-hosting', hint: '30 days of daily backups. Plenty for a homelab — covers accidental deletes and app screw-ups.' },
|
||||
'personal': { label: 'Personal', hint: '30 days of daily backups plus 6 monthly snapshots. Good for personal data where "what did this look like last summer" matters.' },
|
||||
'personal': { label: 'Personal', hint: '30 days of daily backups plus 6 monthly backups. Good for personal data where "what did this look like last summer" matters.' },
|
||||
'enterprise': { label: 'Enterprise', hint: '30 daily + 12 monthly + 5 yearly. Compliance-style retention with multi-year history.' },
|
||||
'custom': { label: 'Custom…', hint: 'Define each retention tier yourself.' }
|
||||
};
|
||||
@ -44,11 +44,11 @@ const BACKUP_LOC_FIELD_DEFS = {
|
||||
B2_ACCOUNT_KEY: { title: 'B2 account key', description: '' },
|
||||
APPEND_ONLY: { title: 'Append-only', description: 'Ransomware-safe — refuse forget/prune for this location even if LibrePortal itself is compromised. Trades off automatic retention cleanup.' },
|
||||
CUSTOM_RETENTION: { title: 'Use custom retention', description: 'Otherwise this location inherits the global retention.' },
|
||||
KEEP_LAST: { title: 'Keep last', description: 'Snapshots to always retain.' },
|
||||
KEEP_DAILY: { title: 'Keep daily', description: 'One snapshot per day for this many days.' },
|
||||
KEEP_WEEKLY: { title: 'Keep weekly', description: 'One snapshot per week for this many weeks.' },
|
||||
KEEP_MONTHLY: { title: 'Keep monthly', description: 'One snapshot per month for this many months.' },
|
||||
KEEP_YEARLY: { title: 'Keep yearly', description: 'One snapshot per year for this many years.' }
|
||||
KEEP_LAST: { title: 'Keep last', description: 'Backups to always retain.' },
|
||||
KEEP_DAILY: { title: 'Keep daily', description: 'One backup per day for this many days.' },
|
||||
KEEP_WEEKLY: { title: 'Keep weekly', description: 'One backup per week for this many weeks.' },
|
||||
KEEP_MONTHLY: { title: 'Keep monthly', description: 'One backup per month for this many months.' },
|
||||
KEEP_YEARLY: { title: 'Keep yearly', description: 'One backup per year for this many years.' }
|
||||
};
|
||||
|
||||
// Fallback for the per-type field schema. The live source is the generator-
|
||||
@ -504,7 +504,7 @@ class BackupPage {
|
||||
subtitleFor(tab) {
|
||||
return {
|
||||
dashboard: "Check what's protected — and when it last ran.",
|
||||
backups: 'Every snapshot across every enabled location.',
|
||||
backups: 'Every backup across every enabled location.',
|
||||
locations: 'Where backups are stored. Add, edit, or remove destinations.',
|
||||
migrate: 'Restore an app from another LibrePortal that shares one of your backup locations.',
|
||||
configuration: 'Schedule, retention, and engine settings.'
|
||||
@ -1113,7 +1113,7 @@ class BackupPage {
|
||||
</svg>
|
||||
<div class="backup-warning-banner-text">
|
||||
<strong>Keep your LibrePortal config backed up offline.</strong>
|
||||
<span>Repository passwords live inside the config directory. Without that backup, snapshots cannot be decrypted by anyone — including you.</span>
|
||||
<span>Repository passwords live inside the config directory. Without that backup, the others cannot be decrypted by anyone — including you.</span>
|
||||
</div>
|
||||
<button type="button" class="backup-warning-banner-close" data-action="dismiss-config-warning" title="Dismiss this warning" aria-label="Dismiss this warning">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
@ -1349,7 +1349,7 @@ class BackupPage {
|
||||
</div>
|
||||
<div class="backup-retention-advanced" data-retention-advanced ${preset === 'custom' ? '' : 'hidden'}>
|
||||
<div class="backup-form-grid">
|
||||
${this.formInput(`${prefix}KEEP_LAST`, 'Keep last', values.last, 'number', '', 'snapshots')}
|
||||
${this.formInput(`${prefix}KEEP_LAST`, 'Keep last', values.last, 'number', '', 'backups')}
|
||||
${this.formInput(`${prefix}KEEP_DAILY`, 'Keep daily', values.daily, 'number', '', 'days')}
|
||||
${this.formInput(`${prefix}KEEP_WEEKLY`, 'Keep weekly', values.weekly, 'number', '', 'weeks')}
|
||||
${this.formInput(`${prefix}KEEP_MONTHLY`, 'Keep monthly', values.monthly, 'number', '', 'months')}
|
||||
@ -1837,7 +1837,7 @@ class BackupPage {
|
||||
if (!modal || !body) return;
|
||||
body.innerHTML = `
|
||||
<p>Restore <strong>${this.escape(app)}</strong> from backup <code>${this.escape(snapshot)}</code> at <strong>${this.escape(locName)}</strong>?</p>
|
||||
<p class="backup-card-hint">The app will be stopped, its folder wiped, the snapshot restored in place, then the app started again. App-specific pre/post-restore hooks run if present.</p>
|
||||
<p class="backup-card-hint">The app will be stopped, its folder wiped, the backup restored in place, then the app started again. App-specific pre/post-restore hooks run if present.</p>
|
||||
`;
|
||||
modal.dataset.app = app;
|
||||
modal.dataset.locIdx = locIdx;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user