Compare commits

..

No commits in common. "64a0509ea9768bc5dd5528f7718c8b8254347823" and "e9e29ba7030f3d713de1a491412982670854c93b" have entirely different histories.

5 changed files with 5 additions and 70 deletions

View File

@ -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('');

View File

@ -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))

View File

@ -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
}

View File

@ -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"

View File

@ -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"