Stable backport combining #860 (never reached stable) and #900:
- jest.config.js gains testTimeout: 120000 — stable still ran on Jest's
5s default for anything unpinned, while its migration chain (134 core
migrations via backports) is nearly as long as beta's.
- All 19 suite-level jest.setTimeout(30000/60000) pins raised to 120s;
local pins override the config default (#860's rationale).
- All 15 hook-ARGUMENT timeout pins on migration-booting beforeAll
hooks raised to 120s (#900's rationale — the 3.97.0-beta.0 release PR
failed on exactly this class on the beta side).
Untouched: the three suites whose pinned hooks don't run migrations
(webhookDelivery, imageProcessor.storage, storageBackend) and
publicQuotes' 30s pin on the rate-limit lockout test.
No test logic changed.
Co-authored-by: Paul Nothaft <[email protected]>
First half of the GUI-only backup roundtrip. Adds a self-describing
".picpeak" archive that can be downloaded from one instance and (later)
re-uploaded to another via the web UI only.
- picpeakExportService.createPicpeak(): dumps every table as NDJSON
(tables introspected at runtime — no hardcoded list, won't rot), plus
a manifest (format version, app version, DB engine, latest migration,
per-table row counts + checksums, includePhotos, contains_secrets),
plus files/ (business-docs + uploads always; original gallery photos
only when includePhotos). NDJSON is engine-neutral so the target
rebuilds schema via migrations then loads rows — enabling pg↔pg /
sqlite↔sqlite and forward-only auto-migrate.
- GET /admin/backup/picpeak/export?includePhotos= streams the file and
sets X-Picpeak-Contains-Secrets (the file holds plaintext SMTP pass,
admin hashes, API keys — the UI must warn).
- Purely additive: no existing backup/restore path is touched.
Integration test proves the archive shape, knex-table exclusion, and
row-count/NDJSON consistency (85 tables on the seed schema).