perf: Arc<AppState>, streaming PROPFIND, spawn_blocking SHA-256
- Issue #4: Wrap AppState in Arc — eliminates 42 Arc::clone + 16 String::clone per request - Issue #2: Reject Depth:infinity with 403 + streaming XML with paginated DB queries - Issue #5: Move chunked upload assembly (SHA-256 hash-on-write) to spawn_blocking - Remove ~270 lines dead code from di.rs (unused builders, Default impl, stubs) - Clean up unused tokio imports in chunked_upload_service.rs
This commit is contained in:
+2
-319
@@ -45,11 +45,7 @@ use crate::infrastructure::services::file_system_i18n_service::FileSystemI18nSer
|
||||
use crate::infrastructure::services::path_service::PathService;
|
||||
use crate::infrastructure::services::trash_cleanup_service::TrashCleanupService;
|
||||
|
||||
use crate::common::stubs::{
|
||||
StubDedupPort, StubFileManagementUseCase, StubFileReadPort,
|
||||
StubFileRetrievalUseCase, StubFileUploadUseCase, StubFileUseCaseFactory, StubFileWritePort,
|
||||
StubFolderStoragePort, StubFolderUseCase, StubI18nService, StubSearchUseCase, StubZipPort,
|
||||
};
|
||||
use crate::common::stubs::StubZipPort;
|
||||
|
||||
/// Factory for the different application components
|
||||
///
|
||||
@@ -765,317 +761,4 @@ pub struct AppState {
|
||||
Option<Arc<crate::infrastructure::services::wopi_discovery_service::WopiDiscoveryService>>,
|
||||
}
|
||||
|
||||
impl Default for AppState {
|
||||
fn default() -> Self {
|
||||
// Minimal stub version for auth middleware and route construction.
|
||||
// Real services are wired in main.rs via AppServiceFactory.
|
||||
|
||||
let config = crate::common::config::AppConfig::default();
|
||||
let path_service = Arc::new(
|
||||
crate::infrastructure::services::path_service::PathService::new(
|
||||
std::path::PathBuf::from("./storage"),
|
||||
),
|
||||
);
|
||||
|
||||
let i18n_repository = Arc::new(StubI18nService)
|
||||
as Arc<dyn crate::domain::services::i18n_service::I18nService>;
|
||||
let folder_service = Arc::new(StubFolderUseCase)
|
||||
as Arc<dyn crate::application::ports::inbound::FolderUseCase>;
|
||||
let file_upload_service = Arc::new(StubFileUploadUseCase)
|
||||
as Arc<dyn crate::application::ports::file_ports::FileUploadUseCase>;
|
||||
let file_retrieval_service = Arc::new(StubFileRetrievalUseCase)
|
||||
as Arc<dyn crate::application::ports::file_ports::FileRetrievalUseCase>;
|
||||
let file_management_service = Arc::new(StubFileManagementUseCase)
|
||||
as Arc<dyn crate::application::ports::file_ports::FileManagementUseCase>;
|
||||
let file_use_case_factory = Arc::new(StubFileUseCaseFactory)
|
||||
as Arc<dyn crate::application::ports::file_ports::FileUseCaseFactory>;
|
||||
|
||||
// Create file content cache for stub
|
||||
let file_content_cache = Arc::new(FileContentCache::new(FileContentCacheConfig::default()));
|
||||
|
||||
// Create dummy thumbnail service
|
||||
let dummy_thumbnail_service: Arc<dyn ThumbnailPort> = Arc::new(
|
||||
crate::infrastructure::services::thumbnail_service::ThumbnailService::new(
|
||||
&std::path::PathBuf::from("./storage"),
|
||||
100,
|
||||
10 * 1024 * 1024,
|
||||
),
|
||||
);
|
||||
|
||||
// Create dummy chunked upload service
|
||||
let dummy_chunked_upload_service: Arc<dyn ChunkedUploadPort> = Arc::new(
|
||||
crate::infrastructure::services::chunked_upload_service::ChunkedUploadService::new_stub(
|
||||
std::path::PathBuf::from("./storage/.uploads"),
|
||||
),
|
||||
);
|
||||
|
||||
// Create dummy image transcode service
|
||||
let dummy_image_transcode_service: Arc<dyn ImageTranscodePort> = Arc::new(
|
||||
crate::infrastructure::services::image_transcode_service::ImageTranscodeService::new(
|
||||
&std::path::PathBuf::from("./storage"),
|
||||
100,
|
||||
10 * 1024 * 1024,
|
||||
),
|
||||
);
|
||||
|
||||
// Stub dedup service (Default is only used for routing stubs, never for real I/O)
|
||||
let dummy_dedup_service: Arc<dyn DedupPort> = Arc::new(StubDedupPort);
|
||||
|
||||
// Core services using stubs
|
||||
let core_services = CoreServices {
|
||||
path_service: path_service.clone(),
|
||||
file_content_cache,
|
||||
thumbnail_service: dummy_thumbnail_service,
|
||||
chunked_upload_service: dummy_chunked_upload_service,
|
||||
image_transcode_service: dummy_image_transcode_service,
|
||||
dedup_service: dummy_dedup_service,
|
||||
zip_service: Arc::new(StubZipPort) as Arc<dyn ZipPort>,
|
||||
config: config.clone(),
|
||||
};
|
||||
|
||||
// Stub folder repo concrete — uses a dummy PgPool-less constructor
|
||||
// In practice, Default is only used for routing stubs; real app always goes through build_app_state
|
||||
let dummy_folder_repo_concrete = Arc::new(FolderDbRepository::new_stub());
|
||||
|
||||
// Repository services using stubs
|
||||
let repository_services = RepositoryServices {
|
||||
folder_repository: Arc::new(StubFolderStoragePort)
|
||||
as Arc<dyn crate::application::ports::outbound::FolderStoragePort>,
|
||||
folder_repo_concrete: dummy_folder_repo_concrete,
|
||||
file_read_repository: Arc::new(StubFileReadPort)
|
||||
as Arc<dyn crate::application::ports::storage_ports::FileReadPort>,
|
||||
file_write_repository: Arc::new(StubFileWritePort)
|
||||
as Arc<dyn crate::application::ports::storage_ports::FileWritePort>,
|
||||
i18n_repository,
|
||||
trash_repository: None,
|
||||
};
|
||||
|
||||
// Dummy concrete services for compatibility
|
||||
let dummy_folder_storage = Arc::new(StubFolderStoragePort)
|
||||
as Arc<dyn crate::application::ports::outbound::FolderStoragePort>;
|
||||
let folder_service_concrete = Arc::new(FolderService::new(dummy_folder_storage));
|
||||
|
||||
// Dummy I18nApplicationService
|
||||
let dummy_i18n_app_service =
|
||||
crate::application::services::i18n_application_service::I18nApplicationService::new(
|
||||
Arc::new(StubI18nService)
|
||||
as Arc<dyn crate::domain::services::i18n_service::I18nService>,
|
||||
);
|
||||
|
||||
// Application services using stubs
|
||||
let application_services = ApplicationServices {
|
||||
folder_service_concrete: folder_service_concrete.clone(),
|
||||
folder_service,
|
||||
file_upload_service,
|
||||
file_retrieval_service,
|
||||
file_management_service,
|
||||
file_use_case_factory,
|
||||
i18n_service: Arc::new(dummy_i18n_app_service),
|
||||
trash_service: None,
|
||||
search_service: Some(Arc::new(StubSearchUseCase)
|
||||
as Arc<dyn crate::application::ports::inbound::SearchUseCase>),
|
||||
share_service: None,
|
||||
favorites_service: None,
|
||||
recent_service: None,
|
||||
};
|
||||
|
||||
Self {
|
||||
core: core_services,
|
||||
repositories: repository_services,
|
||||
applications: application_services,
|
||||
db_pool: None,
|
||||
auth_service: None,
|
||||
admin_settings_service: None,
|
||||
trash_service: None,
|
||||
share_service: None,
|
||||
favorites_service: None,
|
||||
recent_service: None,
|
||||
storage_usage_service: None,
|
||||
calendar_service: None,
|
||||
contact_service: None,
|
||||
calendar_use_case: None,
|
||||
addressbook_use_case: None,
|
||||
contact_use_case: None,
|
||||
wopi_token_service: None,
|
||||
wopi_lock_service: None,
|
||||
wopi_discovery_service: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(
|
||||
core: CoreServices,
|
||||
repositories: RepositoryServices,
|
||||
applications: ApplicationServices,
|
||||
) -> Self {
|
||||
Self {
|
||||
core,
|
||||
repositories,
|
||||
applications,
|
||||
db_pool: None,
|
||||
auth_service: None,
|
||||
admin_settings_service: None,
|
||||
trash_service: None,
|
||||
share_service: None,
|
||||
favorites_service: None,
|
||||
recent_service: None,
|
||||
storage_usage_service: None,
|
||||
calendar_service: None,
|
||||
contact_service: None,
|
||||
calendar_use_case: None,
|
||||
addressbook_use_case: None,
|
||||
contact_use_case: None,
|
||||
wopi_token_service: None,
|
||||
wopi_lock_service: None,
|
||||
wopi_discovery_service: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_database(mut self, db_pool: Arc<PgPool>) -> Self {
|
||||
self.db_pool = Some(db_pool);
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates a minimal AppState for route construction.
|
||||
///
|
||||
/// Uses `Default` stubs for infrastructure services, then overlays the real
|
||||
/// application-level services that arrive as parameters from `main.rs`.
|
||||
/// This keeps `routes.rs` free of any `crate::infrastructure` references.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn for_routing(
|
||||
folder_service: Arc<FolderService>,
|
||||
file_retrieval_service: Arc<
|
||||
dyn crate::application::ports::file_ports::FileRetrievalUseCase,
|
||||
>,
|
||||
file_upload_service: Arc<dyn FileUploadUseCase>,
|
||||
file_management_service: Arc<dyn FileManagementUseCase>,
|
||||
folder_use_case: Arc<dyn crate::application::ports::inbound::FolderUseCase>,
|
||||
i18n_service: Option<
|
||||
Arc<crate::application::services::i18n_application_service::I18nApplicationService>,
|
||||
>,
|
||||
trash_service: Option<Arc<dyn TrashUseCase>>,
|
||||
search_service: Option<Arc<dyn crate::application::ports::inbound::SearchUseCase>>,
|
||||
share_service: Option<Arc<dyn crate::application::ports::share_ports::ShareUseCase>>,
|
||||
favorites_service: Option<Arc<dyn FavoritesUseCase>>,
|
||||
recent_service: Option<Arc<dyn RecentItemsUseCase>>,
|
||||
) -> Self {
|
||||
let mut state = Self::default();
|
||||
|
||||
// Override application services with real ones
|
||||
state.applications.folder_service_concrete = folder_service.clone();
|
||||
state.applications.folder_service = folder_use_case;
|
||||
state.applications.file_upload_service = file_upload_service;
|
||||
state.applications.file_retrieval_service = file_retrieval_service.clone();
|
||||
state.applications.file_management_service = file_management_service;
|
||||
|
||||
if let Some(i18n) = i18n_service {
|
||||
state.applications.i18n_service = i18n;
|
||||
}
|
||||
|
||||
state.applications.trash_service = trash_service.clone();
|
||||
state.applications.search_service = search_service.clone();
|
||||
state.applications.share_service = share_service.clone();
|
||||
state.applications.favorites_service = favorites_service.clone();
|
||||
state.applications.recent_service = recent_service.clone();
|
||||
|
||||
// Also set top-level optional services
|
||||
state.trash_service = trash_service;
|
||||
state.share_service = share_service;
|
||||
state.favorites_service = favorites_service;
|
||||
state.recent_service = recent_service;
|
||||
|
||||
// Create real ZipService with the actual file/folder services
|
||||
state.core.zip_service = Arc::new(
|
||||
crate::infrastructure::services::zip_service::ZipService::new(
|
||||
file_retrieval_service
|
||||
as Arc<dyn crate::application::ports::file_ports::FileRetrievalUseCase>,
|
||||
folder_service.clone()
|
||||
as Arc<dyn crate::application::ports::inbound::FolderUseCase>,
|
||||
),
|
||||
);
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
pub fn with_auth_services(mut self, auth_services: AuthServices) -> Self {
|
||||
self.auth_service = Some(auth_services);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_trash_service(mut self, trash_service: Arc<dyn TrashUseCase>) -> Self {
|
||||
self.trash_service = Some(trash_service);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_share_service(
|
||||
mut self,
|
||||
share_service: Arc<dyn crate::application::ports::share_ports::ShareUseCase>,
|
||||
) -> Self {
|
||||
self.share_service = Some(share_service);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_favorites_service(mut self, favorites_service: Arc<dyn FavoritesUseCase>) -> Self {
|
||||
self.favorites_service = Some(favorites_service);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_recent_service(mut self, recent_service: Arc<dyn RecentItemsUseCase>) -> Self {
|
||||
self.recent_service = Some(recent_service);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_storage_usage_service(
|
||||
mut self,
|
||||
storage_usage_service: Arc<dyn crate::application::ports::storage_ports::StorageUsagePort>,
|
||||
) -> Self {
|
||||
self.storage_usage_service = Some(storage_usage_service);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_calendar_service(
|
||||
mut self,
|
||||
calendar_service: Arc<dyn crate::application::ports::storage_ports::StorageUseCase>,
|
||||
) -> Self {
|
||||
self.calendar_service = Some(calendar_service);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_contact_service(
|
||||
mut self,
|
||||
contact_service: Arc<dyn crate::application::ports::storage_ports::StorageUseCase>,
|
||||
) -> Self {
|
||||
self.contact_service = Some(contact_service);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_calendar_use_case(
|
||||
mut self,
|
||||
calendar_use_case: Arc<dyn crate::application::ports::calendar_ports::CalendarUseCase>,
|
||||
) -> Self {
|
||||
self.calendar_use_case = Some(calendar_use_case);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_addressbook_use_case(
|
||||
mut self,
|
||||
addressbook_use_case: Arc<dyn crate::application::ports::carddav_ports::AddressBookUseCase>,
|
||||
) -> Self {
|
||||
self.addressbook_use_case = Some(addressbook_use_case);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_contact_use_case(
|
||||
mut self,
|
||||
contact_use_case: Arc<dyn crate::application::ports::carddav_ports::ContactUseCase>,
|
||||
) -> Self {
|
||||
self.contact_use_case = Some(contact_use_case);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_zip_service(mut self, zip_service: Arc<dyn ZipPort>) -> Self {
|
||||
self.core.zip_service = zip_service;
|
||||
self
|
||||
}
|
||||
}
|
||||
// All AppState construction is done via struct literal in build_app_state().
|
||||
|
||||
Reference in New Issue
Block a user