fix(versions): review findings on PR #193

- A version no longer hides behind an original that duplicates resolve
  demoted (visible_version_clause, hidden_version_rows).
- Recording a link drops the version's faces, CLIP data and clip failures
  (covers link-at-ingest and link_versions --apply).
- The display file must live in the original's library; otherwise the
  original shows, and PUT /image/versions/display answers 400.
- link_new_files looks up hashes in chunks of 500 and logs errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cameron CordesandClaude Opus 5.5 committed 2026-10-30 14:27:54 -04:00
1 parent 6135034d09
commit 9f50843479
6 files changed
+402 -47

No files matched your search

+20 -3
View File
@@ -159,9 +159,16 @@ pub fn build_response(
});
}
let display_hash = dao
.get_display(original_hash)?
.filter(|d| versions.iter().any(|v| &v.hash == d));
// Clients fetch the display file through the original's library, so a
// choice with no row there reads as the original (as `/photos` does).
let display_hash = dao.get_display(original_hash)?.filter(|d| {
versions.iter().any(|v| {
&v.hash == d
&& original
.as_ref()
.is_none_or(|o| o.library_id == v.library_id)
})
});
Ok(VersionsResponse {
original,
versions,
@@ -232,6 +239,16 @@ pub async fn put_display(
if !current.versions.iter().any(|v| v.hash == hash) {
return HttpResponse::BadRequest().body("display_hash is not a version of this photo");
}
// The display file is fetched through the original's library.
let original_lib = current.original.as_ref().map(|o| o.library_id);
if !current
.versions
.iter()
.any(|v| v.hash == hash && original_lib.is_none_or(|l| l == v.library_id))
{
return HttpResponse::BadRequest()
.body("display_hash has no file in the original's library");
}
}
if let Err(e) = dao.set_display(&resolved.original_hash, wanted) {
warn!("put_display failed: {:?}", e);