feat: wire remaining handlers — tag, quota, register asset, sidecar, processing

14 new endpoints: POST tags, GET quota, POST register, 6 sidecar, 7 processing.
DTOs, AppState groups, LogSidecarWriter, full bootstrap wiring.
This commit is contained in:
2026-05-31 11:04:22 +02:00
parent 19be8c2adf
commit a6b86c23d8
14 changed files with 865 additions and 30 deletions

View File

@@ -4,9 +4,16 @@ use crate::{
extractors::{JwtClaims, UploadedAsset},
state::AppState,
};
use api_types::responses::{AssetResponse, IngestResponse, TimelineResponse};
use api_types::{
requests::{RegisterAssetRequest, TagAssetRequest},
responses::{AssetResponse, IngestResponse, TagResponse, TimelineResponse},
};
use application::{
catalog::{GetAssetQuery, GetTimelineQuery, ReadAssetFileQuery, UpdateMetadataCommand},
catalog::{
GetAssetQuery, GetTimelineQuery, ReadAssetFileQuery, RegisterAssetCommand,
UpdateMetadataCommand,
},
organization::TagAssetCommand,
storage::IngestAssetCommand,
};
use axum::{
@@ -16,7 +23,11 @@ use axum::{
http::{StatusCode, header},
response::Response,
};
use domain::value_objects::{MetadataValue, StructuredData, SystemId};
use domain::{
catalog::entities::AssetType,
errors::DomainError,
value_objects::{MetadataValue, StructuredData, SystemId},
};
#[derive(Debug, serde::Deserialize)]
pub struct TimelineParams {
@@ -124,3 +135,51 @@ pub async fn serve_file(
.body(Body::from(result.data))
.map_err(|e| AppError::from(domain::errors::DomainError::Internal(e.to_string())))
}
pub async fn tag_asset(
State(state): State<AppState>,
claims: JwtClaims,
Path((asset_id,)): Path<(uuid::Uuid,)>,
Json(req): Json<TagAssetRequest>,
) -> Result<(StatusCode, Json<TagResponse>), AppError> {
let cmd = TagAssetCommand {
asset_id: SystemId::from_uuid(asset_id),
tag_name: req.tag_name,
user_id: claims.user_id,
};
let (tag, _asset_tag) = state.organization.tag_asset.execute(cmd).await?;
Ok((StatusCode::CREATED, Json(TagResponse::from_domain(&tag))))
}
fn parse_asset_type(s: &str) -> Result<AssetType, AppError> {
match s {
"image" => Ok(AssetType::Image),
"video" => Ok(AssetType::Video),
"live_photo" => Ok(AssetType::LivePhoto),
_ => Err(AppError::from(DomainError::Validation(format!(
"Invalid asset type: {s}"
)))),
}
}
pub async fn register_asset(
State(state): State<AppState>,
claims: JwtClaims,
Json(req): Json<RegisterAssetRequest>,
) -> Result<(StatusCode, Json<AssetResponse>), AppError> {
let asset_type = parse_asset_type(&req.asset_type)?;
let cmd = RegisterAssetCommand {
volume_id: SystemId::from_uuid(req.volume_id),
relative_path: req.relative_path,
checksum: req.checksum,
asset_type,
mime_type: req.mime_type,
file_size: req.file_size,
owner_id: claims.user_id,
};
let (asset, _dup_group) = state.catalog.register_asset.execute(cmd).await?;
Ok((
StatusCode::CREATED,
Json(AssetResponse::from_domain(&asset, &StructuredData::new())),
))
}