fix(export): give the keepsake viewer the two-phase preflight it was meant to get

The two-phase preflight from eb0e405 landed in ONE place and was spliced inside
the other. `run_zip_export` ended up containing both blocks nested, so the
Gallery path pruned "Memories" archives that were not its to reclaim, while
`run_html_export` silently kept the single-phase form.

That left the exact deadlock the two-phase preflight exists to break, still
open on half the product. At a gallery size where a rebuild needs the previous
generation's bytes: the ZIP prunes its own superseded archive and rebuilds, and
the HTML preflight fails against a Memories archive still on disk. The prune
that would free it runs only after a success that can never happen, and any
epoch bump — a guest deleting one photo — retires the current viewer
immediately. Permanently stuck, unreachable from any handler, discovered at the
end of the night with nobody there.

Both halves now call one `ensure_export_space_reclaiming`, keyed on the
caller's OWN prefix, so they cannot drift again.

Three smaller things found in the same pass:

- The boot-failure panel hardcoded light-mode colours, and its heading set none
  at all — the UA default black on the `#100f0f` dark background. On the one
  screen whose entire job is to be readable, and in the failure mode where the
  app's own stylesheet may be what did not load. Moved to classes in the inline
  <style> so the `html.dark` variants apply.

- `.env.example` assigned RUST_LOG twice, 120 lines apart. Compose takes the
  last one, so an operator raising the level mid-event to chase a problem would
  have changed nothing, silently.

- The comment justifying `detail = ?message` in error.rs still claimed
  `validate_display_name` allows newlines. It rejects control characters now —
  but that is one input against every 4xx message in the app, so the escaping
  is what makes the guarantee general. Said so.

151/151 backend, 58/58 vitest, clippy clean, svelte-check 0 errors, both
builds, caddy validate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-08-08 22:48:22 +02:00
parent 5b705317ef
commit 253878e027
4 changed files with 108 additions and 61 deletions

View File

@@ -51,12 +51,12 @@ POSTGRES_DB=eventsnap
# limit — an OOM in Postgres doesn't degrade one feature, it takes the whole event down.
DATABASE_MAX_CONNECTIONS=15
# Log level. `info` is the right production default: at `debug` the tower-http trace
# layer writes a line per request AND per response, which on a busy event is a large
# multiple of the useful output. Container logs are capped at 10m x 3 per service
# (docker-compose.yml), so a chatty level buys you a shorter history, not more of it.
# To debug a live event: RUST_LOG=eventsnap_backend=debug docker compose up -d app
RUST_LOG=info
# Log level: see the "Logging" section near the bottom of this file.
#
# Defined THERE and nowhere else, deliberately. This file used to assign RUST_LOG twice
# once here and once there — and Compose takes the LAST assignment, so editing this line to
# `debug` to chase a problem during the event changed nothing at all, silently. A key that
# appears twice in a .env is a trap regardless of which value is better.
# ── Authentication ────────────────────────────────────────────────────────────
# Generate with: openssl rand -hex 64