Merge branch 'fix/deploy-unattended-blockers' into main
Two independent lines of production hardening diverged at7d0334band attacked overlapping problems. Neither was a superset, so this is a merge of substance rather than a fast-forward: every conflict was resolved on the merits, and the losing side's intent was re-checked against the winner rather than assumed. MIGRATIONS. The branch's 021/022/023 collided with main's already-DEPLOYED 021_hashtag_counts_respect_bans and 022_client_upload_idempotency. Renumbered to 023/024/025 in a prior commit — main's versions are applied in production, so their version numbers are immutable and the branch's had to move. Verified by running the full sqlx::test suite, which applies the whole chain from scratch. RESOLVED IN MAIN'S FAVOUR (the branch would have regressed these): * upload-queue.ts wholesale — the branch's copy has ZERO client_upload_id references, so taking it would have silently destroyed end-to-end upload idempotency, the one thing standing between a lost response and a duplicate photo charged twice against the guest's quota. * maintenance.rs supervisor — the branch replaced it with a bare tokio::spawn, where one panic silently stops session pruning, media reclaim, the temp sweep and both HashMap prunes, permanently and with no log line. * The decode-budget probe on spawn_blocking, not inline on the async runtime. * feed/+page.svelte's 8s debounce + jitter + max-wait + hidden-tab deferral, against the branch's naive 800ms — at 100 guests the branch's version walks straight into the per-user feed rate limit. * db.rs pool tuning, /uploaders, and the docker-compose deployment story. * ONE /health, still DB-backed. The branch's split (dependency-free liveness + DB-backed readiness) is defensible, but a constant-"ok" /health is the exact defectfaea555fixed and verified live, its motive (Caddy's boot gate) is already covered by app depends_on db: service_healthy, and the two handlers were the same SELECT 1 under two names. TAKEN FROM THE BRANCH: * The large-PNG OOM guard and its bounded-retry counter (023). Together these turn a single upload that can OOM-kill a 1G container into a bounded failure instead of an infinite restart loop under `restart: unless-stopped`. * 024_feed_scalar_counts — the feed no longer aggregates the whole event per page. Pure SQL; column names, order and types are unchanged by design. * The admin-lockout fix: look the admin up BY ROLE, never by name. 025 also frees any guest already squatting on a reserved name. * PIN lockout tier ordering, bounded caption/hashtag reads, SSE ticket caps, PoolTimedOut -> 503 + Retry-After, and the ffmpeg stderr drain. * backfill_video_posters, which main lacked entirely. * TempFileGuard, plus sweep_orphan_originals wired into main's SUPERVISED loop (not the branch's bare one) — it reclaims final-named originals whose commit never happened, a class main's .tmp-only sweep structurally cannot see. * shouldAbortForStall, hand-ported into main's upload-queue.ts since that file was resolved to main. Widens the watchdog at loadend instead of disarming it, bounding a half-open socket at 2 minutes rather than handing the window to xhr.timeout (5-60 min) with the whole queue's `processing` latch held. ALSO: RUST_LOG and EXPORT_PATH pinned in compose. The code fallback was `debug` (a line per request, all night) and EXPORT_PATH was the one path with a mount-shaped default that nothing validated. Verified: cargo check --all-targets, cargo clippy (clean), 144/144 backend tests against a live Postgres including upload_idempotency and upload_concurrency, 51/51 vitest, svelte-check 0 errors, eslint clean, vite build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -56,9 +56,24 @@ services:
|
||||
logging: *default-logging
|
||||
env_file: .env
|
||||
environment:
|
||||
# Default to info. Without this the code fallback in main.rs applies, which is
|
||||
# `eventsnap_backend=debug,tower_http=debug` — a line per HTTP request AND per
|
||||
# response, including every preview fetch, for a multi-day run. The x-logging cap
|
||||
# above bounds the disk cost but not the CPU/IO one.
|
||||
#
|
||||
# Set here rather than only in `.env` because a stock deploy sets RUST_LOG nowhere,
|
||||
# and this is the layer an operator will actually find when they need to raise it
|
||||
# for a single event (`RUST_LOG=eventsnap_backend=debug docker compose up -d app`).
|
||||
RUST_LOG: ${RUST_LOG:-info}
|
||||
# Activates the production secret guard in config.rs — refuses to boot with
|
||||
# placeholder JWT_SECRET / ADMIN_PASSWORD_HASH.
|
||||
APP_ENV: production
|
||||
# Pinned beside MEDIA_PATH for the same reason, and because nothing validates it:
|
||||
# config.rs defaults it to /exports but never checks that it is a mount or that it
|
||||
# differs from media_path. A stray EXPORT_PATH in .env builds the keepsake into the
|
||||
# container's writable layer, where it passes every health check and disk preflight
|
||||
# and then evaporates on the next `up -d`.
|
||||
EXPORT_PATH: /exports
|
||||
# The media volume is mounted at /media (below), so the app MUST write there.
|
||||
# Pin it here rather than trusting .env: if MEDIA_PATH in .env points elsewhere
|
||||
# (e.g. a host path used for running the backend natively) the container can't
|
||||
|
||||
Reference in New Issue
Block a user