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.
43 lines
997 B
Rust
43 lines
997 B
Rust
use std::sync::Arc;
|
|
|
|
use domain::{
|
|
entities::{Asset, AssetFilters},
|
|
errors::DomainError,
|
|
ports::AssetRepository,
|
|
value_objects::SystemId,
|
|
};
|
|
|
|
pub struct SearchAssetsQuery {
|
|
pub owner_id: SystemId,
|
|
pub filters: AssetFilters,
|
|
pub limit: u32,
|
|
pub offset: u32,
|
|
}
|
|
|
|
pub struct SearchResult {
|
|
pub items: Vec<Asset>,
|
|
pub total: u64,
|
|
}
|
|
|
|
pub struct SearchAssetsHandler {
|
|
asset_repo: Arc<dyn AssetRepository>,
|
|
}
|
|
|
|
impl SearchAssetsHandler {
|
|
pub fn new(asset_repo: Arc<dyn AssetRepository>) -> Self {
|
|
Self { asset_repo }
|
|
}
|
|
|
|
pub async fn execute(&self, query: SearchAssetsQuery) -> Result<SearchResult, DomainError> {
|
|
let total = self
|
|
.asset_repo
|
|
.count_search(&query.owner_id, &query.filters)
|
|
.await?;
|
|
let items = self
|
|
.asset_repo
|
|
.search(&query.owner_id, &query.filters, query.limit, query.offset)
|
|
.await?;
|
|
Ok(SearchResult { items, total })
|
|
}
|
|
}
|