github preston-peterson/aerodrome v3.4.1

latest releases: v3.4.136, v3.4.135, v3.4.134...
5 months ago

Three fixes to the v3.4.0 server-side backup work, surfaced during the v3.4.0 prod migration. The v3.4.0 release shipped the new server-side flow correctly but made three judgment calls that turned out wrong in practice. v3.4.1 reverses all three and lands the UI in the state v3.4.0 should have shipped in.

Fix #1 — restore the 2 GB browser-upload cap with the correct rejection message. The v3.4.0 release removed the hardcoded 2 GB cap on POST /api/backup/import on the rationale that the streaming-upload refactor made the cap unnecessary. That conflated two separate things: streaming-to-disk is a memory guard (correct, kept), but the 2 GB cap is a UX guard (incorrectly removed). Above 2 GB, browser uploads stop being reliable regardless of cap settings — network timeouts, browser memory pressure under long transfers, intermediate proxy limits, and connection interruptions all start happening. The user-visible failure mode in v3.4.0 was a multi-minute upload that ended with "NetworkError when attempting to fetch resource" — exactly the user-hostile failure the cap was supposed to prevent. v3.4.1 reinstates the 2 GB cap with progressive enforcement (check during streaming, abort early rather than after fully receiving a doomed upload) and a clear rejection message that points users at the server-side flow: "Backup is too large for browser upload (>2 GB). Above this size, browser uploads become unreliable. Use the server-side restore flow instead: copy this file into <install_dir>/.backups/ via scp or rsync, then use the Existing server-side backups list below — your backup will appear after clicking Rescan." The rejection is HTTP 413 (Payload Too Large) which is the semantically-correct status code.

Fix #2 — replace the path-input restore UI with a list-only flow plus Rescan button. The v3.4.0 "Restore from a path on disk" subsection asked users to type a full path into a text input while simultaneously documenting that the path must be inside .backups/. That's a redundant-typing UX trap: the user enters /opt/aerodrome/.backups/aerodrome-backup-20260512-150658.zip when only the filename is meaningful (the directory is a server-side constraint they shouldn't be typing). Worse, any typo in the directory portion produced a confusing "outside scope" 403 instead of a "no such file" 404. The v3.4.1 design removes the path-entry input entirely — the "Existing server-side backups" list above is now the only restore surface. Each backup in the list has its existing "Restore from this" button. Above the list, a prominent "↻ Rescan" button re-fetches /api/backup/list-server-side and shows toast feedback with the new-since-last-fetch count ("Found 3 backup(s) (+1 new)"). The Rescan button is the answer to the "I just scp'd a file, why don't I see it?" workflow — one click and the new file appears in the list. The POST /api/backup/restore-from-path API endpoint is kept (it's called by the per-row "Restore from this" buttons with the full path resolved server-side), so the API contract is unchanged; only the user-typed-path UI surface goes away.

Fix #3 — fix the stuck "Restoring..." button on failed restore. The v3.4.0 path-restore flow used a fixed button ID (ssRestoreBtn) and re-enabled it in a finally block — but the per-row buttons in the list had no stable ID, so when the path-restore implementation handled their click via restoreFromServerPathDirect(path) (which routed through the same code), the finally block re-enabled the wrong button (or no button at all if ssRestoreBtn had been removed). The user-visible result during the v3.4.0 prod debug session: a failed restore left "Restoring..." disabled indefinitely. The v3.4.1 fix collapses the two functions into a single restoreFromServerSideBackup(path, btnEl) that takes the actual button element as a parameter — the finally block always re-enables the right button regardless of where it lives in the DOM. The per-row buttons pass this so the function receives the clicked button directly. Side benefit: this pattern generalizes — if the same backup-restore flow is reused from anywhere else in the UI later, the calling site just passes its own button element and stuck-button bugs become structurally impossible.

Why this is a patch (3.4.1) rather than a deferred polish item. The v3.4.0 build had three observable bugs against the design we'd agreed to before coding. Stuck buttons and silent removal of UX guards aren't acceptable to ship under "we'll fix it in the next bundle" — they're regressions from the intended behavior. v3.4.1 is the v3.4.0 release that should have shipped, with the same architectural model (browser + server-side as two transports for one zip format) but the UX guards and stuck-button bug corrected. The retrospective: when design conversations specify "keep X as a guard but route around it for the large-data case," shipping "removed X entirely" is the wrong interpretation no matter how clean the streaming refactor felt afterward.

Scope:

  • server.py — restore the 2 GB cap in POST /api/backup/import with progressive check + HTTP 413 + server-side-redirect message (~25 lines). POST /api/backup/restore-from-path endpoint unchanged (still called from list-row buttons).
  • templates/config.html — remove "Restore from a path on disk" HTML subsection (~25 lines removed); collapse restoreFromServerPath + restoreFromServerPathDirect into restoreFromServerSideBackup(path, btnEl) (~50 lines net); add rescanServerSideBackups() with new-since-last-fetch toast (~30 lines); replace "↻ Refresh" button with "↻ Rescan" button; update section intro text to match the new flow.
  • docs/INSTALL.md — update "Restoring from a server-side backup" paragraph to describe the list+Rescan flow rather than the path-input flow.

No schema changes, no config-key changes, no sudoers changes, no API endpoint additions or removals. Strictly a UX correction on top of the v3.4.0 architecture.

Operational note: users at v3.4.0 should upgrade to v3.4.1 before relying on the server-side flow — the path-input UI subsection is gone in favor of a cleaner list-driven workflow, the stuck-button bug is fixed, and oversized browser uploads now get a fast actionable error message instead of a multi-minute "NetworkError" timeout.

Don't miss a new aerodrome release

NewReleases is sending notifications on new releases.