feat(installer): install picpeak directly from a backup via trigger file
Closes the six-step DR dance ("onboard throwaway admin → restore via
wizard → log out → log back in with originals") by letting admins
recover an install with zero clicks past `docker compose up`.
Convention: drop a file named `RESTORE_ON_INSTALL` (no extension OR
.txt) into the existing `/backup` bind mount. On next container
start, the new boot hook detects it, runs the restore, and starts
the server with the restored state. Admin opens the browser, login
works first try.
Payload variants:
- empty file → auto-picks newest backup-manifest-*.json from
/backup/manifests/. Useful for "restore the latest".
- path inside the file → uses that specific manifest. Useful for
"I want this older backup, not the most recent".
Safety gates (three layers):
1. Trigger file must exist — no auto-magic, admin signals intent
2. DB must be empty (no events, ≤1 admin) — refuses to clobber
production data
3. Restore failure leaves the trigger file in place for retry on
next container start. Success deletes it so subsequent boots
don't redo the work.
Override hook: INSTALL_FROM_BACKUP_FORCE=true skips guard #2 for the
"I know what I'm doing" edge case (dev env rebuilds, etc).
No docker-compose changes required — uses the bind mount picpeak
already has, env vars are optional. The minimal admin workflow now
matches the bare-minimum mental model: "copy my backup files,
restart the container, log in with original credentials."
Tests: 7 scenarios covering trigger detection, payload variants,
safety gates, success/failure trigger-file lifecycle.
This commit is contained in:
@@ -824,6 +824,24 @@ async function startServer() {
|
||||
logger.warn('restore-settings self-heal failed at boot:', err.message);
|
||||
}
|
||||
|
||||
// Install-from-backup trigger. If `RESTORE_ON_INSTALL` (or
|
||||
// `.txt`) exists in the /backup mount AND the DB is empty, run
|
||||
// the restore HERE before any admin UI surfaces. Lets admins
|
||||
// recover a picpeak install with: (a) place backup files in the
|
||||
// bind mount, (b) drop the trigger file, (c) `docker compose up`.
|
||||
// No onboarding wizard, no throwaway admin, no compose-file
|
||||
// changes. See _installFromBackupBoot.js for the full rationale
|
||||
// + the safety gates.
|
||||
try {
|
||||
const { tryInstallFromBackup } = require('./src/services/_installFromBackupBoot');
|
||||
const result = await tryInstallFromBackup(db, logger);
|
||||
if (result.ran) {
|
||||
logger.info(`Install-from-backup: completed from ${result.manifestPath}. Server will start with restored state.`);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn('Install-from-backup hook threw:', err.message);
|
||||
}
|
||||
|
||||
// Start backup service
|
||||
await startBackupService();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user