From 2d9dd2c2d0c6f0ced78ac14160405fd65b7ae559 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 31 May 2026 06:14:19 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20clean=20up=20presentation=20layer?= =?UTF-8?q?=20=E2=80=94=20AppState=20grouping,=20multipart=20extractor,=20?= =?UTF-8?q?thin=20handlers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/api-types/src/responses.rs | 17 +-- .../tests/catalog/queries/get_asset.rs | 8 +- .../tests/storage/commands/ingest_asset.rs | 1 - crates/bootstrap/src/factory.rs | 67 ++++---- crates/presentation/src/constants.rs | 3 + crates/presentation/src/extractors/mod.rs | 2 + .../src/extractors/multipart_upload.rs | 74 +++++++++ crates/presentation/src/handlers/albums.rs | 8 +- crates/presentation/src/handlers/assets.rs | 144 ++++-------------- crates/presentation/src/handlers/auth.rs | 6 +- crates/presentation/src/handlers/storage.rs | 4 +- .../src/handlers/storage_example.rs | 27 ---- crates/presentation/src/lib.rs | 1 + crates/presentation/src/state.rs | 95 +++++------- 14 files changed, 199 insertions(+), 258 deletions(-) create mode 100644 crates/presentation/src/constants.rs create mode 100644 crates/presentation/src/extractors/multipart_upload.rs delete mode 100644 crates/presentation/src/handlers/storage_example.rs diff --git a/crates/api-types/src/responses.rs b/crates/api-types/src/responses.rs index 552896a..ff91aee 100644 --- a/crates/api-types/src/responses.rs +++ b/crates/api-types/src/responses.rs @@ -68,22 +68,7 @@ impl AssetResponse { let meta_map = metadata .inner() .iter() - .map(|(k, v)| { - let json_val = match v { - domain::value_objects::MetadataValue::String(s) => { - serde_json::Value::String(s.clone()) - } - domain::value_objects::MetadataValue::Integer(i) => { - serde_json::json!(*i) - } - domain::value_objects::MetadataValue::Float(f) => { - serde_json::json!(*f) - } - domain::value_objects::MetadataValue::Boolean(b) => serde_json::Value::Bool(*b), - domain::value_objects::MetadataValue::Null => serde_json::Value::Null, - }; - (k.clone(), json_val) - }) + .map(|(k, v)| (k.clone(), serde_json::Value::from(v))) .collect(); Self { diff --git a/crates/application/tests/catalog/queries/get_asset.rs b/crates/application/tests/catalog/queries/get_asset.rs index c4f197e..174ca2f 100644 --- a/crates/application/tests/catalog/queries/get_asset.rs +++ b/crates/application/tests/catalog/queries/get_asset.rs @@ -17,13 +17,7 @@ async fn returns_asset_with_resolved_metadata() { checksum: Checksum::new("a".repeat(64)).unwrap(), }; let owner = SystemId::new(); - let asset = Asset::new( - source, - AssetType::Image, - "image/jpeg", - 1024, - owner, - ); + let asset = Asset::new(source, AssetType::Image, "image/jpeg", 1024, owner); asset_repo.save(&asset).await.unwrap(); // Add exif layer diff --git a/crates/application/tests/storage/commands/ingest_asset.rs b/crates/application/tests/storage/commands/ingest_asset.rs index 2b5cd49..fb76d33 100644 --- a/crates/application/tests/storage/commands/ingest_asset.rs +++ b/crates/application/tests/storage/commands/ingest_asset.rs @@ -77,7 +77,6 @@ impl Harness { } } - #[tokio::test] async fn ingests_successfully() { let h = Harness::new(); diff --git a/crates/bootstrap/src/factory.rs b/crates/bootstrap/src/factory.rs index 3df2a4f..121ead0 100644 --- a/crates/bootstrap/src/factory.rs +++ b/crates/bootstrap/src/factory.rs @@ -16,15 +16,18 @@ use adapters_postgres::{ connect, run_migrations, }; -use adapters_storage::{LocalFileStorage, ObjectStorageAdapter, StorageConfig, build_store}; +use adapters_storage::LocalFileStorage; use application::{ - catalog::{GetAssetHandler, GetTimelineHandler, UpdateMetadataHandler}, + catalog::{GetAssetHandler, GetTimelineHandler, ReadAssetFileHandler, UpdateMetadataHandler}, identity::{GetProfileHandler, LoginUserHandler, RegisterUserHandler}, organization::{CreateAlbumHandler, GetAlbumHandler, ManageAlbumEntriesHandler}, storage::{IngestAssetHandler, RegisterLibraryPathHandler, RegisterVolumeHandler}, }; -use presentation::{routes::app_router, state::AppState}; +use presentation::{ + routes::app_router, + state::{AppState, CatalogHandlers, IdentityHandlers, OrganizationHandlers, StorageHandlers}, +}; use crate::config::Config; use crate::log_event_publisher::LogEventPublisher; @@ -46,11 +49,6 @@ pub async fn build_app(config: &Config) -> Result { )); let get_profile_handler = Arc::new(GetProfileHandler::new(user_repo)); - // Object storage - let storage_cfg = StorageConfig::from_env()?; - let store = build_store(&storage_cfg)?; - let storage = Arc::new(ObjectStorageAdapter::new(store, &storage_cfg.prefix)?); - // Repos let album_repo = Arc::new(PostgresAlbumRepository::new(pool.clone())); let asset_repo = Arc::new(PostgresAssetRepository::new(pool.clone())); @@ -62,7 +60,7 @@ pub async fn build_app(config: &Config) -> Result { let ledger_repo = Arc::new(PostgresUsageLedgerRepository::new(pool.clone())); let event_publisher: Arc = Arc::new(LogEventPublisher); - // File storage for ingest + // File storage let storage_path = std::env::var("STORAGE_PATH").unwrap_or_else(|_| "./data/media".to_string()); let file_storage: Arc = Arc::new(LocalFileStorage::new(&storage_path)); @@ -94,30 +92,45 @@ pub async fn build_app(config: &Config) -> Result { metadata_repo, event_publisher, )); + let read_asset_file_handler = Arc::new(ReadAssetFileHandler::new(asset_repo, file_storage)); // Storage handlers let register_volume_handler = Arc::new(RegisterVolumeHandler::new(volume_repo.clone())); let register_library_path_handler = Arc::new(RegisterLibraryPathHandler::new(volume_repo, path_repo)); - let state = AppState::new( - register_handler, - login_handler, - get_profile_handler, - issuer, - storage, - create_album_handler, - get_album_handler, - manage_album_entries_handler, - ingest_asset_handler, - get_asset_handler, - get_timeline_handler, - update_metadata_handler, - register_volume_handler, - register_library_path_handler, - file_storage, - asset_repo, - ); + let identity = IdentityHandlers { + register: register_handler, + login: login_handler, + get_profile: get_profile_handler, + }; + + let catalog = CatalogHandlers { + ingest_asset: ingest_asset_handler, + get_asset: get_asset_handler, + get_timeline: get_timeline_handler, + update_metadata: update_metadata_handler, + read_asset_file: read_asset_file_handler, + }; + + let organization = OrganizationHandlers { + create_album: create_album_handler, + get_album: get_album_handler, + manage_album_entries: manage_album_entries_handler, + }; + + let storage_handlers = StorageHandlers { + register_volume: register_volume_handler, + register_library_path: register_library_path_handler, + }; + + let state = AppState { + identity, + catalog, + organization, + storage: storage_handlers, + token_issuer: issuer, + }; let cors = CorsLayer::new() .allow_origin( diff --git a/crates/presentation/src/constants.rs b/crates/presentation/src/constants.rs new file mode 100644 index 0000000..4947471 --- /dev/null +++ b/crates/presentation/src/constants.rs @@ -0,0 +1,3 @@ +pub const DEFAULT_PAGE_SIZE: u32 = 50; +pub const MAX_PAGE_SIZE: u32 = 1000; +pub const DEFAULT_DEVICE_ID: &str = "web"; diff --git a/crates/presentation/src/extractors/mod.rs b/crates/presentation/src/extractors/mod.rs index f7e87be..b51fc15 100644 --- a/crates/presentation/src/extractors/mod.rs +++ b/crates/presentation/src/extractors/mod.rs @@ -1,5 +1,7 @@ pub mod auth; pub mod json; +pub mod multipart_upload; pub use auth::JwtClaims; pub use json::ValidatedJson; +pub use multipart_upload::UploadedAsset; diff --git a/crates/presentation/src/extractors/multipart_upload.rs b/crates/presentation/src/extractors/multipart_upload.rs new file mode 100644 index 0000000..3ab8787 --- /dev/null +++ b/crates/presentation/src/extractors/multipart_upload.rs @@ -0,0 +1,74 @@ +use axum::extract::Multipart; +use bytes::Bytes; +use domain::errors::DomainError; +use domain::value_objects::SystemId; + +use crate::constants::DEFAULT_DEVICE_ID; +use crate::errors::AppError; + +pub struct UploadedAsset { + pub filename: String, + pub data: Bytes, + pub target_path_id: SystemId, + pub client_device_id: String, +} + +impl UploadedAsset { + pub async fn from_multipart(mut multipart: Multipart) -> Result { + let mut file_data: Option = None; + let mut filename: Option = None; + let mut target_path_id: Option = None; + let mut client_device_id = DEFAULT_DEVICE_ID.to_string(); + + while let Some(field) = multipart + .next_field() + .await + .map_err(|e| AppError::from(DomainError::Validation(e.to_string())))? + { + let name = field.name().unwrap_or("").to_string(); + match name.as_str() { + "file" => { + filename = field.file_name().map(|s| s.to_string()); + file_data = Some( + field + .bytes() + .await + .map_err(|e| AppError::from(DomainError::Internal(e.to_string())))?, + ); + } + "target_path_id" => { + let text = field + .text() + .await + .map_err(|e| AppError::from(DomainError::Validation(e.to_string())))?; + target_path_id = Some( + text.parse::() + .map_err(|e| AppError::from(DomainError::Validation(e.to_string())))?, + ); + } + "client_device_id" => { + client_device_id = field + .text() + .await + .map_err(|e| AppError::from(DomainError::Validation(e.to_string())))?; + } + _ => {} + } + } + + let data = file_data + .ok_or_else(|| AppError::from(DomainError::Validation("Missing file field".into())))?; + let fname = filename + .ok_or_else(|| AppError::from(DomainError::Validation("Missing filename".into())))?; + let path_id = target_path_id.ok_or_else(|| { + AppError::from(DomainError::Validation("Missing target_path_id".into())) + })?; + + Ok(Self { + filename: fname, + data, + target_path_id: SystemId::from_uuid(path_id), + client_device_id, + }) + } +} diff --git a/crates/presentation/src/handlers/albums.rs b/crates/presentation/src/handlers/albums.rs index d80e20a..93722ef 100644 --- a/crates/presentation/src/handlers/albums.rs +++ b/crates/presentation/src/handlers/albums.rs @@ -22,7 +22,7 @@ pub async fn create_album( title: req.title, creator_id: claims.user_id, }; - let album = state.create_album_handler.execute(cmd).await?; + let album = state.organization.create_album.execute(cmd).await?; Ok(( StatusCode::CREATED, Json(AlbumResponse::from_domain(&album)), @@ -38,7 +38,7 @@ pub async fn get_album( album_id: SystemId::from_uuid(album_id), user_id: claims.user_id, }; - let album = state.get_album_handler.execute(query).await?; + let album = state.organization.get_album.execute(query).await?; Ok(Json(AlbumResponse::from_domain(&album))) } @@ -55,7 +55,7 @@ pub async fn add_entry( }, user_id: claims.user_id, }; - let album = state.manage_album_entries_handler.execute(cmd).await?; + let album = state.organization.manage_album_entries.execute(cmd).await?; Ok((StatusCode::OK, Json(AlbumResponse::from_domain(&album)))) } @@ -71,6 +71,6 @@ pub async fn remove_entry( }, user_id: claims.user_id, }; - let album = state.manage_album_entries_handler.execute(cmd).await?; + let album = state.organization.manage_album_entries.execute(cmd).await?; Ok(Json(AlbumResponse::from_domain(&album))) } diff --git a/crates/presentation/src/handlers/assets.rs b/crates/presentation/src/handlers/assets.rs index 2b75157..761f1ea 100644 --- a/crates/presentation/src/handlers/assets.rs +++ b/crates/presentation/src/handlers/assets.rs @@ -1,10 +1,12 @@ -use crate::{errors::AppError, extractors::JwtClaims, state::AppState}; -use api_types::{ - requests::UpdateMetadataRequest, - responses::{AssetResponse, IngestResponse, TimelineResponse}, +use crate::{ + constants::{DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE}, + errors::AppError, + extractors::{JwtClaims, UploadedAsset}, + state::AppState, }; +use api_types::responses::{AssetResponse, IngestResponse, TimelineResponse}; use application::{ - catalog::{GetAssetQuery, GetTimelineQuery, UpdateMetadataCommand}, + catalog::{GetAssetQuery, GetTimelineQuery, ReadAssetFileQuery, UpdateMetadataCommand}, storage::IngestAssetCommand, }; use axum::{ @@ -25,78 +27,25 @@ pub struct TimelineParams { pub async fn ingest( State(state): State, claims: JwtClaims, - mut multipart: Multipart, + multipart: Multipart, ) -> Result<(StatusCode, Json), AppError> { - let mut file_data: Option = None; - let mut filename: Option = None; - let mut target_path_id: Option = None; - let mut client_device_id = "web".to_string(); - - while let Some(field) = multipart - .next_field() - .await - .map_err(|e| AppError::from(domain::errors::DomainError::Validation(e.to_string())))? - { - let name = field.name().unwrap_or("").to_string(); - match name.as_str() { - "file" => { - filename = field.file_name().map(|s| s.to_string()); - let data = field.bytes().await.map_err(|e| { - AppError::from(domain::errors::DomainError::Internal(e.to_string())) - })?; - file_data = Some(data); - } - "target_path_id" => { - let text = field.text().await.map_err(|e| { - AppError::from(domain::errors::DomainError::Validation(e.to_string())) - })?; - target_path_id = Some(text.parse::().map_err(|e| { - AppError::from(domain::errors::DomainError::Validation(e.to_string())) - })?); - } - "client_device_id" => { - client_device_id = field.text().await.map_err(|e| { - AppError::from(domain::errors::DomainError::Validation(e.to_string())) - })?; - } - _ => {} - } - } - - let data = file_data.ok_or_else(|| { - AppError::from(domain::errors::DomainError::Validation( - "Missing file field".to_string(), - )) - })?; - let fname = filename.ok_or_else(|| { - AppError::from(domain::errors::DomainError::Validation( - "Missing filename".to_string(), - )) - })?; - let path_id = target_path_id.ok_or_else(|| { - AppError::from(domain::errors::DomainError::Validation( - "Missing target_path_id".to_string(), - )) - })?; - - let file_size = data.len() as u64; + let upload = UploadedAsset::from_multipart(multipart).await?; let cmd = IngestAssetCommand { uploader_id: claims.user_id, - client_device_id, - filename: fname, - target_path_id: SystemId::from_uuid(path_id), - file_size, - data, + client_device_id: upload.client_device_id, + filename: upload.filename, + target_path_id: upload.target_path_id, + file_size: upload.data.len() as u64, + data: upload.data, }; - let (asset, session) = state.ingest_asset_handler.execute(cmd).await?; - let empty_meta = StructuredData::new(); + let (asset, session) = state.catalog.ingest_asset.execute(cmd).await?; Ok(( StatusCode::CREATED, Json(IngestResponse { - asset: AssetResponse::from_domain(&asset, &empty_meta), + asset: AssetResponse::from_domain(&asset, &StructuredData::new()), session_id: *session.session_id.as_uuid(), }), )) @@ -111,7 +60,7 @@ pub async fn get_asset( asset_id: SystemId::from_uuid(asset_id), user_id: claims.user_id, }; - let (asset, metadata) = state.get_asset_handler.execute(query).await?; + let (asset, metadata) = state.catalog.get_asset.execute(query).await?; Ok(Json(AssetResponse::from_domain(&asset, &metadata))) } @@ -122,10 +71,10 @@ pub async fn timeline( ) -> Result, AppError> { let query = GetTimelineQuery { owner_id: claims.user_id, - limit: params.limit.unwrap_or(50), + limit: params.limit.unwrap_or(DEFAULT_PAGE_SIZE).min(MAX_PAGE_SIZE), offset: params.offset.unwrap_or(0), }; - let results = state.get_timeline_handler.execute(query).await?; + let results = state.catalog.get_timeline.execute(query).await?; let total = results.len(); let assets = results .iter() @@ -138,26 +87,11 @@ pub async fn update_metadata( State(state): State, claims: JwtClaims, Path((asset_id,)): Path<(uuid::Uuid,)>, - Json(req): Json, + Json(req): Json, ) -> Result, AppError> { let mut data = StructuredData::new(); for (k, v) in req.data { - let mv = match v { - serde_json::Value::String(s) => MetadataValue::String(s), - serde_json::Value::Number(n) => { - if let Some(i) = n.as_i64() { - MetadataValue::Integer(i) - } else if let Some(f) = n.as_f64() { - MetadataValue::Float(f) - } else { - MetadataValue::Null - } - } - serde_json::Value::Bool(b) => MetadataValue::Boolean(b), - serde_json::Value::Null => MetadataValue::Null, - _ => MetadataValue::String(v.to_string()), - }; - data.insert(k, mv); + data.insert(k, MetadataValue::from(v)); } let cmd = UpdateMetadataCommand { @@ -165,7 +99,7 @@ pub async fn update_metadata( user_id: claims.user_id, data, }; - state.update_metadata_handler.execute(cmd).await?; + state.catalog.update_metadata.execute(cmd).await?; Ok(Json(serde_json::json!({ "status": "updated" }))) } @@ -174,33 +108,19 @@ pub async fn serve_file( _claims: JwtClaims, Path((asset_id,)): Path<(uuid::Uuid,)>, ) -> Result { - let asset = state - .asset_repo - .find_by_id(&SystemId::from_uuid(asset_id)) - .await? - .ok_or_else(|| domain::errors::DomainError::NotFound("Asset not found".into()))?; + let query = ReadAssetFileQuery { + asset_id: SystemId::from_uuid(asset_id), + }; + let result = state.catalog.read_asset_file.execute(query).await?; - let data = state - .file_storage - .read_file(&asset.source_reference.relative_path) - .await?; - - Ok(Response::builder() + Response::builder() .status(StatusCode::OK) - .header(header::CONTENT_TYPE, &asset.mime_type) - .header(header::CONTENT_LENGTH, data.len()) + .header(header::CONTENT_TYPE, &result.mime_type) + .header(header::CONTENT_LENGTH, result.data.len()) .header( header::CONTENT_DISPOSITION, - format!( - "inline; filename=\"{}\"", - asset - .source_reference - .relative_path - .rsplit('/') - .next() - .unwrap_or("file") - ), + format!("inline; filename=\"{}\"", result.filename), ) - .body(Body::from(data)) - .unwrap()) + .body(Body::from(result.data)) + .map_err(|e| AppError::from(domain::errors::DomainError::Internal(e.to_string()))) } diff --git a/crates/presentation/src/handlers/auth.rs b/crates/presentation/src/handlers/auth.rs index 9fd96bd..d7c86d1 100644 --- a/crates/presentation/src/handlers/auth.rs +++ b/crates/presentation/src/handlers/auth.rs @@ -28,7 +28,7 @@ pub async fn register( email: req.email, password: req.password, }; - let user = state.register_handler.execute(cmd).await?; + let user = state.identity.register.execute(cmd).await?; let token = state .token_issuer .issue(&user.id, "user") @@ -59,7 +59,7 @@ pub async fn login( email: req.email, password: req.password, }; - let (user, token) = state.login_handler.execute(cmd).await?; + let (user, token) = state.identity.login.execute(cmd).await?; Ok(Json(AuthResponse { token, user: UserResponse::from_domain(&user), @@ -81,6 +81,6 @@ pub async fn me( let query = GetProfileQuery { user_id: claims.user_id, }; - let user = state.get_profile_handler.execute(query).await?; + let user = state.identity.get_profile.execute(query).await?; Ok(Json(UserResponse::from_domain(&user))) } diff --git a/crates/presentation/src/handlers/storage.rs b/crates/presentation/src/handlers/storage.rs index e5d6997..37aa5e6 100644 --- a/crates/presentation/src/handlers/storage.rs +++ b/crates/presentation/src/handlers/storage.rs @@ -17,7 +17,7 @@ pub async fn register_volume( uri_prefix: req.uri_prefix, is_writable: req.is_writable, }; - let volume = state.register_volume_handler.execute(cmd).await?; + let volume = state.storage.register_volume.execute(cmd).await?; Ok(( StatusCode::CREATED, Json(VolumeResponse::from_domain(&volume)), @@ -35,7 +35,7 @@ pub async fn register_library_path( owner_id: SystemId::from_uuid(req.owner_id), is_ingest_destination: req.is_ingest_destination, }; - let path = state.register_library_path_handler.execute(cmd).await?; + let path = state.storage.register_library_path.execute(cmd).await?; Ok(( StatusCode::CREATED, Json(LibraryPathResponse::from_domain(&path)), diff --git a/crates/presentation/src/handlers/storage_example.rs b/crates/presentation/src/handlers/storage_example.rs deleted file mode 100644 index 19aac8a..0000000 --- a/crates/presentation/src/handlers/storage_example.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Example: stream a stored file as an HTTP response. -// Remove this file or replace with your own handlers. -// -// To use, add to your router: -// .route("/files/*key", get(storage_example::get_file)) -// -// use axum::{ -// body::Body, -// extract::{Path, State}, -// http::StatusCode, -// response::IntoResponse, -// }; -// use futures::StreamExt; -// use crate::state::AppState; -// -// pub async fn get_file( -// Path(key): Path, -// State(state): State, -// ) -> Result { -// let stream = state -// .storage -// .get(&key) -// .await -// .map_err(|_| StatusCode::NOT_FOUND)?; -// let body = Body::from_stream(stream.map(|r| r.map_err(|e| e.to_string()))); -// Ok(body) -// } diff --git a/crates/presentation/src/lib.rs b/crates/presentation/src/lib.rs index fa5838a..719f9b2 100644 --- a/crates/presentation/src/lib.rs +++ b/crates/presentation/src/lib.rs @@ -1,3 +1,4 @@ +pub mod constants; pub mod errors; pub mod extractors; pub mod handlers; diff --git a/crates/presentation/src/state.rs b/crates/presentation/src/state.rs index 013e350..f9d6bad 100644 --- a/crates/presentation/src/state.rs +++ b/crates/presentation/src/state.rs @@ -1,70 +1,47 @@ +use std::sync::Arc; + use application::{ - catalog::{GetAssetHandler, GetTimelineHandler, UpdateMetadataHandler}, + catalog::{GetAssetHandler, GetTimelineHandler, ReadAssetFileHandler, UpdateMetadataHandler}, identity::{GetProfileHandler, LoginUserHandler, RegisterUserHandler}, organization::{CreateAlbumHandler, GetAlbumHandler, ManageAlbumEntriesHandler}, storage::{IngestAssetHandler, RegisterLibraryPathHandler, RegisterVolumeHandler}, }; -use std::sync::Arc; +use domain::ports::TokenIssuer; -use domain::ports::{AssetRepository, FileStoragePort, StoragePort, TokenIssuer}; +#[derive(Clone)] +pub struct IdentityHandlers { + pub register: Arc, + pub login: Arc, + pub get_profile: Arc, +} + +#[derive(Clone)] +pub struct CatalogHandlers { + pub ingest_asset: Arc, + pub get_asset: Arc, + pub get_timeline: Arc, + pub update_metadata: Arc, + pub read_asset_file: Arc, +} + +#[derive(Clone)] +pub struct OrganizationHandlers { + pub create_album: Arc, + pub get_album: Arc, + pub manage_album_entries: Arc, +} + +#[derive(Clone)] +pub struct StorageHandlers { + pub register_volume: Arc, + pub register_library_path: Arc, +} #[derive(Clone)] pub struct AppState { - pub register_handler: Arc, - pub login_handler: Arc, - pub get_profile_handler: Arc, + pub identity: IdentityHandlers, + pub catalog: CatalogHandlers, + pub organization: OrganizationHandlers, + pub storage: StorageHandlers, pub token_issuer: Arc, - pub storage: Arc, - pub create_album_handler: Arc, - pub get_album_handler: Arc, - pub manage_album_entries_handler: Arc, - pub ingest_asset_handler: Arc, - pub get_asset_handler: Arc, - pub get_timeline_handler: Arc, - pub update_metadata_handler: Arc, - pub register_volume_handler: Arc, - pub register_library_path_handler: Arc, - pub file_storage: Arc, - pub asset_repo: Arc, -} - -impl AppState { - #[allow(clippy::too_many_arguments)] - pub fn new( - register_handler: Arc, - login_handler: Arc, - get_profile_handler: Arc, - token_issuer: Arc, - storage: Arc, - create_album_handler: Arc, - get_album_handler: Arc, - manage_album_entries_handler: Arc, - ingest_asset_handler: Arc, - get_asset_handler: Arc, - get_timeline_handler: Arc, - update_metadata_handler: Arc, - register_volume_handler: Arc, - register_library_path_handler: Arc, - file_storage: Arc, - asset_repo: Arc, - ) -> Self { - Self { - register_handler, - login_handler, - get_profile_handler, - token_issuer, - storage, - create_album_handler, - get_album_handler, - manage_album_entries_handler, - ingest_asset_handler, - get_asset_handler, - get_timeline_handler, - update_metadata_handler, - register_volume_handler, - register_library_path_handler, - file_storage, - asset_repo, - } - } }