feat: serve derivative files via GET /assets/{id}/derivatives/{profile}
- ReadDerivativeHandler queries DerivativeRepository + FileStoragePort - Profile URL param: thumbnail, thumbnail_large, web_optimized, video_sd - Immutable cache headers (derivatives don't change once generated) - Wired into bootstrap catalog service builder
This commit is contained in:
@@ -10,8 +10,8 @@ use api_types::{
|
||||
};
|
||||
use application::{
|
||||
catalog::{
|
||||
GetAssetQuery, GetTimelineQuery, ReadAssetFileQuery, RegisterAssetCommand,
|
||||
UpdateMetadataCommand,
|
||||
GetAssetQuery, GetTimelineQuery, ReadAssetFileQuery, ReadDerivativeQuery,
|
||||
RegisterAssetCommand, UpdateMetadataCommand,
|
||||
},
|
||||
organization::TagAssetCommand,
|
||||
storage::IngestAssetCommand,
|
||||
@@ -152,6 +152,40 @@ pub async fn tag_asset(
|
||||
Ok((StatusCode::CREATED, Json(TagResponse::from_domain(&tag))))
|
||||
}
|
||||
|
||||
pub async fn serve_derivative(
|
||||
State(state): State<AppState>,
|
||||
_claims: JwtClaims,
|
||||
Path((asset_id, profile)): Path<(uuid::Uuid, String)>,
|
||||
) -> Result<Response, AppError> {
|
||||
let profile = parse_derivative_profile(&profile)?;
|
||||
let query = ReadDerivativeQuery {
|
||||
asset_id: SystemId::from_uuid(asset_id),
|
||||
profile,
|
||||
};
|
||||
let result = state.catalog.read_derivative.execute(query).await?;
|
||||
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(header::CONTENT_TYPE, &result.mime_type)
|
||||
.header(header::CONTENT_LENGTH, result.data.len())
|
||||
.header(header::CACHE_CONTROL, "public, max-age=31536000, immutable")
|
||||
.body(Body::from(result.data))
|
||||
.map_err(|e| AppError::from(DomainError::Internal(e.to_string())))
|
||||
}
|
||||
|
||||
fn parse_derivative_profile(s: &str) -> Result<domain::entities::DerivativeProfile, AppError> {
|
||||
use domain::entities::DerivativeProfile;
|
||||
match s {
|
||||
"thumbnail" | "thumbnail_square" => Ok(DerivativeProfile::ThumbnailSquare),
|
||||
"thumbnail_large" => Ok(DerivativeProfile::ThumbnailLarge),
|
||||
"web" | "web_optimized" => Ok(DerivativeProfile::WebOptimized),
|
||||
"video_sd" => Ok(DerivativeProfile::VideoSd),
|
||||
_ => Err(AppError::from(DomainError::Validation(format!(
|
||||
"Unknown derivative profile: {s}"
|
||||
)))),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_asset_type(s: &str) -> Result<AssetType, AppError> {
|
||||
match s {
|
||||
"image" => Ok(AssetType::Image),
|
||||
|
||||
@@ -29,6 +29,10 @@ pub fn api_v1_router() -> Router<AppState> {
|
||||
.route("/assets/{id}", get(assets::get_asset))
|
||||
.route("/assets/{id}/metadata", put(assets::update_metadata))
|
||||
.route("/assets/{id}/file", get(assets::serve_file))
|
||||
.route(
|
||||
"/assets/{id}/derivatives/{profile}",
|
||||
get(assets::serve_derivative),
|
||||
)
|
||||
.route("/assets/{id}/tags", post(assets::tag_asset))
|
||||
// sharing
|
||||
.route("/sharing", post(sharing::share_resource))
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::sync::Arc;
|
||||
|
||||
use application::{
|
||||
catalog::{
|
||||
GetAssetHandler, GetTimelineHandler, ReadAssetFileHandler, RegisterAssetHandler,
|
||||
UpdateMetadataHandler,
|
||||
GetAssetHandler, GetTimelineHandler, ReadAssetFileHandler, ReadDerivativeHandler,
|
||||
RegisterAssetHandler, UpdateMetadataHandler,
|
||||
},
|
||||
identity::{GetProfileHandler, LoginUserHandler, RegisterUserHandler},
|
||||
organization::{
|
||||
@@ -41,6 +41,7 @@ pub struct CatalogHandlers {
|
||||
pub get_timeline: Arc<GetTimelineHandler>,
|
||||
pub update_metadata: Arc<UpdateMetadataHandler>,
|
||||
pub read_asset_file: Arc<ReadAssetFileHandler>,
|
||||
pub read_derivative: Arc<ReadDerivativeHandler>,
|
||||
pub register_asset: Arc<RegisterAssetHandler>,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user