Fix code-review findings on multi-tenant branch
- images.rs: scope /api/img and /api/photos/{id}/original by album owner —
close the cross-tenant original/thumbnail leak (tenancy test now covers
these routes)
- migration 0003: refuse to run when albums exist and users != 1 instead of
silently reassigning every album to the oldest user
- Gallery: callback-ref ResizeObserver so a gallery mounted empty still
lays out once photos arrive (was permanently blank)
- upload dedup: re-uploading identical content whose photo is in 'error'
resets and re-enqueues it instead of returning the broken row
- client hashing: skip (and fall back to plain upload) above 512MB to avoid
whole-file arrayBuffer OOM / the ~2GiB cap
- zip: always spool each entry (no unread prefetched S3 body held across a
slow client stream) and backfill BOTH sha256 and crc32 for legacy photos
- tests/auth: share one session_payload builder instead of re-implementing
the cookie format in the test and mint_session
This commit is contained in:
+21
-2
@@ -41,6 +41,25 @@ fn sanitize_filename(raw: &str) -> Result<String, ApiError> {
|
||||
Ok(cleaned.chars().take(150).collect())
|
||||
}
|
||||
|
||||
/// A dedup hit on a photo that previously failed processing means the user is
|
||||
/// re-uploading to fix it — reset it and re-enqueue instead of handing back a
|
||||
/// broken row that the UI would report as "already uploaded".
|
||||
async fn heal_if_errored(state: &AppState, photo: Photo) -> ApiResult<Photo> {
|
||||
if photo.status != PhotoStatus::Error {
|
||||
return Ok(photo);
|
||||
}
|
||||
let mut tx = state.db.begin().await?;
|
||||
let healed: Photo =
|
||||
sqlx::query_as("update photos set status = $2, error = null where id = $1 returning *")
|
||||
.bind(photo.id)
|
||||
.bind(PhotoStatus::Uploaded.as_str())
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
jobs::ensure_process_photo(&mut tx, photo.id).await?;
|
||||
tx.commit().await?;
|
||||
Ok(healed)
|
||||
}
|
||||
|
||||
pub async fn upload(
|
||||
State(state): State<AppState>,
|
||||
user: AuthUser,
|
||||
@@ -94,7 +113,7 @@ pub async fn upload(
|
||||
.fetch_optional(&state.db)
|
||||
.await?;
|
||||
if let Some(existing) = existing {
|
||||
return Ok(Json(existing));
|
||||
return Ok(Json(heal_if_errored(&state, existing).await?));
|
||||
}
|
||||
|
||||
// Upload to S3 first, then create the row and enqueue processing in one
|
||||
@@ -153,7 +172,7 @@ pub async fn upload(
|
||||
.fetch_optional(&state.db)
|
||||
.await?;
|
||||
if let Some(winner) = winner {
|
||||
return Ok(Json(winner));
|
||||
return Ok(Json(heal_if_errored(&state, winner).await?));
|
||||
}
|
||||
}
|
||||
return Err(e.into());
|
||||
|
||||
Reference in New Issue
Block a user