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:
@@ -40,6 +40,19 @@ pub struct ListJobsParams {
|
||||
pub offset: Option<u32>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get, path = "/api/v1/jobs",
|
||||
security(("bearer_token" = [])),
|
||||
params(
|
||||
("status" = Option<String>, Query, description = "Status filter"),
|
||||
("limit" = Option<u32>, Query, description = "Page size"),
|
||||
("offset" = Option<u32>, Query, description = "Page offset")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Job list", body = JobListResponse),
|
||||
(status = 401, description = "Unauthorized")
|
||||
)
|
||||
)]
|
||||
pub async fn list_jobs(
|
||||
State(state): State<AppState>,
|
||||
claims: JwtClaims,
|
||||
@@ -59,6 +72,15 @@ pub async fn list_jobs(
|
||||
}))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post, path = "/api/v1/jobs",
|
||||
request_body = EnqueueJobRequest,
|
||||
security(("bearer_token" = [])),
|
||||
responses(
|
||||
(status = 201, description = "Job enqueued", body = JobResponse),
|
||||
(status = 401, description = "Unauthorized")
|
||||
)
|
||||
)]
|
||||
pub async fn enqueue_job(
|
||||
State(state): State<AppState>,
|
||||
claims: JwtClaims,
|
||||
@@ -82,6 +104,15 @@ pub async fn enqueue_job(
|
||||
Ok((StatusCode::CREATED, Json(JobResponse::from_domain(&job))))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post, path = "/api/v1/jobs/{id}/start",
|
||||
security(("bearer_token" = [])),
|
||||
params(("id" = uuid::Uuid, Path, description = "Job ID")),
|
||||
responses(
|
||||
(status = 200, description = "Job started", body = JobResponse),
|
||||
(status = 404, description = "Not found")
|
||||
)
|
||||
)]
|
||||
pub async fn start_job(
|
||||
State(state): State<AppState>,
|
||||
claims: JwtClaims,
|
||||
@@ -95,6 +126,16 @@ pub async fn start_job(
|
||||
Ok(Json(JobResponse::from_domain(&job)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post, path = "/api/v1/jobs/{id}/complete",
|
||||
request_body = CompleteJobRequest,
|
||||
security(("bearer_token" = [])),
|
||||
params(("id" = uuid::Uuid, Path, description = "Job ID")),
|
||||
responses(
|
||||
(status = 200, description = "Job completed", body = JobResponse),
|
||||
(status = 404, description = "Not found")
|
||||
)
|
||||
)]
|
||||
pub async fn complete_job(
|
||||
State(state): State<AppState>,
|
||||
claims: JwtClaims,
|
||||
@@ -110,6 +151,16 @@ pub async fn complete_job(
|
||||
Ok(Json(JobResponse::from_domain(&job)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post, path = "/api/v1/jobs/{id}/fail",
|
||||
request_body = FailJobRequest,
|
||||
security(("bearer_token" = [])),
|
||||
params(("id" = uuid::Uuid, Path, description = "Job ID")),
|
||||
responses(
|
||||
(status = 200, description = "Job failed", body = JobResponse),
|
||||
(status = 404, description = "Not found")
|
||||
)
|
||||
)]
|
||||
pub async fn fail_job(
|
||||
State(state): State<AppState>,
|
||||
claims: JwtClaims,
|
||||
@@ -125,6 +176,15 @@ pub async fn fail_job(
|
||||
Ok(Json(JobResponse::from_domain(&job)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get, path = "/api/v1/jobs/batches/{id}",
|
||||
security(("bearer_token" = [])),
|
||||
params(("id" = uuid::Uuid, Path, description = "Batch ID")),
|
||||
responses(
|
||||
(status = 200, description = "Batch progress", body = BatchProgressResponse),
|
||||
(status = 404, description = "Not found")
|
||||
)
|
||||
)]
|
||||
pub async fn batch_progress(
|
||||
State(state): State<AppState>,
|
||||
claims: JwtClaims,
|
||||
@@ -138,6 +198,15 @@ pub async fn batch_progress(
|
||||
Ok(Json(BatchProgressResponse::from_domain(&progress)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post, path = "/api/v1/plugins",
|
||||
request_body = ManagePluginRequest,
|
||||
security(("bearer_token" = [])),
|
||||
responses(
|
||||
(status = 201, description = "Plugin managed", body = PluginResponse),
|
||||
(status = 401, description = "Unauthorized")
|
||||
)
|
||||
)]
|
||||
pub async fn manage_plugin(
|
||||
State(state): State<AppState>,
|
||||
claims: JwtClaims,
|
||||
@@ -182,6 +251,15 @@ pub async fn manage_plugin(
|
||||
))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post, path = "/api/v1/pipelines",
|
||||
request_body = ConfigurePipelineRequest,
|
||||
security(("bearer_token" = [])),
|
||||
responses(
|
||||
(status = 201, description = "Pipeline configured", body = PipelineResponse),
|
||||
(status = 401, description = "Unauthorized")
|
||||
)
|
||||
)]
|
||||
pub async fn configure_pipeline(
|
||||
State(state): State<AppState>,
|
||||
claims: JwtClaims,
|
||||
|
||||
Reference in New Issue
Block a user