fix(diashow): open SSE on mount; raise upload rate 10→100/hour

diashow only subscribed to SSE events but never called connectSse(), so a
kiosk/projector opening /diashow directly (not via /feed) never opened the
EventSource — the showcase display got a one-time /feed snapshot and no live
updates, showing "Noch keine Beiträge" forever when turned on before any
photos. Open the stream in onMount (idempotent) and close it in onDestroy,
mirroring the feed page.

Raise the default upload_rate_per_hour from 10 to 100 (migration 015, scoped
to installs still on the old default so admin overrides are preserved). Guests
routinely upload bursts of 10-20 photos; the old default throttled the first
burst. Also update the code fallback and the test-mode reseed.

Both verified end-to-end against the docker test stack.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-18 17:29:08 +02:00
parent 3654aca18b
commit 3c3a7d0082
6 changed files with 26 additions and 5 deletions

View File

@@ -709,7 +709,7 @@ CREATE TABLE config (
INSERT INTO config (key, value) VALUES
('max_image_size_mb', '20'),
('max_video_size_mb', '500'),
('upload_rate_per_hour', '10'),
('upload_rate_per_hour', '100'), -- raised from 10 in migration 015 (guests upload bursts of 10-20)
('feed_rate_per_min', '60'),
('export_rate_per_day', '3'),
('quota_tolerance', '0.75'),

View File

@@ -0,0 +1,3 @@
-- Revert the default upload rate to 10/hour for installs still on the raised
-- default (preserves any explicit admin override at another value).
UPDATE config SET value = '10' WHERE key = 'upload_rate_per_hour' AND value = '100';

View File

@@ -0,0 +1,10 @@
-- Raise the default per-guest upload rate from 10/hour to 100/hour.
--
-- Rationale: guests routinely upload a burst of 10-20 photos at once (phone
-- multi-select). At the old default of 10/hour a real guest's first burst was
-- throttled — surfaced by the 2026-07-18 load test. 100/hour comfortably covers
-- several bursts across an event while still bounding abuse.
--
-- Only bump installs still on the old default; an admin who deliberately set a
-- different value keeps it (migration 005 seeded 10; this UPDATE is scoped to '10').
UPDATE config SET value = '100' WHERE key = 'upload_rate_per_hour' AND value = '10';

View File

@@ -40,13 +40,13 @@ pub async fn truncate_all(
.execute(&state.pool)
.await?;
// Reseed config — mirrors migrations 005 and 009. Kept in sync by hand
// Reseed config — mirrors migrations 005, 009 and 015. Kept in sync by hand
// because pulling SQL out of the migration files at runtime is fragile.
sqlx::query(
r#"INSERT INTO config (key, value) VALUES
('max_image_size_mb', '20'),
('max_video_size_mb', '500'),
('upload_rate_per_hour', '10'),
('upload_rate_per_hour', '100'),
('feed_rate_per_min', '60'),
('export_rate_per_day', '3'),
('quota_tolerance', '0.75'),

View File

@@ -48,7 +48,7 @@ pub async fn upload(
let upload_rate_on = config::get_bool(&state.config_cache, "upload_rate_enabled", true).await;
if rate_limits_on && upload_rate_on {
let upload_rate =
config::get_i64(&state.config_cache, "upload_rate_per_hour", 10).await as usize;
config::get_i64(&state.config_cache, "upload_rate_per_hour", 100).await as usize;
if let Err(retry_after_secs) = state.rate_limiter.check_with_retry(
format!("upload:{}", auth.user_id),
upload_rate,

View File

@@ -5,7 +5,7 @@
import { getToken } from '$lib/auth';
import { showBottomNav } from '$lib/ui-store';
import { dataMode, pickMediaUrl } from '$lib/data-mode-store';
import { onSseEvent } from '$lib/sse';
import { connectSse, disconnectSse, onSseEvent } from '$lib/sse';
import { SlideQueue } from '$lib/diashow/queue';
import { transitions, findTransition } from '$lib/diashow/transitions';
import { acquireWakeLock, releaseWakeLock } from '$lib/diashow/wakelock';
@@ -233,6 +233,13 @@
unsubs.push(onSseEvent('upload-deleted', handleUploadDeleted));
unsubs.push(onSseEvent('user-hidden', handleUserHidden));
unsubs.push(onSseEvent('feed-delta', handleFeedDelta));
// Open the stream ourselves — a kiosk/projector loads /diashow directly (never
// via /feed), so we can't rely on another page having opened the singleton
// EventSource. Without this the show only ever gets its one-time /feed snapshot
// and never receives live `upload-processed` events. connectSse() is idempotent
// (no-ops if the feed page already opened it). Subscriptions are registered above
// first so no early event is missed.
connectSse();
void loadInitial();
});
@@ -243,6 +250,7 @@
if (processedDebounce) clearTimeout(processedDebounce);
void releaseWakeLock();
for (const unsub of unsubs) unsub();
disconnectSse();
});
</script>