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

@@ -91,6 +91,34 @@ pub struct RegisterAssetRequest {
pub file_size: u64,
}
// --- Bulk ---
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
pub struct BulkDeleteRequest {
pub asset_ids: Vec<uuid::Uuid>,
}
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
pub struct BulkTagRequest {
pub asset_ids: Vec<uuid::Uuid>,
pub tag_name: String,
}
// --- Album update ---
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
pub struct UpdateAlbumRequest {
pub title: Option<String>,
pub description: Option<String>,
}
// --- Stack reorder ---
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
pub struct ReorderStackRequest {
pub member_order: Vec<uuid::Uuid>,
}
// --- Stacks ---
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]

View File

@@ -87,7 +87,7 @@ impl AssetResponse {
#[derive(Debug, serde::Serialize, utoipa::ToSchema)]
pub struct TimelineResponse {
pub assets: Vec<AssetResponse>,
pub total: usize,
pub total: u64,
}
#[derive(Debug, serde::Serialize, utoipa::ToSchema)]