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:
@@ -8,7 +8,7 @@ use tracing::{error, info, warn};
|
||||
use application::processing::{EnqueueJobCommand, ProcessNextJobCommand};
|
||||
use domain::entities::JobType;
|
||||
use domain::events::DomainEvent;
|
||||
use domain::ports::EventConsumer;
|
||||
use domain::ports::{EventConsumer, JobRepository};
|
||||
use domain::value_objects::StructuredData;
|
||||
|
||||
mod config;
|
||||
@@ -70,6 +70,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
registry,
|
||||
event_pub.clone(),
|
||||
));
|
||||
let job_repo: Arc<dyn JobRepository> = repos.job.clone();
|
||||
let enqueue = Arc::new(build_enqueue_handler(&repos, event_pub));
|
||||
|
||||
// ── Shutdown signal ───────────────────────────────────────────────
|
||||
@@ -180,6 +181,27 @@ async fn main() -> anyhow::Result<()> {
|
||||
error!(error = %e, "event loop: failed to enqueue SyncSidecar");
|
||||
}
|
||||
}
|
||||
DomainEvent::JobCompleted { job_id, .. } => {
|
||||
info!(job_id = %job_id, "event loop: JobCompleted → check derivative generation");
|
||||
(envelope.ack)();
|
||||
// Look up the job to see if it was ExtractMetadata
|
||||
if let Ok(Some(job)) = job_repo.find_by_id(job_id).await
|
||||
&& job.job_type == JobType::ExtractMetadata
|
||||
&& let Some(asset_id) = job.target_asset_id
|
||||
{
|
||||
info!(asset_id = %asset_id, "event loop: ExtractMetadata done → enqueue GenerateDerivative");
|
||||
let cmd = EnqueueJobCommand {
|
||||
job_type: JobType::GenerateDerivative,
|
||||
priority: 5,
|
||||
payload: StructuredData::new(),
|
||||
target_asset_id: Some(asset_id),
|
||||
batch_id: None,
|
||||
};
|
||||
if let Err(e) = enqueue.execute(cmd).await {
|
||||
error!(error = %e, "event loop: failed to enqueue GenerateDerivative");
|
||||
}
|
||||
}
|
||||
}
|
||||
DomainEvent::JobEnqueued {
|
||||
job_id, job_type, ..
|
||||
} => {
|
||||
|
||||
Reference in New Issue
Block a user