perf: replace dyn trait objects with concrete types to eliminate vtable overhead
Remove async-trait dependency and use native Rust async fn in traits. Replace Arc<dyn Trait> with Arc<ConcreteType> throughout the codebase to enable monomorphization and eliminate dynamic dispatch overhead. Key changes: - Remove write-behind cache (no implementation existed) - Fix should_transcode static method call - Use ContactStorageAdapter directly instead of dyn AddressBookUseCase - Clean up unused trait imports across services and DI https://claude.ai/code/session_01EbAFEfyJNLRmJHmmYDX3Tt
This commit is contained in:
@@ -8,13 +8,14 @@ use crate::common::config::OidcConfig;
|
||||
use crate::common::errors::{DomainError, ErrorKind};
|
||||
use crate::domain::repositories::settings_repository::SettingsRepository;
|
||||
use crate::infrastructure::services::oidc_service::OidcService;
|
||||
use crate::infrastructure::repositories::pg::SettingsPgRepository;
|
||||
|
||||
/// Admin settings service — manages platform configuration in the database.
|
||||
///
|
||||
/// Configuration priority: **env vars > DB settings > defaults**.
|
||||
/// Supports hot-reloading OIDC configuration without server restart.
|
||||
pub struct AdminSettingsService {
|
||||
settings_repo: Arc<dyn SettingsRepository>,
|
||||
settings_repo: Arc<SettingsPgRepository>,
|
||||
env_oidc_config: OidcConfig,
|
||||
auth_app_service: Arc<AuthApplicationService>,
|
||||
server_base_url: String,
|
||||
@@ -22,7 +23,7 @@ pub struct AdminSettingsService {
|
||||
|
||||
impl AdminSettingsService {
|
||||
pub fn new(
|
||||
settings_repo: Arc<dyn SettingsRepository>,
|
||||
settings_repo: Arc<SettingsPgRepository>,
|
||||
env_oidc_config: OidcConfig,
|
||||
auth_app_service: Arc<AuthApplicationService>,
|
||||
server_base_url: String,
|
||||
|
||||
@@ -13,6 +13,9 @@ use chrono::{Duration, Utc};
|
||||
use moka::future::Cache;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration as StdDuration;
|
||||
use crate::infrastructure::repositories::pg::AppPasswordPgRepository;
|
||||
use crate::infrastructure::services::password_hasher::Argon2PasswordHasher;
|
||||
use crate::infrastructure::repositories::pg::UserPgRepository;
|
||||
|
||||
/// App password token length (32 random alphanumeric chars after prefix).
|
||||
const TOKEN_LENGTH: usize = 32;
|
||||
@@ -39,9 +42,9 @@ struct CachedBasicAuthResult {
|
||||
}
|
||||
|
||||
pub struct AppPasswordService {
|
||||
repo: Arc<dyn AppPasswordStoragePort>,
|
||||
hasher: Arc<dyn PasswordHasherPort>,
|
||||
user_repo: Arc<dyn UserStoragePort>,
|
||||
repo: Arc<AppPasswordPgRepository>,
|
||||
hasher: Arc<Argon2PasswordHasher>,
|
||||
user_repo: Arc<UserPgRepository>,
|
||||
base_url: String,
|
||||
|
||||
/// In-memory cache of successful Basic Auth verifications.
|
||||
@@ -59,9 +62,9 @@ pub struct AppPasswordService {
|
||||
|
||||
impl AppPasswordService {
|
||||
pub fn new(
|
||||
repo: Arc<dyn AppPasswordStoragePort>,
|
||||
hasher: Arc<dyn PasswordHasherPort>,
|
||||
user_repo: Arc<dyn UserStoragePort>,
|
||||
repo: Arc<AppPasswordPgRepository>,
|
||||
hasher: Arc<Argon2PasswordHasher>,
|
||||
user_repo: Arc<UserPgRepository>,
|
||||
base_url: String,
|
||||
) -> Self {
|
||||
let auth_cache = Cache::builder()
|
||||
|
||||
@@ -15,6 +15,12 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
use std::time::Duration;
|
||||
use crate::infrastructure::services::password_hasher::Argon2PasswordHasher;
|
||||
use crate::application::services::folder_service::FolderService;
|
||||
use crate::infrastructure::services::jwt_service::JwtTokenService;
|
||||
use crate::infrastructure::services::oidc_service::OidcService;
|
||||
use crate::infrastructure::repositories::pg::SessionPgRepository;
|
||||
use crate::infrastructure::repositories::pg::UserPgRepository;
|
||||
|
||||
/// Tracks a pending OIDC authorization flow (CSRF + PKCE + nonce)
|
||||
#[derive(Clone)]
|
||||
@@ -31,7 +37,7 @@ struct PendingOidcToken {
|
||||
|
||||
/// Interior state for OIDC — protected by RwLock for hot-reload.
|
||||
struct OidcState {
|
||||
service: Option<Arc<dyn OidcServicePort>>,
|
||||
service: Option<Arc<OidcService>>,
|
||||
config: Option<OidcConfig>,
|
||||
}
|
||||
|
||||
@@ -40,11 +46,11 @@ const DEFAULT_ADMIN_QUOTA: i64 = 107_374_182_400;
|
||||
const DEFAULT_USER_QUOTA: i64 = 1_073_741_824; // 1 GB
|
||||
|
||||
pub struct AuthApplicationService {
|
||||
user_storage: Arc<dyn UserStoragePort>,
|
||||
session_storage: Arc<dyn SessionStoragePort>,
|
||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
||||
token_service: Arc<dyn TokenServicePort>,
|
||||
folder_service: Option<Arc<dyn FolderUseCase>>,
|
||||
user_storage: Arc<UserPgRepository>,
|
||||
session_storage: Arc<SessionPgRepository>,
|
||||
password_hasher: Arc<Argon2PasswordHasher>,
|
||||
token_service: Arc<JwtTokenService>,
|
||||
folder_service: Option<Arc<FolderService>>,
|
||||
/// Path to the storage directory, used for disk-space–aware quota calculation
|
||||
storage_path: PathBuf,
|
||||
oidc: RwLock<OidcState>,
|
||||
@@ -58,10 +64,10 @@ pub struct AuthApplicationService {
|
||||
|
||||
impl AuthApplicationService {
|
||||
pub fn new(
|
||||
user_storage: Arc<dyn UserStoragePort>,
|
||||
session_storage: Arc<dyn SessionStoragePort>,
|
||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
||||
token_service: Arc<dyn TokenServicePort>,
|
||||
user_storage: Arc<UserPgRepository>,
|
||||
session_storage: Arc<SessionPgRepository>,
|
||||
password_hasher: Arc<Argon2PasswordHasher>,
|
||||
token_service: Arc<JwtTokenService>,
|
||||
storage_path: PathBuf,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -133,7 +139,7 @@ impl AuthApplicationService {
|
||||
}
|
||||
|
||||
/// Configures the folder service, needed to create personal folders
|
||||
pub fn with_folder_service(mut self, folder_service: Arc<dyn FolderUseCase>) -> Self {
|
||||
pub fn with_folder_service(mut self, folder_service: Arc<FolderService>) -> Self {
|
||||
self.folder_service = Some(folder_service);
|
||||
self
|
||||
}
|
||||
@@ -141,7 +147,7 @@ impl AuthApplicationService {
|
||||
/// Configures the OIDC service
|
||||
pub fn with_oidc(
|
||||
self,
|
||||
oidc_service: Arc<dyn OidcServicePort>,
|
||||
oidc_service: Arc<OidcService>,
|
||||
oidc_config: OidcConfig,
|
||||
) -> Self {
|
||||
{
|
||||
@@ -153,7 +159,7 @@ impl AuthApplicationService {
|
||||
}
|
||||
|
||||
/// Hot-reload OIDC configuration at runtime (called from admin settings service)
|
||||
pub fn reload_oidc(&self, oidc_service: Arc<dyn OidcServicePort>, oidc_config: OidcConfig) {
|
||||
pub fn reload_oidc(&self, oidc_service: Arc<OidcService>, oidc_config: OidcConfig) {
|
||||
let mut state = self.oidc.write().unwrap();
|
||||
state.service = Some(oidc_service);
|
||||
state.config = Some(oidc_config);
|
||||
@@ -188,7 +194,7 @@ impl AuthApplicationService {
|
||||
}
|
||||
|
||||
/// Returns an Arc clone of the OIDC service if available
|
||||
pub fn oidc_service(&self) -> Option<Arc<dyn OidcServicePort>> {
|
||||
pub fn oidc_service(&self) -> Option<Arc<OidcService>> {
|
||||
let state = self.oidc.read().unwrap();
|
||||
state.service.clone()
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ use crate::application::ports::trash_ports::TrashUseCase;
|
||||
use crate::application::services::folder_service::FolderService;
|
||||
use crate::common::config::AppConfig;
|
||||
use crate::common::errors::DomainError;
|
||||
use crate::application::services::file_management_service::FileManagementService;
|
||||
use crate::application::services::file_retrieval_service::FileRetrievalService;
|
||||
use crate::application::services::trash_service::TrashService;
|
||||
|
||||
/// Specific errors for batch operations
|
||||
#[derive(Debug, Error)]
|
||||
@@ -65,18 +68,18 @@ pub struct BatchStats {
|
||||
|
||||
/// Batch operations service
|
||||
pub struct BatchOperationService {
|
||||
file_retrieval: Arc<dyn FileRetrievalUseCase>,
|
||||
file_management: Arc<dyn FileManagementUseCase>,
|
||||
file_retrieval: Arc<FileRetrievalService>,
|
||||
file_management: Arc<FileManagementService>,
|
||||
folder_service: Arc<FolderService>,
|
||||
trash_service: Option<Arc<dyn TrashUseCase>>,
|
||||
trash_service: Option<Arc<TrashService>>,
|
||||
config: AppConfig,
|
||||
}
|
||||
|
||||
impl BatchOperationService {
|
||||
/// Creates a new instance of the batch operations service
|
||||
pub fn new(
|
||||
file_retrieval: Arc<dyn FileRetrievalUseCase>,
|
||||
file_management: Arc<dyn FileManagementUseCase>,
|
||||
file_retrieval: Arc<FileRetrievalService>,
|
||||
file_management: Arc<FileManagementService>,
|
||||
folder_service: Arc<FolderService>,
|
||||
config: AppConfig,
|
||||
) -> Self {
|
||||
@@ -91,8 +94,8 @@ impl BatchOperationService {
|
||||
|
||||
/// Creates a new instance with default configuration
|
||||
pub fn default(
|
||||
file_retrieval: Arc<dyn FileRetrievalUseCase>,
|
||||
file_management: Arc<dyn FileManagementUseCase>,
|
||||
file_retrieval: Arc<FileRetrievalService>,
|
||||
file_management: Arc<FileManagementService>,
|
||||
folder_service: Arc<FolderService>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
@@ -104,7 +107,7 @@ impl BatchOperationService {
|
||||
}
|
||||
|
||||
/// Set the optional trash service (enables batch trash operations)
|
||||
pub fn with_trash_service(mut self, trash_service: Arc<dyn TrashUseCase>) -> Self {
|
||||
pub fn with_trash_service(mut self, trash_service: Arc<TrashService>) -> Self {
|
||||
self.trash_service = Some(trash_service);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -8,18 +7,18 @@ use crate::application::dtos::calendar_dto::{
|
||||
};
|
||||
use crate::application::ports::calendar_ports::{CalendarStoragePort, CalendarUseCase};
|
||||
use crate::common::errors::{DomainError, ErrorKind};
|
||||
use crate::infrastructure::adapters::calendar_storage_adapter::CalendarStorageAdapter;
|
||||
|
||||
pub struct CalendarService {
|
||||
calendar_storage: Arc<dyn CalendarStoragePort>,
|
||||
calendar_storage: Arc<CalendarStorageAdapter>,
|
||||
}
|
||||
|
||||
impl CalendarService {
|
||||
pub fn new(calendar_storage: Arc<dyn CalendarStoragePort>) -> Self {
|
||||
pub fn new(calendar_storage: Arc<CalendarStorageAdapter>) -> Self {
|
||||
Self { calendar_storage }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CalendarUseCase for CalendarService {
|
||||
async fn create_calendar(
|
||||
&self,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
use sqlx::types::Uuid;
|
||||
use std::sync::Arc;
|
||||
@@ -17,18 +16,21 @@ use crate::common::errors::DomainError;
|
||||
use crate::domain::entities::contact::{Address, AddressBook, Contact, ContactGroup, Email, Phone};
|
||||
use crate::domain::repositories::address_book_repository::AddressBookRepository;
|
||||
use crate::domain::repositories::contact_repository::{ContactGroupRepository, ContactRepository};
|
||||
use crate::infrastructure::repositories::pg::AddressBookPgRepository;
|
||||
use crate::infrastructure::repositories::pg::ContactGroupPgRepository;
|
||||
use crate::infrastructure::repositories::pg::ContactPgRepository;
|
||||
|
||||
pub struct ContactService {
|
||||
address_book_repository: Arc<dyn AddressBookRepository>,
|
||||
contact_repository: Arc<dyn ContactRepository>,
|
||||
contact_group_repository: Arc<dyn ContactGroupRepository>,
|
||||
address_book_repository: Arc<AddressBookPgRepository>,
|
||||
contact_repository: Arc<ContactPgRepository>,
|
||||
contact_group_repository: Arc<ContactGroupPgRepository>,
|
||||
}
|
||||
|
||||
impl ContactService {
|
||||
pub fn new(
|
||||
address_book_repository: Arc<dyn AddressBookRepository>,
|
||||
contact_repository: Arc<dyn ContactRepository>,
|
||||
contact_group_repository: Arc<dyn ContactGroupRepository>,
|
||||
address_book_repository: Arc<AddressBookPgRepository>,
|
||||
contact_repository: Arc<ContactPgRepository>,
|
||||
contact_group_repository: Arc<ContactGroupPgRepository>,
|
||||
) -> Self {
|
||||
Self {
|
||||
address_book_repository,
|
||||
@@ -264,7 +266,6 @@ impl ContactService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AddressBookUseCase for ContactService {
|
||||
async fn create_address_book(
|
||||
&self,
|
||||
@@ -514,7 +515,6 @@ impl AddressBookUseCase for ContactService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ContactUseCase for ContactService {
|
||||
async fn create_contact(&self, dto: CreateContactDto) -> Result<ContactDto, DomainError> {
|
||||
let address_book_id = Uuid::parse_str(&dto.address_book_id)
|
||||
@@ -1092,7 +1092,6 @@ impl ContactUseCase for ContactService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl StorageUseCase for ContactService {
|
||||
async fn handle_request(
|
||||
&self,
|
||||
|
||||
@@ -18,6 +18,10 @@ use crate::application::ports::auth_ports::{
|
||||
use crate::common::errors::{DomainError, ErrorKind};
|
||||
use crate::domain::entities::device_code::{DeviceCode, DeviceCodeStatus};
|
||||
use crate::domain::entities::session::Session;
|
||||
use crate::infrastructure::repositories::pg::DeviceCodePgRepository;
|
||||
use crate::infrastructure::services::jwt_service::JwtTokenService;
|
||||
use crate::infrastructure::repositories::pg::SessionPgRepository;
|
||||
use crate::infrastructure::repositories::pg::UserPgRepository;
|
||||
|
||||
/// Default device code lifetime: 15 minutes (RFC 8628 recommends 5-30 min).
|
||||
const DEVICE_CODE_LIFETIME_SECS: i64 = 900;
|
||||
@@ -34,20 +38,20 @@ const USER_CODE_LETTER_LEN: usize = 4;
|
||||
const USER_CODE_DIGIT_LEN: usize = 4;
|
||||
|
||||
pub struct DeviceAuthService {
|
||||
device_code_storage: Arc<dyn DeviceCodeStoragePort>,
|
||||
token_service: Arc<dyn TokenServicePort>,
|
||||
user_storage: Arc<dyn UserStoragePort>,
|
||||
session_storage: Arc<dyn SessionStoragePort>,
|
||||
device_code_storage: Arc<DeviceCodePgRepository>,
|
||||
token_service: Arc<JwtTokenService>,
|
||||
user_storage: Arc<UserPgRepository>,
|
||||
session_storage: Arc<SessionPgRepository>,
|
||||
/// Base URL of the server (e.g. "https://cloud.example.com")
|
||||
base_url: String,
|
||||
}
|
||||
|
||||
impl DeviceAuthService {
|
||||
pub fn new(
|
||||
device_code_storage: Arc<dyn DeviceCodeStoragePort>,
|
||||
token_service: Arc<dyn TokenServicePort>,
|
||||
user_storage: Arc<dyn UserStoragePort>,
|
||||
session_storage: Arc<dyn SessionStoragePort>,
|
||||
device_code_storage: Arc<DeviceCodePgRepository>,
|
||||
token_service: Arc<JwtTokenService>,
|
||||
user_storage: Arc<UserPgRepository>,
|
||||
session_storage: Arc<SessionPgRepository>,
|
||||
base_url: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -3,26 +3,25 @@ use crate::application::dtos::favorites_dto::{
|
||||
};
|
||||
use crate::application::ports::favorites_ports::{FavoritesRepositoryPort, FavoritesUseCase};
|
||||
use crate::common::errors::{DomainError, ErrorKind, Result};
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
use tracing::info;
|
||||
use crate::infrastructure::repositories::pg::FavoritesPgRepository;
|
||||
|
||||
/// Implementation of the FavoritesUseCase for managing user favorites.
|
||||
///
|
||||
/// Depends on `FavoritesRepositoryPort` (outbound port) instead of
|
||||
/// accessing the database directly, following hexagonal architecture.
|
||||
pub struct FavoritesService {
|
||||
repo: Arc<dyn FavoritesRepositoryPort>,
|
||||
repo: Arc<FavoritesPgRepository>,
|
||||
}
|
||||
|
||||
impl FavoritesService {
|
||||
/// Create a new FavoritesService with the given repository port
|
||||
pub fn new(repo: Arc<dyn FavoritesRepositoryPort>) -> Self {
|
||||
pub fn new(repo: Arc<FavoritesPgRepository>) -> Self {
|
||||
Self { repo }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FavoritesUseCase for FavoritesService {
|
||||
/// Get all favorites for a user
|
||||
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>> {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::application::dtos::file_dto::FileDto;
|
||||
@@ -7,6 +6,8 @@ use crate::application::ports::storage_ports::{CopyFolderTreeResult, FileWritePo
|
||||
use crate::application::ports::trash_ports::TrashUseCase;
|
||||
use crate::common::errors::DomainError;
|
||||
use tracing::{error, info, warn};
|
||||
use crate::infrastructure::repositories::pg::file_blob_write_repository::FileBlobWriteRepository;
|
||||
use crate::application::services::trash_service::TrashService;
|
||||
|
||||
/// Service for file management operations (move, delete).
|
||||
///
|
||||
@@ -15,13 +16,13 @@ use tracing::{error, info, warn};
|
||||
/// This service only orchestrates trash vs. permanent delete — it never
|
||||
/// touches ref_count directly.
|
||||
pub struct FileManagementService {
|
||||
file_repository: Arc<dyn FileWritePort>,
|
||||
trash_service: Option<Arc<dyn TrashUseCase>>,
|
||||
file_repository: Arc<FileBlobWriteRepository>,
|
||||
trash_service: Option<Arc<TrashService>>,
|
||||
}
|
||||
|
||||
impl FileManagementService {
|
||||
/// Creates a new FileManagementService.
|
||||
pub fn new(file_repository: Arc<dyn FileWritePort>) -> Self {
|
||||
pub fn new(file_repository: Arc<FileBlobWriteRepository>) -> Self {
|
||||
Self {
|
||||
file_repository,
|
||||
trash_service: None,
|
||||
@@ -30,8 +31,8 @@ impl FileManagementService {
|
||||
|
||||
/// Creates a FileManagementService with a trash service.
|
||||
pub fn with_trash(
|
||||
file_repository: Arc<dyn FileWritePort>,
|
||||
trash_service: Option<Arc<dyn TrashUseCase>>,
|
||||
file_repository: Arc<FileBlobWriteRepository>,
|
||||
trash_service: Option<Arc<TrashService>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
file_repository,
|
||||
@@ -40,7 +41,6 @@ impl FileManagementService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FileManagementUseCase for FileManagementService {
|
||||
async fn move_file(
|
||||
&self,
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use async_trait::async_trait;
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::{Stream, StreamExt};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::application::dtos::file_dto::FileDto;
|
||||
use crate::application::ports::cache_ports::{ContentCachePort, WriteBehindCachePort};
|
||||
use crate::application::ports::file_ports::{FileRetrievalUseCase, OptimizedFileContent};
|
||||
use crate::application::ports::storage_ports::FileReadPort;
|
||||
use crate::application::ports::transcode_ports::{ImageTranscodePort, OutputFormat};
|
||||
use crate::common::errors::DomainError;
|
||||
use tracing::{debug, info};
|
||||
use crate::infrastructure::repositories::pg::file_blob_read_repository::FileBlobReadRepository;
|
||||
use crate::infrastructure::services::file_content_cache::FileContentCache;
|
||||
use crate::infrastructure::services::image_transcode_service::{ImageTranscodeService, OutputFormat};
|
||||
|
||||
/// Threshold below which files are served from RAM cache (10 MB).
|
||||
const CACHE_THRESHOLD: u64 = 10 * 1024 * 1024;
|
||||
@@ -23,48 +23,29 @@ const CACHE_THRESHOLD: u64 = 10 * 1024 * 1024;
|
||||
/// - Tier 2: Memory-mapped I/O (10–100 MB)
|
||||
/// - Tier 3: Streaming (≥100 MB)
|
||||
pub struct FileRetrievalService {
|
||||
file_read: Arc<dyn FileReadPort>,
|
||||
write_behind: Option<Arc<dyn WriteBehindCachePort>>,
|
||||
content_cache: Option<Arc<dyn ContentCachePort>>,
|
||||
transcode: Option<Arc<dyn ImageTranscodePort>>,
|
||||
file_read: Arc<FileBlobReadRepository>,
|
||||
content_cache: Option<Arc<FileContentCache>>,
|
||||
transcode: Option<Arc<ImageTranscodeService>>,
|
||||
}
|
||||
|
||||
impl FileRetrievalService {
|
||||
/// Backward-compatible constructor (simple pass-through).
|
||||
pub fn new(file_repository: Arc<dyn FileReadPort>) -> Self {
|
||||
pub fn new(file_repository: Arc<FileBlobReadRepository>) -> Self {
|
||||
Self {
|
||||
file_read: file_repository,
|
||||
write_behind: None,
|
||||
content_cache: None,
|
||||
transcode: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Full constructor with all infrastructure ports.
|
||||
pub fn new_full(
|
||||
file_read: Arc<dyn FileReadPort>,
|
||||
write_behind: Arc<dyn WriteBehindCachePort>,
|
||||
content_cache: Arc<dyn ContentCachePort>,
|
||||
transcode: Arc<dyn ImageTranscodePort>,
|
||||
) -> Self {
|
||||
Self {
|
||||
file_read,
|
||||
write_behind: Some(write_behind),
|
||||
content_cache: Some(content_cache),
|
||||
transcode: Some(transcode),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor for blob-storage model: read + content cache + transcode.
|
||||
/// No write-behind needed — dedup handled at the repository layer.
|
||||
pub fn new_with_cache(
|
||||
file_read: Arc<dyn FileReadPort>,
|
||||
content_cache: Arc<dyn ContentCachePort>,
|
||||
transcode: Arc<dyn ImageTranscodePort>,
|
||||
file_read: Arc<FileBlobReadRepository>,
|
||||
content_cache: Arc<FileContentCache>,
|
||||
transcode: Arc<ImageTranscodeService>,
|
||||
) -> Self {
|
||||
Self {
|
||||
file_read,
|
||||
write_behind: None,
|
||||
content_cache: Some(content_cache),
|
||||
transcode: Some(transcode),
|
||||
}
|
||||
@@ -85,7 +66,7 @@ impl FileRetrievalService {
|
||||
return None;
|
||||
}
|
||||
let transcode = self.transcode.as_ref()?;
|
||||
if !transcode.should_transcode(mime, file_size) {
|
||||
if !ImageTranscodeService::should_transcode(mime, file_size) {
|
||||
return None;
|
||||
}
|
||||
let format = OutputFormat::WebP;
|
||||
@@ -121,37 +102,6 @@ impl FileRetrievalService {
|
||||
let modified_at = dto.modified_at;
|
||||
let do_transcode = accept_webp && !prefer_original;
|
||||
|
||||
// ── Tier 0: Write-behind cache ───────────────────────
|
||||
if let Some(wb) = &self.write_behind
|
||||
&& let Some(pending) = wb.get_pending(id).await
|
||||
{
|
||||
debug!(
|
||||
"⚡ TIER 0 Write-Behind HIT: {} ({} bytes)",
|
||||
file_name,
|
||||
pending.len()
|
||||
);
|
||||
let (data, mime) = if do_transcode {
|
||||
if let Some((t, m)) = self
|
||||
.try_transcode(id, &pending, &mime_type, file_size, true)
|
||||
.await
|
||||
{
|
||||
(t, m)
|
||||
} else {
|
||||
(pending, mime_type.clone())
|
||||
}
|
||||
} else {
|
||||
(pending, mime_type.clone())
|
||||
};
|
||||
return Ok((
|
||||
dto,
|
||||
OptimizedFileContent::Bytes {
|
||||
data,
|
||||
mime_type: mime,
|
||||
was_transcoded: do_transcode,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
// ── Tier 1: Hot cache + transcode (<10 MB) ──────────
|
||||
if file_size < CACHE_THRESHOLD {
|
||||
// Check content cache first
|
||||
@@ -243,7 +193,6 @@ impl FileRetrievalService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FileRetrievalUseCase for FileRetrievalService {
|
||||
async fn get_file(&self, id: &str) -> Result<FileDto, DomainError> {
|
||||
let file = self.file_read.get_file(id).await?;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
@@ -8,6 +7,9 @@ use crate::application::ports::file_ports::FileUploadUseCase;
|
||||
use crate::application::ports::storage_ports::{FileReadPort, FileWritePort};
|
||||
use crate::common::errors::DomainError;
|
||||
use tracing::{debug, info, warn};
|
||||
use crate::infrastructure::repositories::pg::FileBlobReadRepository;
|
||||
use crate::infrastructure::repositories::pg::FileBlobWriteRepository;
|
||||
use crate::application::services::storage_usage_service::StorageUsageService;
|
||||
|
||||
/// Helper function to extract username from folder path string.
|
||||
/// e.g. "My Folder - user1/subfolder/file.txt" → "user1"
|
||||
@@ -42,17 +44,17 @@ fn extract_username_from_path(path: &str) -> Option<String> {
|
||||
/// Peak RAM usage during any upload: ~256 KB (streaming hash) regardless of file size.
|
||||
pub struct FileUploadService {
|
||||
/// Write port — handles save, streaming, deferred registration
|
||||
file_write: Arc<dyn FileWritePort>,
|
||||
file_write: Arc<FileBlobWriteRepository>,
|
||||
/// Read port — needed for WebDAV create_file / update_file
|
||||
file_read: Option<Arc<dyn FileReadPort>>,
|
||||
file_read: Option<Arc<FileBlobReadRepository>>,
|
||||
/// Optional storage usage tracking
|
||||
storage_usage_service:
|
||||
Option<Arc<dyn crate::application::ports::storage_ports::StorageUsagePort>>,
|
||||
Option<Arc<StorageUsageService>>,
|
||||
}
|
||||
|
||||
impl FileUploadService {
|
||||
/// Constructor with write port only (minimal).
|
||||
pub fn new(file_repository: Arc<dyn FileWritePort>) -> Self {
|
||||
pub fn new(file_repository: Arc<FileBlobWriteRepository>) -> Self {
|
||||
Self {
|
||||
file_write: file_repository,
|
||||
file_read: None,
|
||||
@@ -62,8 +64,8 @@ impl FileUploadService {
|
||||
|
||||
/// Constructor for blob-storage model: write + read ports.
|
||||
pub fn new_with_read(
|
||||
file_write: Arc<dyn FileWritePort>,
|
||||
file_read: Arc<dyn FileReadPort>,
|
||||
file_write: Arc<FileBlobWriteRepository>,
|
||||
file_read: Arc<FileBlobReadRepository>,
|
||||
) -> Self {
|
||||
Self {
|
||||
file_write,
|
||||
@@ -75,7 +77,7 @@ impl FileUploadService {
|
||||
/// Configures the storage usage service
|
||||
pub fn with_storage_usage_service(
|
||||
mut self,
|
||||
storage_usage_service: Arc<dyn crate::application::ports::storage_ports::StorageUsagePort>,
|
||||
storage_usage_service: Arc<StorageUsageService>,
|
||||
) -> Self {
|
||||
self.storage_usage_service = Some(storage_usage_service);
|
||||
self
|
||||
@@ -106,7 +108,6 @@ impl FileUploadService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FileUploadUseCase for FileUploadService {
|
||||
/// Streaming upload from a temp file on disk.
|
||||
///
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::application::ports::file_ports::{
|
||||
FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase, FileUseCaseFactory,
|
||||
};
|
||||
use crate::application::ports::storage_ports::{FileReadPort, FileWritePort};
|
||||
use crate::application::ports::file_ports::FileUseCaseFactory;
|
||||
use crate::application::services::file_management_service::FileManagementService;
|
||||
use crate::application::services::file_retrieval_service::FileRetrievalService;
|
||||
use crate::application::services::file_upload_service::FileUploadService;
|
||||
use crate::infrastructure::repositories::pg::file_blob_read_repository::FileBlobReadRepository;
|
||||
use crate::infrastructure::repositories::pg::file_blob_write_repository::FileBlobWriteRepository;
|
||||
|
||||
/// Factory for creating file use case implementations
|
||||
pub struct AppFileUseCaseFactory {
|
||||
file_read_repository: Arc<dyn FileReadPort>,
|
||||
file_write_repository: Arc<dyn FileWritePort>,
|
||||
file_read_repository: Arc<FileBlobReadRepository>,
|
||||
file_write_repository: Arc<FileBlobWriteRepository>,
|
||||
}
|
||||
|
||||
impl AppFileUseCaseFactory {
|
||||
/// Creates a new factory for file use cases
|
||||
pub fn new(
|
||||
file_read_repository: Arc<dyn FileReadPort>,
|
||||
file_write_repository: Arc<dyn FileWritePort>,
|
||||
file_read_repository: Arc<FileBlobReadRepository>,
|
||||
file_write_repository: Arc<FileBlobWriteRepository>,
|
||||
) -> Self {
|
||||
Self {
|
||||
file_read_repository,
|
||||
@@ -28,15 +27,15 @@ impl AppFileUseCaseFactory {
|
||||
}
|
||||
|
||||
impl FileUseCaseFactory for AppFileUseCaseFactory {
|
||||
fn create_file_upload_use_case(&self) -> Arc<dyn FileUploadUseCase> {
|
||||
fn create_file_upload_use_case(&self) -> Arc<FileUploadService> {
|
||||
Arc::new(FileUploadService::new(self.file_write_repository.clone()))
|
||||
}
|
||||
|
||||
fn create_file_retrieval_use_case(&self) -> Arc<dyn FileRetrievalUseCase> {
|
||||
fn create_file_retrieval_use_case(&self) -> Arc<FileRetrievalService> {
|
||||
Arc::new(FileRetrievalService::new(self.file_read_repository.clone()))
|
||||
}
|
||||
|
||||
fn create_file_management_use_case(&self) -> Arc<dyn FileManagementUseCase> {
|
||||
fn create_file_management_use_case(&self) -> Arc<FileManagementService> {
|
||||
Arc::new(FileManagementService::new(
|
||||
self.file_write_repository.clone(),
|
||||
))
|
||||
|
||||
@@ -2,20 +2,20 @@ use crate::application::dtos::folder_dto::{
|
||||
CreateFolderDto, FolderDto, MoveFolderDto, RenameFolderDto,
|
||||
};
|
||||
use crate::application::ports::inbound::FolderUseCase;
|
||||
use crate::application::ports::outbound::FolderStoragePort;
|
||||
use crate::common::errors::{DomainError, ErrorKind};
|
||||
use crate::domain::services::path_service::StoragePath;
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
use crate::infrastructure::repositories::pg::folder_db_repository::FolderDbRepository;
|
||||
use crate::domain::repositories::folder_repository::FolderRepository;
|
||||
|
||||
/// Implementation of the use case for folder operations
|
||||
pub struct FolderService {
|
||||
folder_storage: Arc<dyn FolderStoragePort>,
|
||||
folder_storage: Arc<FolderDbRepository>,
|
||||
}
|
||||
|
||||
impl FolderService {
|
||||
/// Creates a new folder service
|
||||
pub fn new(folder_storage: Arc<dyn FolderStoragePort>) -> Self {
|
||||
pub fn new(folder_storage: Arc<FolderDbRepository>) -> Self {
|
||||
Self { folder_storage }
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ impl FolderService {
|
||||
pub fn new_stub() -> impl FolderUseCase {
|
||||
struct FolderServiceStub;
|
||||
|
||||
#[async_trait]
|
||||
impl FolderUseCase for FolderServiceStub {
|
||||
async fn create_folder(&self, _dto: CreateFolderDto) -> Result<FolderDto, DomainError> {
|
||||
Ok(FolderDto::empty())
|
||||
@@ -124,7 +123,6 @@ impl FolderService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FolderUseCase for FolderService {
|
||||
/// Creates a new folder
|
||||
async fn create_folder(&self, dto: CreateFolderDto) -> Result<FolderDto, DomainError> {
|
||||
|
||||
@@ -1,43 +1,23 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::domain::services::i18n_service::{I18nResult, I18nService, Locale};
|
||||
use crate::infrastructure::services::file_system_i18n_service::FileSystemI18nService;
|
||||
|
||||
/// Service for i18n operations
|
||||
pub struct I18nApplicationService {
|
||||
i18n_service: Arc<dyn I18nService>,
|
||||
i18n_service: Arc<FileSystemI18nService>,
|
||||
}
|
||||
|
||||
impl I18nApplicationService {
|
||||
/// Creates a dummy service for testing
|
||||
pub fn dummy() -> Self {
|
||||
struct DummyI18nService;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl I18nService for DummyI18nService {
|
||||
async fn translate(&self, _key: &str, _locale: Locale) -> I18nResult<String> {
|
||||
Ok("DUMMY_TRANSLATION".to_string())
|
||||
}
|
||||
|
||||
async fn load_translations(&self, _locale: Locale) -> I18nResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn available_locales(&self) -> Vec<Locale> {
|
||||
vec![Locale::English, Locale::Spanish]
|
||||
}
|
||||
|
||||
async fn is_supported(&self, _locale: Locale) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
i18n_service: Arc::new(DummyI18nService),
|
||||
i18n_service: Arc::new(FileSystemI18nService::dummy()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new i18n application service
|
||||
pub fn new(i18n_service: Arc<dyn I18nService>) -> Self {
|
||||
pub fn new(i18n_service: Arc<FileSystemI18nService>) -> Self {
|
||||
Self { i18n_service }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
use crate::application::dtos::recent_dto::RecentItemDto;
|
||||
use crate::application::ports::recent_ports::{RecentItemsRepositoryPort, RecentItemsUseCase};
|
||||
use crate::common::errors::{DomainError, ErrorKind, Result};
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
use tracing::info;
|
||||
use crate::infrastructure::repositories::pg::RecentItemsPgRepository;
|
||||
|
||||
/// Implementation of the use case for managing recent items.
|
||||
///
|
||||
/// Depends on `RecentItemsRepositoryPort` (outbound port) instead
|
||||
/// of accessing `PgPool` directly, following the hexagonal architecture.
|
||||
pub struct RecentService {
|
||||
repo: Arc<dyn RecentItemsRepositoryPort>,
|
||||
repo: Arc<RecentItemsPgRepository>,
|
||||
max_recent_items: i32,
|
||||
}
|
||||
|
||||
impl RecentService {
|
||||
/// Create a new recent items service
|
||||
pub fn new(repo: Arc<dyn RecentItemsRepositoryPort>, max_recent_items: i32) -> Self {
|
||||
pub fn new(repo: Arc<RecentItemsPgRepository>, max_recent_items: i32) -> Self {
|
||||
Self {
|
||||
repo,
|
||||
max_recent_items: max_recent_items.clamp(1, 100),
|
||||
@@ -24,7 +24,6 @@ impl RecentService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl RecentItemsUseCase for RecentService {
|
||||
/// Get recent items for a user
|
||||
async fn get_recent_items(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use std::cmp::Reverse;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -13,11 +12,13 @@ use crate::application::dtos::search_dto::{
|
||||
SearchSuggestionItem, SearchSuggestionsDto,
|
||||
};
|
||||
use crate::application::ports::inbound::SearchUseCase;
|
||||
use crate::application::ports::outbound::FolderStoragePort;
|
||||
use crate::application::ports::storage_ports::FileReadPort;
|
||||
use crate::common::errors::Result;
|
||||
use crate::domain::entities::folder::Folder;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use crate::infrastructure::repositories::pg::file_blob_read_repository::FileBlobReadRepository;
|
||||
use crate::infrastructure::repositories::pg::folder_db_repository::FolderDbRepository;
|
||||
use crate::domain::repositories::folder_repository::FolderRepository;
|
||||
|
||||
/**
|
||||
* High-performance search service implementation for files and folders.
|
||||
@@ -37,10 +38,10 @@ use std::hash::{Hash, Hasher};
|
||||
*/
|
||||
pub struct SearchService {
|
||||
/// Repository for file operations
|
||||
file_repository: Arc<dyn FileReadPort>,
|
||||
file_repository: Arc<FileBlobReadRepository>,
|
||||
|
||||
/// Repository for folder operations
|
||||
folder_repository: Arc<dyn FolderStoragePort>,
|
||||
folder_repository: Arc<FolderDbRepository>,
|
||||
|
||||
/// Lock-free concurrent cache with automatic TTL and LRU eviction (moka).
|
||||
/// Values are `Arc<SearchResultsDto>` so cache insert/hit is a single
|
||||
@@ -109,8 +110,8 @@ impl SearchService {
|
||||
* Creates a new instance of the search service.
|
||||
*/
|
||||
pub fn new(
|
||||
file_repository: Arc<dyn FileReadPort>,
|
||||
folder_repository: Arc<dyn FolderStoragePort>,
|
||||
file_repository: Arc<FileBlobReadRepository>,
|
||||
folder_repository: Arc<FolderDbRepository>,
|
||||
cache_ttl: u64,
|
||||
max_cache_size: usize,
|
||||
) -> Self {
|
||||
@@ -255,7 +256,6 @@ impl SearchService {
|
||||
|
||||
// ─── SearchUseCase trait implementation ──────────────────────────────────
|
||||
|
||||
#[async_trait]
|
||||
impl SearchUseCase for SearchService {
|
||||
/**
|
||||
* Performs a search based on the specified criteria.
|
||||
@@ -489,7 +489,6 @@ impl SearchService {
|
||||
pub fn new_stub() -> impl SearchUseCase {
|
||||
struct SearchServiceStub;
|
||||
|
||||
#[async_trait]
|
||||
impl SearchUseCase for SearchServiceStub {
|
||||
async fn search(
|
||||
&self,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
@@ -12,7 +11,6 @@ use crate::{
|
||||
},
|
||||
ports::{
|
||||
auth_ports::PasswordHasherPort,
|
||||
outbound::FolderStoragePort,
|
||||
share_ports::{ShareStoragePort, ShareUseCase},
|
||||
storage_ports::FileReadPort,
|
||||
},
|
||||
@@ -20,6 +18,11 @@ use crate::{
|
||||
common::{config::AppConfig, errors::DomainError},
|
||||
domain::entities::share::{Share, ShareItemType, SharePermissions},
|
||||
};
|
||||
use crate::infrastructure::services::password_hasher::Argon2PasswordHasher;
|
||||
use crate::infrastructure::repositories::pg::file_blob_read_repository::FileBlobReadRepository;
|
||||
use crate::infrastructure::repositories::pg::folder_db_repository::FolderDbRepository;
|
||||
use crate::infrastructure::repositories::pg::SharePgRepository;
|
||||
use crate::domain::repositories::folder_repository::FolderRepository;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ShareServiceError {
|
||||
@@ -67,10 +70,10 @@ const MAX_CONCURRENT_HASHES: usize = 2;
|
||||
|
||||
pub struct ShareService {
|
||||
config: Arc<AppConfig>,
|
||||
share_repository: Arc<dyn ShareStoragePort>,
|
||||
file_repository: Arc<dyn FileReadPort>,
|
||||
folder_repository: Arc<dyn FolderStoragePort>,
|
||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
||||
share_repository: Arc<SharePgRepository>,
|
||||
file_repository: Arc<FileBlobReadRepository>,
|
||||
folder_repository: Arc<FolderDbRepository>,
|
||||
password_hasher: Arc<Argon2PasswordHasher>,
|
||||
/// Bounds the number of in-flight Argon2 password hashes to avoid
|
||||
/// saturating the blocking thread pool and consuming excessive RAM.
|
||||
hash_semaphore: Arc<Semaphore>,
|
||||
@@ -79,10 +82,10 @@ pub struct ShareService {
|
||||
impl ShareService {
|
||||
pub fn new(
|
||||
config: Arc<AppConfig>,
|
||||
share_repository: Arc<dyn ShareStoragePort>,
|
||||
file_repository: Arc<dyn FileReadPort>,
|
||||
folder_repository: Arc<dyn FolderStoragePort>,
|
||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
||||
share_repository: Arc<SharePgRepository>,
|
||||
file_repository: Arc<FileBlobReadRepository>,
|
||||
folder_repository: Arc<FolderDbRepository>,
|
||||
password_hasher: Arc<Argon2PasswordHasher>,
|
||||
) -> Self {
|
||||
Self {
|
||||
config,
|
||||
@@ -139,7 +142,6 @@ impl ShareService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ShareUseCase for ShareService {
|
||||
async fn create_shared_link(
|
||||
&self,
|
||||
@@ -400,13 +402,11 @@ mod tests {
|
||||
use crate::application::ports::share_ports::ShareStoragePort;
|
||||
use crate::common::config::AppConfig;
|
||||
use crate::domain::repositories::folder_repository::FolderRepository;
|
||||
use async_trait::async_trait;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
struct MockPasswordHasher;
|
||||
|
||||
#[async_trait]
|
||||
impl PasswordHasherPort for MockPasswordHasher {
|
||||
async fn hash_password(&self, password: &str) -> Result<String, DomainError> {
|
||||
Ok(format!("hashed_{}", password))
|
||||
@@ -420,7 +420,6 @@ mod tests {
|
||||
struct MockFileRepository;
|
||||
struct MockFolderRepository;
|
||||
|
||||
#[async_trait]
|
||||
impl FileReadPort for MockFileRepository {
|
||||
async fn get_file(
|
||||
&self,
|
||||
@@ -523,7 +522,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FolderRepository for MockFolderRepository {
|
||||
async fn create_folder(
|
||||
&self,
|
||||
@@ -671,7 +669,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ShareStoragePort for MockShareRepository {
|
||||
async fn save_share(&self, share: &Share) -> Result<Share, DomainError> {
|
||||
let mut shares = self.shares.lock().unwrap();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::application::ports::auth_ports::UserStoragePort;
|
||||
use crate::application::ports::storage_ports::StorageUsagePort;
|
||||
use crate::common::errors::DomainError;
|
||||
use async_trait::async_trait;
|
||||
use sqlx::PgPool;
|
||||
use std::sync::Arc;
|
||||
use tokio::task;
|
||||
use tracing::{debug, error, info};
|
||||
use crate::infrastructure::repositories::pg::UserPgRepository;
|
||||
|
||||
/**
|
||||
* Service for managing and updating user storage usage statistics.
|
||||
@@ -18,12 +18,12 @@ use tracing::{debug, error, info};
|
||||
*/
|
||||
pub struct StorageUsageService {
|
||||
pool: Arc<PgPool>,
|
||||
user_repository: Arc<dyn UserStoragePort>,
|
||||
user_repository: Arc<UserPgRepository>,
|
||||
}
|
||||
|
||||
impl StorageUsageService {
|
||||
/// Creates a new storage usage service
|
||||
pub fn new(pool: Arc<PgPool>, user_repository: Arc<dyn UserStoragePort>) -> Self {
|
||||
pub fn new(pool: Arc<PgPool>, user_repository: Arc<UserPgRepository>) -> Self {
|
||||
Self {
|
||||
pool,
|
||||
user_repository,
|
||||
@@ -111,7 +111,6 @@ impl StorageUsageService {
|
||||
* Implementation of the StorageUsagePort trait to expose storage usage services
|
||||
* to the application layer.
|
||||
*/
|
||||
#[async_trait]
|
||||
impl StorageUsagePort for StorageUsageService {
|
||||
async fn update_user_storage_usage(&self, user_id: &str) -> Result<i64, DomainError> {
|
||||
StorageUsageService::update_user_storage_usage(self, user_id).await
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, error, info, instrument};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::dtos::trash_dto::TrashedItemDto;
|
||||
use crate::application::ports::outbound::FolderStoragePort;
|
||||
use crate::application::ports::storage_ports::{FileReadPort, FileWritePort};
|
||||
use crate::application::ports::trash_ports::TrashUseCase;
|
||||
use crate::common::errors::{DomainError, ErrorKind, Result};
|
||||
use crate::domain::entities::trashed_item::{TrashedItem, TrashedItemType};
|
||||
use crate::domain::repositories::trash_repository::TrashRepository;
|
||||
use crate::infrastructure::repositories::pg::file_blob_read_repository::FileBlobReadRepository;
|
||||
use crate::infrastructure::repositories::pg::file_blob_write_repository::FileBlobWriteRepository;
|
||||
use crate::infrastructure::repositories::pg::folder_db_repository::FolderDbRepository;
|
||||
use crate::infrastructure::repositories::pg::trash_db_repository::TrashDbRepository;
|
||||
use crate::domain::repositories::folder_repository::FolderRepository;
|
||||
|
||||
/**
|
||||
* Application service for trash operations.
|
||||
@@ -26,16 +29,16 @@ use crate::domain::repositories::trash_repository::TrashRepository;
|
||||
*/
|
||||
pub struct TrashService {
|
||||
/// Repository for trash-specific operations like listing and retrieving trashed items
|
||||
trash_repository: Arc<dyn TrashRepository>,
|
||||
trash_repository: Arc<TrashDbRepository>,
|
||||
|
||||
/// Port for file read operations (get file metadata)
|
||||
file_read_port: Arc<dyn FileReadPort>,
|
||||
file_read_port: Arc<FileBlobReadRepository>,
|
||||
|
||||
/// Port for file write operations (trash, restore, delete)
|
||||
file_write_port: Arc<dyn FileWritePort>,
|
||||
file_write_port: Arc<FileBlobWriteRepository>,
|
||||
|
||||
/// Port for folder operations (get folder, trash, restore, delete)
|
||||
folder_storage_port: Arc<dyn FolderStoragePort>,
|
||||
folder_storage_port: Arc<FolderDbRepository>,
|
||||
|
||||
/// Number of days items should be kept in trash before automatic cleanup
|
||||
retention_days: u32,
|
||||
@@ -43,10 +46,10 @@ pub struct TrashService {
|
||||
|
||||
impl TrashService {
|
||||
pub fn new(
|
||||
trash_repository: Arc<dyn TrashRepository>,
|
||||
file_read_port: Arc<dyn FileReadPort>,
|
||||
file_write_port: Arc<dyn FileWritePort>,
|
||||
folder_storage_port: Arc<dyn FolderStoragePort>,
|
||||
trash_repository: Arc<TrashDbRepository>,
|
||||
file_read_port: Arc<FileBlobReadRepository>,
|
||||
file_write_port: Arc<FileBlobWriteRepository>,
|
||||
folder_storage_port: Arc<FolderDbRepository>,
|
||||
retention_days: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -118,7 +121,6 @@ impl TrashService {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TrashUseCase for TrashService {
|
||||
#[instrument(skip(self))]
|
||||
async fn get_trash_items(&self, user_id: &str) -> Result<Vec<TrashedItemDto>> {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use chrono::Utc;
|
||||
use futures::Stream;
|
||||
@@ -40,7 +39,6 @@ impl MockTrashRepository {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TrashRepository for MockTrashRepository {
|
||||
async fn add_to_trash(&self, item: &TrashedItem) -> Result<()> {
|
||||
let mut items = self.trash_items.lock().unwrap();
|
||||
@@ -135,7 +133,6 @@ impl MockFileRepository {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FileReadPort for MockFileRepository {
|
||||
async fn get_file(&self, id: &str) -> std::result::Result<File, DomainError> {
|
||||
let files = self.files.lock().unwrap();
|
||||
@@ -216,7 +213,6 @@ impl FileReadPort for MockFileRepository {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FileWritePort for MockFileRepository {
|
||||
async fn save_file_from_temp(
|
||||
&self,
|
||||
@@ -350,7 +346,6 @@ impl MockFolderRepository {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FolderRepository for MockFolderRepository {
|
||||
async fn create_folder(
|
||||
&self,
|
||||
@@ -514,8 +509,8 @@ mod tests {
|
||||
|
||||
let service = TrashService::new(
|
||||
trash_repo.clone(),
|
||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
||||
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||
folder_repo.clone(),
|
||||
30, // 30 days retention
|
||||
);
|
||||
@@ -588,8 +583,8 @@ mod tests {
|
||||
|
||||
let service = TrashService::new(
|
||||
trash_repo.clone(),
|
||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
||||
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||
folder_repo.clone(),
|
||||
30, // 30 days retention
|
||||
);
|
||||
@@ -653,8 +648,8 @@ mod tests {
|
||||
|
||||
let service = TrashService::new(
|
||||
trash_repo.clone(),
|
||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
||||
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||
folder_repo.clone(),
|
||||
30, // 30 days retention
|
||||
);
|
||||
@@ -723,8 +718,8 @@ mod tests {
|
||||
|
||||
let service = TrashService::new(
|
||||
trash_repo.clone(),
|
||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
||||
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||
folder_repo.clone(),
|
||||
30, // 30 days retention
|
||||
);
|
||||
@@ -792,8 +787,8 @@ mod tests {
|
||||
|
||||
let service = TrashService::new(
|
||||
trash_repo.clone(),
|
||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
||||
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||
folder_repo.clone(),
|
||||
30, // 30 days retention
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user