diff --git a/backend/src/services/export.rs b/backend/src/services/export.rs index 4cac9ab..f754c80 100644 --- a/backend/src/services/export.rs +++ b/backend/src/services/export.rs @@ -20,6 +20,29 @@ use crate::state::SseEvent; static VIEWER_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static/export-viewer"); +// ── Shared visibility filter ───────────────────────────────────────────────── + +/// The predicate that decides what lands in a keepsake, as ONE definition. +/// +/// Two queries have to agree on it: [`query_uploads`], which selects the rows the archives are +/// built from, and [`estimate_export_bytes`], which sizes them for the disk preflight. They used +/// to state it separately, and the direction of drift matters — an estimate that misses rows the +/// archive writes UNDER-reserves, which is the exact ENOSPC the preflight exists to prevent. +/// +/// A `SRC:`-marked copy in the integration tests cannot catch that: drift means production moved +/// and the copy didn't, so both sides of such a test sit still and it keeps passing. Sharing the +/// fragment removes the failure by construction instead, and leaves the test doing what it is +/// actually good at — pinning the behaviour. +/// +/// CONTRACT: callers must alias `upload` as `u` and join `"user"` as `usr`, and bind the event id +/// as `$1`. +macro_rules! export_visibility_where { + () => { + "WHERE u.event_id = $1 AND u.deleted_at IS NULL + AND usr.uploads_hidden = FALSE AND usr.is_banned = FALSE" + }; +} + // ── DB query rows ──────────────────────────────────────────────────────────── #[derive(sqlx::FromRow)] @@ -1051,7 +1074,7 @@ async fn run_html_export_inner( // ── DB helpers ─────────────────────────────────────────────────────────────── async fn query_uploads(pool: &PgPool, event_id: Uuid) -> Result> { - Ok(sqlx::query_as::<_, ExportUploadRow>( + Ok(sqlx::query_as::<_, ExportUploadRow>(concat!( "SELECT u.id, u.original_path, u.mime_type, u.caption, usr.display_name AS uploader_name, COUNT(DISTINCT l.user_id) AS like_count, @@ -1059,11 +1082,12 @@ async fn query_uploads(pool: &PgPool, event_id: Uuid) -> Result