feat: frontend-ready backend — pagination, auto-derivatives, list endpoints, bulk ops, OpenAPI

Pagination: count_by_owner + count_search on AssetRepository,
timeline/search return real total count (not page len).

Auto-derivatives: worker enqueues GenerateDerivative when
ExtractMetadata job completes, closing the upload→thumbnail gap.

List endpoints: GET /albums, GET /stacks with user scoping.
ListAlbumsHandler, ListStacksHandler, find_by_owner on AssetStackRepository.

Tag filtering: tag_name field on AssetFilters, JOIN asset_tags+tags
in postgres search/count queries.

Bulk operations: POST /assets/bulk-delete, POST /assets/bulk-tag.

Album update: PUT /albums/{id} with UpdateAlbumHandler (title, description).

OpenAPI: utoipa annotations on all 47 endpoints + all request/response
schemas registered. Scalar UI at /scalar covers full API.
This commit is contained in:
2026-05-31 23:06:25 +02:00
parent bcaf49cc81
commit 7b5bb66b37
33 changed files with 1048 additions and 72 deletions

View File

@@ -26,8 +26,14 @@ fn protected_routes(state: &AppState) -> Router<AppState> {
.route("/auth/me", get(auth::me))
.route("/auth/logout", post(auth::logout))
// albums
.route("/albums", post(albums::create_album))
.route("/albums/{id}", get(albums::get_album))
.route(
"/albums",
get(albums::list_albums).post(albums::create_album),
)
.route(
"/albums/{id}",
get(albums::get_album).put(albums::update_album),
)
.route("/albums/{id}/entries", post(albums::add_entry))
.route(
"/albums/{id}/entries/{asset_id}",
@@ -49,8 +55,13 @@ fn protected_routes(state: &AppState) -> Router<AppState> {
get(assets::serve_derivative),
)
.route("/assets/{id}/tags", post(assets::tag_asset))
.route("/assets/bulk-delete", post(assets::bulk_delete))
.route("/assets/bulk-tag", post(assets::bulk_tag))
// stacks
.route("/stacks", post(stacks::create_stack))
.route(
"/stacks",
get(stacks::list_stacks).post(stacks::create_stack),
)
.route(
"/stacks/detect-live-photos",
post(stacks::detect_live_photos),