feat: add GET /libraries and library query param plumbing

New `/libraries` endpoint returns configured libraries so clients can
discover them. `FilesRequest` and `MemoriesRequest` gain an optional
`library` param (accepts name or numeric id). Unknown values are
rejected with 400; absent values span all libraries. `/memories`
now scopes its filesystem walk + EXIF query to the resolved library.
`MemoryItem` carries `library_id` so union-mode clients can render a
per-item source badge.

Behavior is unchanged in single-library mode: omitting `library` still
returns results from the primary library, which is the only one
configured until a second row is added to the libraries table.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Cameron
2026-04-17 15:35:14 -04:00
parent 00da97fe86
commit 08ac9a7695
5 changed files with 92 additions and 4 deletions

View File

@@ -235,8 +235,24 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
)
.to_string(),
),
KeyValue::new("library", req.library.clone().unwrap_or_default()),
]);
// Resolve the optional library filter. Unknown values return 400.
// For Phase 3 the filesystem walk still operates against a single
// library's root; Phase 4 introduces multi-root union scanning.
let library = match crate::libraries::resolve_library_param(
&app_state,
req.library.as_deref(),
) {
Ok(lib) => lib,
Err(msg) => {
log::warn!("Rejecting /photos request: {}", msg);
return HttpResponse::BadRequest().body(msg);
}
};
let scoped_library = library.unwrap_or_else(|| app_state.primary_library());
let span_context = opentelemetry::Context::current_with_span(span);
// Check if EXIF filtering is requested