Compare commits
No commits in common. "64a0509ea9768bc5dd5528f7718c8b8254347823" and "e9e29ba7030f3d713de1a491412982670854c93b" have entirely different histories.
64a0509ea9
...
e9e29ba703
@ -331,26 +331,17 @@ class BackupPage {
|
|||||||
|
|
||||||
async refreshAll() {
|
async refreshAll() {
|
||||||
const ts = Date.now();
|
const ts = Date.now();
|
||||||
const [dashboard, locations, , schema, migrate, peersData] = await Promise.all([
|
const [dashboard, locations, , schema, migrate] = await Promise.all([
|
||||||
this.fetchJson(`/data/backup/generated/dashboard.json?t=${ts}`),
|
this.fetchJson(`/data/backup/generated/dashboard.json?t=${ts}`),
|
||||||
this.fetchJson(`/data/backup/generated/locations.json?t=${ts}`),
|
this.fetchJson(`/data/backup/generated/locations.json?t=${ts}`),
|
||||||
this.loadSystemConfigs(),
|
this.loadSystemConfigs(),
|
||||||
this.fetchJson(`/data/backup/generated/schema.json?t=${ts}`),
|
this.fetchJson(`/data/backup/generated/schema.json?t=${ts}`),
|
||||||
this.fetchJson(`/data/backup/generated/migrate.json?t=${ts}`),
|
this.fetchJson(`/data/backup/generated/migrate.json?t=${ts}`)
|
||||||
this.fetchJson(`/data/peers/generated/peers.json?t=${ts}`)
|
|
||||||
]);
|
]);
|
||||||
this.dashboard = dashboard;
|
this.dashboard = dashboard;
|
||||||
this.locations = locations;
|
this.locations = locations;
|
||||||
this.locSchema = schema;
|
this.locSchema = schema;
|
||||||
this.migrate = migrate;
|
this.migrate = migrate;
|
||||||
// Build hostname → friendly-name lookup once so renderMigrate can show
|
|
||||||
// "homelab (host: homelab.lan)" instead of bare hostnames.
|
|
||||||
this.hostnameToPeerName = {};
|
|
||||||
for (const p of (peersData?.peers || [])) {
|
|
||||||
if (p.kind === 'backup-channel' && p.config?.hostname) {
|
|
||||||
this.hostnameToPeerName[p.config.hostname] = p.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.snapshotsByLoc = {};
|
this.snapshotsByLoc = {};
|
||||||
|
|
||||||
if (!this.engines.length) await this.loadEngines();
|
if (!this.engines.length) await this.loadEngines();
|
||||||
@ -1820,16 +1811,11 @@ class BackupPage {
|
|||||||
<h3 style="margin:0">${this.escape(loc.name || 'Location')}</h3>
|
<h3 style="margin:0">${this.escape(loc.name || 'Location')}</h3>
|
||||||
<span class="backup-card-hint">${(loc.hosts || []).length} other host${loc.hosts.length === 1 ? '' : 's'} backing up here</span>
|
<span class="backup-card-hint">${(loc.hosts || []).length} other host${loc.hosts.length === 1 ? '' : 's'} backing up here</span>
|
||||||
</div>
|
</div>
|
||||||
${loc.hosts.map(host => {
|
${loc.hosts.map(host => `
|
||||||
const peerName = (this.hostnameToPeerName || {})[host.hostname];
|
|
||||||
const headerLabel = peerName
|
|
||||||
? `<strong style="font-size:1.05em">${this.escape(peerName)}</strong><span class="backup-card-hint" style="margin-left:6px; font-size:.85em">host: <code>${this.escape(host.hostname)}</code></span>`
|
|
||||||
: `<strong style="font-size:1.05em">${this.escape(host.hostname)}</strong>`;
|
|
||||||
return `
|
|
||||||
<div class="backup-migrate-host" style="border:1px solid var(--border-color, #2a2a2a); border-radius:8px; padding:14px; margin-bottom:12px">
|
<div class="backup-migrate-host" style="border:1px solid var(--border-color, #2a2a2a); border-radius:8px; padding:14px; margin-bottom:12px">
|
||||||
<div style="display:flex; justify-content:space-between; align-items:center; gap:12px; margin-bottom:10px">
|
<div style="display:flex; justify-content:space-between; align-items:center; gap:12px; margin-bottom:10px">
|
||||||
<div>
|
<div>
|
||||||
${headerLabel}
|
<strong style="font-size:1.05em">${this.escape(host.hostname)}</strong>
|
||||||
<span class="backup-card-hint" style="margin-left:10px">${(host.apps || []).length} app${host.apps.length === 1 ? '' : 's'} available</span>
|
<span class="backup-card-hint" style="margin-left:10px">${(host.apps || []).length} app${host.apps.length === 1 ? '' : 's'} available</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="backup-primary-btn" data-action="migrate-host"
|
<button class="backup-primary-btn" data-action="migrate-host"
|
||||||
@ -1860,8 +1846,7 @@ class BackupPage {
|
|||||||
}).join('')}
|
}).join('')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`).join('')}
|
||||||
}).join('')}
|
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
|
|||||||
@ -81,9 +81,6 @@ migrateApplyApp()
|
|||||||
migrateEmit phase=pre-backup status=skipped reason=user-opt-out app="$app"
|
migrateEmit phase=pre-backup status=skipped reason=user-opt-out app="$app"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Per-app pre-migrate hook (optional) — declared in the app's tools.sh.
|
|
||||||
migrateRunHook "$app" pre "$source_host" "restic"
|
|
||||||
|
|
||||||
# ---- 3. The actual restore (reuses existing restoreAppStart) --------------
|
# ---- 3. The actual restore (reuses existing restoreAppStart) --------------
|
||||||
# restoreAppStart already wipes the app folder, restores the snapshot,
|
# restoreAppStart already wipes the app folder, restores the snapshot,
|
||||||
# re-runs the install-time tag pipeline, and starts the container. The
|
# re-runs the install-time tag pipeline, and starts the container. The
|
||||||
@ -112,9 +109,6 @@ migrateApplyApp()
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Per-app post-migrate hook (optional) — last thing before we declare done.
|
|
||||||
migrateRunHook "$app" post "$source_host" "restic"
|
|
||||||
|
|
||||||
local finished_at
|
local finished_at
|
||||||
finished_at=$(date +%s)
|
finished_at=$(date +%s)
|
||||||
local duration=$((finished_at - started_at))
|
local duration=$((finished_at - started_at))
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Per-app migrate hooks. After a migrate (restic-mediated apply OR direct-SSH
|
|
||||||
# pull) places the source's data on this host, some apps need app-specific
|
|
||||||
# fix-ups beyond the standard URL rewrite — rotating a federation key,
|
|
||||||
# regenerating an OIDC client secret, dropping a SaaS lock, etc.
|
|
||||||
#
|
|
||||||
# Convention: an app's tools.sh (auto-sourced by the modular per-app tools
|
|
||||||
# loader — see [[libreportal-modular-app-tools]]) may declare:
|
|
||||||
#
|
|
||||||
# <app>_migrate_pre() # called before stop+wipe
|
|
||||||
# <app>_migrate_post() # called after restart, before user sees it
|
|
||||||
#
|
|
||||||
# Both receive: $1 = source_hostname (peer hostname or backup tag),
|
|
||||||
# $2 = transport ("restic" | "direct-ssh")
|
|
||||||
# Hooks are optional — apps without them just inherit the standard flow.
|
|
||||||
|
|
||||||
# Run a single named hook if it exists. Quiet if not defined.
|
|
||||||
migrateRunHook()
|
|
||||||
{
|
|
||||||
local app="$1"
|
|
||||||
local stage="$2" # "pre" or "post"
|
|
||||||
local source="$3"
|
|
||||||
local transport="$4"
|
|
||||||
|
|
||||||
local hook_name="${app}_migrate_${stage}"
|
|
||||||
if declare -f "$hook_name" >/dev/null 2>&1; then
|
|
||||||
isNotice "Running ${stage}-migrate hook for ${app}"
|
|
||||||
migrateEmit phase="hook-${stage}" status=running app="$app" hook="$hook_name"
|
|
||||||
if "$hook_name" "$source" "$transport"; then
|
|
||||||
migrateEmit phase="hook-${stage}" status=complete app="$app"
|
|
||||||
else
|
|
||||||
isNotice "Hook ${hook_name} returned non-zero — continuing migrate"
|
|
||||||
migrateEmit phase="hook-${stage}" status=failed app="$app"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
@ -64,9 +64,6 @@ peerPullApp()
|
|||||||
migrateEmit phase=pre-backup status=skipped reason=user-opt-out app="$app"
|
migrateEmit phase=pre-backup status=skipped reason=user-opt-out app="$app"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Per-app pre-migrate hook (optional) — declared in the app's tools.sh.
|
|
||||||
migrateRunHook "$app" pre "$peer_name" "direct-ssh"
|
|
||||||
|
|
||||||
# ---- 3. Stop + wipe ----------------------------------------------------
|
# ---- 3. Stop + wipe ----------------------------------------------------
|
||||||
migrateEmit phase=stop status=running app="$app"
|
migrateEmit phase=stop status=running app="$app"
|
||||||
if declare -f dockerComposeDown >/dev/null 2>&1 && [[ -d "$containers_dir$app" ]]; then
|
if declare -f dockerComposeDown >/dev/null 2>&1 && [[ -d "$containers_dir$app" ]]; then
|
||||||
@ -120,9 +117,6 @@ peerPullApp()
|
|||||||
fi
|
fi
|
||||||
migrateEmit phase=start-app status=complete app="$app"
|
migrateEmit phase=start-app status=complete app="$app"
|
||||||
|
|
||||||
# Per-app post-migrate hook (optional) — last thing before declaring done.
|
|
||||||
migrateRunHook "$app" post "$peer_name" "direct-ssh"
|
|
||||||
|
|
||||||
local finished_at; finished_at=$(date +%s)
|
local finished_at; finished_at=$(date +%s)
|
||||||
local duration=$((finished_at - started_at))
|
local duration=$((finished_at - started_at))
|
||||||
isSuccessful "Pulled $app from $peer_name in ${duration}s"
|
isSuccessful "Pulled $app from $peer_name in ${duration}s"
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
migrate_scripts=(
|
migrate_scripts=(
|
||||||
"migrate/migrate_apply.sh"
|
"migrate/migrate_apply.sh"
|
||||||
"migrate/migrate_discover.sh"
|
"migrate/migrate_discover.sh"
|
||||||
"migrate/migrate_hooks.sh"
|
|
||||||
"migrate/migrate_pre_backup.sh"
|
"migrate/migrate_pre_backup.sh"
|
||||||
"migrate/migrate_preflight.sh"
|
"migrate/migrate_preflight.sh"
|
||||||
"migrate/migrate_progress.sh"
|
"migrate/migrate_progress.sh"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user