Commit Graph

4 Commits

Author SHA1 Message Date
fabi
7154b3a810 fix(upload): remove the /original rate limit that would have broken the feed
The limiter added here was justified as bounding "100 guests occasionally tapping Original
anzeigen". That is not what this route is. `pickMediaUrl` resolves to
`preview_url ?? thumbnail_url ?? /original`, and a freshly committed upload has BOTH
derivatives null until the compression worker reaches it — at COMPRESSION_WORKER_CONCURRENCY=2
that is minutes during a post-ceremony burst. So /original is the feed's hot path for exactly
the newest photos, in a newest-first grid, at the busiest moment.

With every guest behind one NAT the 600/min bucket is venue-wide: six new photos fanned out
by `upload-new` to ~100 open feeds exhausts it, and then every original fetch from anyone
429s for the rest of the window. The tiles' own 4-second retry uses a fresh `?r=` nonce, so
the clients hold the bucket saturated themselves — the whole venue watching the newest
photos render as broken tiles while the projector skips slides.

A per-IP bucket cannot separate one scraper from the entire party when they share an
address, and these media routes are unauthenticated by design (an `<img>` cannot send a
bearer token), so there is no per-user key to move to. Bandwidth abuse belongs at the proxy.

Also here: the release/lock check order. `release ⇒ lock`, so testing the lock first made
the `GalleryReleased` arm unreachable dead code and every post-release upload answered
`uploads_locked`. The codes are not interchangeable to the client — `uploads_locked` charges
a retry attempt and re-pushes the whole photo on the backoff ladder against an answer that
cannot change, while `gallery_released` parks it and says the photo is safe but the hosts
must reopen. Both sites now test release first, so the fast path and the commit-time
re-check agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:43:50 +02:00
MechaCat02
57a907eca5 feat(theme+comments): runtime colour theme and COMMENTS_ENABLED switch
Colour theme is configurable at runtime from two seed colours (brand + accent);
neutrals stay fixed for contrast safety. Tailwind v4 var()-based tokens let a
:root:root override recolour everything with no rebuild; the 50->950 ramps are
derived via a color-mix ladder. Config lives in the DB config table (admin UI:
Config > Farbschema, presets + custom pickers + live preview), served on the public
/event endpoint with env defaults (THEME_PRESET/PRIMARY/ACCENT), propagated live via
event-updated SSE, and cached in localStorage for a no-flash boot. The keepsake export
mirrors the same ladder in Rust so offline archives match the event theme.

COMMENTS_ENABLED (env, default true) is a boot-time kill-switch: the backend rejects
new comments with 403 and the frontend hides the comment button (feed card) and
panel/composer (lightbox). Existing comments stay in the DB, hidden, and return when
re-enabled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 15:21:55 +02:00
fabi
ee554e7f38 style(backend): rustfmt the whole tree; gate cargo fmt --check in CI
The backend had never been run through rustfmt. Doing it in one mechanical pass (134 files)
so no future functional diff is buried under formatting churn, then gating `cargo fmt
--check` in checks.yml so it stays clean.

Formatting only — no logic, SQL, or behaviour changed. Verified after the reformat:
cargo test 56 passed, clippy --all-targets -D warnings clean, cargo fmt --check clean.

This is the deferred cleanup noted when CI's Format step was first left out.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:52:17 +02:00
fabi
faf7a2504a fix(audit): restore broken upload pipeline + role-based E2E audit fixes
A comprehensive role-based E2E audit (guest/host/admin, across browser
sessions) surfaced one critical and several smaller issues; this addresses
them and hardens the tests that missed them.

Critical
- The client upload pipeline was fully broken: the IndexedDB v1->v2 upgrade
  opened a *new* transaction inside the upgrade callback, which throws during
  a version-change transaction and aborted the whole upgrade, leaving the
  queue object store uncreated -- so no UI upload ever fired. Reuse the
  version-change transaction the callback provides, and bump the DB to v3 with
  a contains() guard so installs already corrupted by the shipped bug
  self-heal on next load. Re-enabled the previously-fixme'd UI upload E2E test.

High / Medium
- Event lock is uploads-only again: likes, comments and browsing stay open
  while the event is locked (USER_JOURNEYS 9.3 / FEATURES) -- it was wrongly
  freezing social interaction. Updated the event-lock spec accordingly.
- get_original now excludes soft-deleted and ban-hidden uploads, and direct
  /media/originals/** serving is blocked, so a hidden user's originals can no
  longer be pulled by UUID (all originals go through the checked alias).
- The upload handler reads the file field with an early-abort size cap chosen
  from the declared content-type, instead of buffering the entire body before
  the size check.

Low
- unban_user mirrors the ban role guard (a host can no longer unban a
  host/admin banned by an admin).
- reset_user_pin's UPDATE is event-scoped.
- Admin login returns and stores a real identity (user_id + display name)
  instead of a blank session.
- The host user list no longer renders target-actions (ban/promote/demote/PIN)
  on the caller's own row, where the backend always rejected them.
- /diashow gains a client-side auth guard like the other protected routes.
- The join page shows the event name via a new public GET /api/v1/event.

Verified: backend cargo build clean, frontend svelte-check 0 errors, full
Playwright E2E suite 144 passed / 1 skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 07:28:27 +02:00