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:
Generated
-12
@@ -129,17 +129,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-trait"
|
|
||||||
version = "0.1.89"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async_zip"
|
name = "async_zip"
|
||||||
version = "0.0.18"
|
version = "0.0.18"
|
||||||
@@ -1819,7 +1808,6 @@ dependencies = [
|
|||||||
"argon2",
|
"argon2",
|
||||||
"async-compression",
|
"async-compression",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"async-trait",
|
|
||||||
"async_zip",
|
"async_zip",
|
||||||
"axum",
|
"axum",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ futures = "0.3.32"
|
|||||||
async-stream = "0.3.6"
|
async-stream = "0.3.6"
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
uuid = { version = "1.21.0", features = ["v4", "serde"] }
|
uuid = { version = "1.21.0", features = ["v4", "serde"] }
|
||||||
async-trait = "0.1.89"
|
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
mockall = { version = "0.14.0", optional = true }
|
mockall = { version = "0.14.0", optional = true }
|
||||||
sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio", "tls-rustls", "chrono", "uuid", "json"] }
|
sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio", "tls-rustls", "chrono", "uuid", "json"] }
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use crate::domain::entities::app_password::AppPassword;
|
|||||||
use crate::domain::entities::device_code::DeviceCode;
|
use crate::domain::entities::device_code::DeviceCode;
|
||||||
use crate::domain::entities::session::Session;
|
use crate::domain::entities::session::Session;
|
||||||
use crate::domain::entities::user::User;
|
use crate::domain::entities::user::User;
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Cryptography Ports - Extracted from Domain to maintain Clean Architecture
|
// Cryptography Ports - Extracted from Domain to maintain Clean Architecture
|
||||||
@@ -16,7 +15,6 @@ use async_trait::async_trait;
|
|||||||
///
|
///
|
||||||
/// Methods are async because implementations (e.g. Argon2) are CPU-intensive
|
/// Methods are async because implementations (e.g. Argon2) are CPU-intensive
|
||||||
/// and must run on a blocking thread pool to avoid starving Tokio workers.
|
/// and must run on a blocking thread pool to avoid starving Tokio workers.
|
||||||
#[async_trait]
|
|
||||||
pub trait PasswordHasherPort: Send + Sync + 'static {
|
pub trait PasswordHasherPort: Send + Sync + 'static {
|
||||||
/// Hash a plain text password
|
/// Hash a plain text password
|
||||||
async fn hash_password(&self, password: &str) -> Result<String, DomainError>;
|
async fn hash_password(&self, password: &str) -> Result<String, DomainError>;
|
||||||
@@ -69,7 +67,6 @@ pub trait TokenServicePort: Send + Sync + 'static {
|
|||||||
// Storage Ports
|
// Storage Ports
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait UserStoragePort: Send + Sync + 'static {
|
pub trait UserStoragePort: Send + Sync + 'static {
|
||||||
/// Creates a new user
|
/// Creates a new user
|
||||||
async fn create_user(&self, user: User) -> Result<User, DomainError>;
|
async fn create_user(&self, user: User) -> Result<User, DomainError>;
|
||||||
@@ -153,7 +150,6 @@ pub struct OidcIdClaims {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Port for OIDC operations — implemented in infrastructure layer
|
/// Port for OIDC operations — implemented in infrastructure layer
|
||||||
#[async_trait]
|
|
||||||
pub trait OidcServicePort: Send + Sync + 'static {
|
pub trait OidcServicePort: Send + Sync + 'static {
|
||||||
/// Get the authorization URL for redirecting the user to the IdP.
|
/// Get the authorization URL for redirecting the user to the IdP.
|
||||||
/// Includes PKCE code_challenge (S256) and nonce for ID token binding.
|
/// Includes PKCE code_challenge (S256) and nonce for ID token binding.
|
||||||
@@ -187,7 +183,6 @@ pub trait OidcServicePort: Send + Sync + 'static {
|
|||||||
fn provider_name(&self) -> &str;
|
fn provider_name(&self) -> &str;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait SessionStoragePort: Send + Sync + 'static {
|
pub trait SessionStoragePort: Send + Sync + 'static {
|
||||||
/// Creates a new session
|
/// Creates a new session
|
||||||
async fn create_session(&self, session: Session) -> Result<Session, DomainError>;
|
async fn create_session(&self, session: Session) -> Result<Session, DomainError>;
|
||||||
@@ -209,7 +204,6 @@ pub trait SessionStoragePort: Send + Sync + 'static {
|
|||||||
// Device Authorization Grant Port (RFC 8628)
|
// Device Authorization Grant Port (RFC 8628)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait DeviceCodeStoragePort: Send + Sync + 'static {
|
pub trait DeviceCodeStoragePort: Send + Sync + 'static {
|
||||||
/// Persist a new device code flow
|
/// Persist a new device code flow
|
||||||
async fn create_device_code(&self, device_code: DeviceCode) -> Result<DeviceCode, DomainError>;
|
async fn create_device_code(&self, device_code: DeviceCode) -> Result<DeviceCode, DomainError>;
|
||||||
@@ -238,7 +232,6 @@ pub trait DeviceCodeStoragePort: Send + Sync + 'static {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/// Storage port for application-specific passwords (HTTP Basic Auth for DAV clients).
|
/// Storage port for application-specific passwords (HTTP Basic Auth for DAV clients).
|
||||||
#[async_trait]
|
|
||||||
pub trait AppPasswordStoragePort: Send + Sync + 'static {
|
pub trait AppPasswordStoragePort: Send + Sync + 'static {
|
||||||
/// Persist a new app password (hash already computed).
|
/// Persist a new app password (hash already computed).
|
||||||
async fn create(&self, app_password: AppPassword) -> Result<AppPassword, DomainError>;
|
async fn create(&self, app_password: AppPassword) -> Result<AppPassword, DomainError>;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
//! implementation details.
|
//! implementation details.
|
||||||
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -30,7 +29,6 @@ pub struct WriteBehindStatsDto {
|
|||||||
/// Provides deferred write semantics: small files are held in memory
|
/// Provides deferred write semantics: small files are held in memory
|
||||||
/// and the response is returned immediately, while actual disk writes
|
/// and the response is returned immediately, while actual disk writes
|
||||||
/// happen asynchronously in the background.
|
/// happen asynchronously in the background.
|
||||||
#[async_trait]
|
|
||||||
pub trait WriteBehindCachePort: Send + Sync + 'static {
|
pub trait WriteBehindCachePort: Send + Sync + 'static {
|
||||||
/// Check if a file size is eligible for write-behind caching.
|
/// Check if a file size is eligible for write-behind caching.
|
||||||
fn is_eligible_size(&self, size: usize) -> bool;
|
fn is_eligible_size(&self, size: usize) -> bool;
|
||||||
@@ -82,7 +80,6 @@ pub struct CachedMetadataDto {
|
|||||||
///
|
///
|
||||||
/// Provides fast lookups for existence, size, timestamps and MIME types
|
/// Provides fast lookups for existence, size, timestamps and MIME types
|
||||||
/// without hitting the filesystem on every request.
|
/// without hitting the filesystem on every request.
|
||||||
#[async_trait]
|
|
||||||
pub trait MetadataCachePort: Send + Sync + 'static {
|
pub trait MetadataCachePort: Send + Sync + 'static {
|
||||||
/// Get cached metadata for a path, or `None` on miss / expired.
|
/// Get cached metadata for a path, or `None` on miss / expired.
|
||||||
async fn get_metadata(&self, path: &Path) -> Option<CachedMetadataDto>;
|
async fn get_metadata(&self, path: &Path) -> Option<CachedMetadataDto>;
|
||||||
@@ -106,7 +103,6 @@ pub trait MetadataCachePort: Send + Sync + 'static {
|
|||||||
///
|
///
|
||||||
/// Implementations should use LRU eviction and respect size limits so that
|
/// Implementations should use LRU eviction and respect size limits so that
|
||||||
/// the application layer never needs to know the concrete cache type.
|
/// the application layer never needs to know the concrete cache type.
|
||||||
#[async_trait]
|
|
||||||
pub trait ContentCachePort: Send + Sync + 'static {
|
pub trait ContentCachePort: Send + Sync + 'static {
|
||||||
/// Check whether a file of the given size should be cached.
|
/// Check whether a file of the given size should be cached.
|
||||||
fn should_cache(&self, size: usize) -> bool;
|
fn should_cache(&self, size: usize) -> bool;
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ use crate::application::dtos::calendar_dto::{
|
|||||||
UpdateCalendarDto, UpdateEventDto,
|
UpdateCalendarDto, UpdateEventDto,
|
||||||
};
|
};
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
/// Port for external calendar storage mechanisms
|
/// Port for external calendar storage mechanisms
|
||||||
#[async_trait]
|
|
||||||
pub trait CalendarStoragePort: Send + Sync + 'static {
|
pub trait CalendarStoragePort: Send + Sync + 'static {
|
||||||
// Calendar operations
|
// Calendar operations
|
||||||
async fn create_calendar(
|
async fn create_calendar(
|
||||||
@@ -111,7 +109,6 @@ pub trait CalendarStoragePort: Send + Sync + 'static {
|
|||||||
/// All methods require an explicit `user_id` parameter for authorization.
|
/// All methods require an explicit `user_id` parameter for authorization.
|
||||||
/// The CalDAV protocol handler extracts the user identity from JWT claims
|
/// The CalDAV protocol handler extracts the user identity from JWT claims
|
||||||
/// and passes it through.
|
/// and passes it through.
|
||||||
#[async_trait]
|
|
||||||
pub trait CalendarUseCase: Send + Sync + 'static {
|
pub trait CalendarUseCase: Send + Sync + 'static {
|
||||||
// Calendar operations
|
// Calendar operations
|
||||||
async fn create_calendar(
|
async fn create_calendar(
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ use crate::application::dtos::contact_dto::{
|
|||||||
GroupMembershipDto, UpdateContactDto, UpdateContactGroupDto,
|
GroupMembershipDto, UpdateContactDto, UpdateContactGroupDto,
|
||||||
};
|
};
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
pub type CardDavRepositoryError = DomainError;
|
pub type CardDavRepositoryError = DomainError;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait AddressBookUseCase: Send + Sync + 'static {
|
pub trait AddressBookUseCase: Send + Sync + 'static {
|
||||||
// Address Book operations
|
// Address Book operations
|
||||||
async fn create_address_book(
|
async fn create_address_book(
|
||||||
@@ -57,7 +55,6 @@ pub trait AddressBookUseCase: Send + Sync + 'static {
|
|||||||
) -> Result<Vec<(String, bool)>, DomainError>;
|
) -> Result<Vec<(String, bool)>, DomainError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait ContactUseCase: Send + Sync + 'static {
|
pub trait ContactUseCase: Send + Sync + 'static {
|
||||||
// Contact operations
|
// Contact operations
|
||||||
async fn create_contact(&self, dto: CreateContactDto) -> Result<ContactDto, DomainError>;
|
async fn create_contact(&self, dto: CreateContactDto) -> Result<ContactDto, DomainError>;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
//! the specific upload implementation (TUS-like protocol, S3 multipart, etc.).
|
//! the specific upload implementation (TUS-like protocol, S3 multipart, etc.).
|
||||||
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -53,7 +52,6 @@ pub struct UploadStatusResponseDto {
|
|||||||
/// Implementations manage upload sessions, chunk storage, reassembly,
|
/// Implementations manage upload sessions, chunk storage, reassembly,
|
||||||
/// and cleanup, while the application layer only interacts through
|
/// and cleanup, while the application layer only interacts through
|
||||||
/// this abstraction.
|
/// this abstraction.
|
||||||
#[async_trait]
|
|
||||||
pub trait ChunkedUploadPort: Send + Sync + 'static {
|
pub trait ChunkedUploadPort: Send + Sync + 'static {
|
||||||
/// Create a new upload session.
|
/// Create a new upload session.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
//! compression implementations (gzip, zstd, etc.).
|
//! compression implementations (gzip, zstd, etc.).
|
||||||
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
/// Compression level settings for file compression operations.
|
/// Compression level settings for file compression operations.
|
||||||
///
|
///
|
||||||
@@ -27,7 +26,6 @@ pub enum CompressionLevel {
|
|||||||
/// Implementations of this trait provide the actual compression logic
|
/// Implementations of this trait provide the actual compression logic
|
||||||
/// (e.g., gzip, zstd) while the application layer remains agnostic
|
/// (e.g., gzip, zstd) while the application layer remains agnostic
|
||||||
/// of the specific algorithm used.
|
/// of the specific algorithm used.
|
||||||
#[async_trait]
|
|
||||||
pub trait CompressionPort: Send + Sync + 'static {
|
pub trait CompressionPort: Send + Sync + 'static {
|
||||||
/// Compress data in memory.
|
/// Compress data in memory.
|
||||||
async fn compress_data(
|
async fn compress_data(
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
//! content-addressable storage implementation.
|
//! content-addressable storage implementation.
|
||||||
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -92,7 +91,6 @@ pub struct DedupStatsDto {
|
|||||||
/// Implementations store files by their content hash, eliminating
|
/// Implementations store files by their content hash, eliminating
|
||||||
/// duplicate storage automatically. Multiple file references can
|
/// duplicate storage automatically. Multiple file references can
|
||||||
/// point to the same physical blob.
|
/// point to the same physical blob.
|
||||||
#[async_trait]
|
|
||||||
pub trait DedupPort: Send + Sync + 'static {
|
pub trait DedupPort: Send + Sync + 'static {
|
||||||
/// Store content with deduplication (from bytes).
|
/// Store content with deduplication (from bytes).
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
use crate::application::dtos::favorites_dto::{BatchFavoritesResult, FavoriteItemDto};
|
use crate::application::dtos::favorites_dto::{BatchFavoritesResult, FavoriteItemDto};
|
||||||
use crate::common::errors::Result;
|
use crate::common::errors::Result;
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
/// Defines operations for managing user favorites
|
/// Defines operations for managing user favorites
|
||||||
#[async_trait]
|
|
||||||
pub trait FavoritesUseCase: Send + Sync {
|
pub trait FavoritesUseCase: Send + Sync {
|
||||||
/// Get all favorites for a user
|
/// Get all favorites for a user
|
||||||
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>>;
|
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>>;
|
||||||
@@ -40,7 +38,6 @@ pub trait FavoritesUseCase: Send + Sync {
|
|||||||
/// Application services depend on this trait instead of
|
/// Application services depend on this trait instead of
|
||||||
/// accessing `PgPool` directly. The concrete implementation
|
/// accessing `PgPool` directly. The concrete implementation
|
||||||
/// lives in `infrastructure::repositories::pg`.
|
/// lives in `infrastructure::repositories::pg`.
|
||||||
#[async_trait]
|
|
||||||
pub trait FavoritesRepositoryPort: Send + Sync + 'static {
|
pub trait FavoritesRepositoryPort: Send + Sync + 'static {
|
||||||
/// Gets all favorites for a user.
|
/// Gets all favorites for a user.
|
||||||
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>>;
|
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>>;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -8,6 +7,9 @@ use std::sync::Arc;
|
|||||||
use crate::application::dtos::file_dto::FileDto;
|
use crate::application::dtos::file_dto::FileDto;
|
||||||
use crate::application::ports::storage_ports::CopyFolderTreeResult;
|
use crate::application::ports::storage_ports::CopyFolderTreeResult;
|
||||||
use crate::common::errors::DomainError;
|
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::file_upload_service::FileUploadService;
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────
|
||||||
// Upload port
|
// Upload port
|
||||||
@@ -24,7 +26,6 @@ use crate::common::errors::DomainError;
|
|||||||
/// - Chunked uploads: chunks already on disk → `upload_file_from_path`
|
/// - Chunked uploads: chunks already on disk → `upload_file_from_path`
|
||||||
/// - WebDAV PUT (new): handler streams to temp file → `update_file_streaming`
|
/// - WebDAV PUT (new): handler streams to temp file → `update_file_streaming`
|
||||||
/// - WebDAV PUT (small/compat): `create_file` / `update_file` spool internally
|
/// - WebDAV PUT (small/compat): `create_file` / `update_file` spool internally
|
||||||
#[async_trait]
|
|
||||||
pub trait FileUploadUseCase: Send + Sync + 'static {
|
pub trait FileUploadUseCase: Send + Sync + 'static {
|
||||||
/// Upload from a temp file already on disk (true streaming, ~256 KB RAM).
|
/// Upload from a temp file already on disk (true streaming, ~256 KB RAM).
|
||||||
///
|
///
|
||||||
@@ -102,7 +103,6 @@ pub enum OptimizedFileContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Primary port for file retrieval operations
|
/// Primary port for file retrieval operations
|
||||||
#[async_trait]
|
|
||||||
pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||||
/// Gets a file by its ID
|
/// Gets a file by its ID
|
||||||
async fn get_file(&self, id: &str) -> Result<FileDto, DomainError>;
|
async fn get_file(&self, id: &str) -> Result<FileDto, DomainError>;
|
||||||
@@ -188,7 +188,6 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
|||||||
// ─────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Primary port for file management operations
|
/// Primary port for file management operations
|
||||||
#[async_trait]
|
|
||||||
pub trait FileManagementUseCase: Send + Sync + 'static {
|
pub trait FileManagementUseCase: Send + Sync + 'static {
|
||||||
/// Moves a file to another folder
|
/// Moves a file to another folder
|
||||||
async fn move_file(
|
async fn move_file(
|
||||||
@@ -241,7 +240,7 @@ pub trait FileManagementUseCase: Send + Sync + 'static {
|
|||||||
|
|
||||||
/// Factory for creating file use case implementations
|
/// Factory for creating file use case implementations
|
||||||
pub trait FileUseCaseFactory: Send + Sync + 'static {
|
pub trait FileUseCaseFactory: Send + Sync + 'static {
|
||||||
fn create_file_upload_use_case(&self) -> Arc<dyn FileUploadUseCase>;
|
fn create_file_upload_use_case(&self) -> Arc<FileUploadService>;
|
||||||
fn create_file_retrieval_use_case(&self) -> Arc<dyn FileRetrievalUseCase>;
|
fn create_file_retrieval_use_case(&self) -> Arc<FileRetrievalService>;
|
||||||
fn create_file_management_use_case(&self) -> Arc<dyn FileManagementUseCase>;
|
fn create_file_management_use_case(&self) -> Arc<FileManagementService>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::application::dtos::folder_dto::{
|
use crate::application::dtos::folder_dto::{
|
||||||
@@ -10,7 +9,6 @@ use crate::application::dtos::search_dto::{
|
|||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
|
|
||||||
/// Primary port for folder operations
|
/// Primary port for folder operations
|
||||||
#[async_trait]
|
|
||||||
pub trait FolderUseCase: Send + Sync + 'static {
|
pub trait FolderUseCase: Send + Sync + 'static {
|
||||||
/// Creates a new folder
|
/// Creates a new folder
|
||||||
async fn create_folder(&self, dto: CreateFolderDto) -> Result<FolderDto, DomainError>;
|
async fn create_folder(&self, dto: CreateFolderDto) -> Result<FolderDto, DomainError>;
|
||||||
@@ -89,7 +87,6 @@ pub trait FolderUseCase: Send + Sync + 'static {
|
|||||||
* All search processing (filtering, scoring, sorting, categorization)
|
* All search processing (filtering, scoring, sorting, categorization)
|
||||||
* is handled server-side in Rust for maximum efficiency.
|
* is handled server-side in Rust for maximum efficiency.
|
||||||
*/
|
*/
|
||||||
#[async_trait]
|
|
||||||
pub trait SearchUseCase: Send + Sync + 'static {
|
pub trait SearchUseCase: Send + Sync + 'static {
|
||||||
/// Performs a full search based on the specified criteria.
|
/// Performs a full search based on the specified criteria.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
@@ -10,7 +9,6 @@ pub use crate::domain::repositories::folder_repository::FolderRepository;
|
|||||||
use super::storage_ports::{FileReadPort, FileWritePort};
|
use super::storage_ports::{FileReadPort, FileWritePort};
|
||||||
|
|
||||||
/// Secondary port for storage operations
|
/// Secondary port for storage operations
|
||||||
#[async_trait]
|
|
||||||
pub trait StoragePort: Send + Sync + 'static {
|
pub trait StoragePort: Send + Sync + 'static {
|
||||||
/// Resolves a domain path to a physical path
|
/// Resolves a domain path to a physical path
|
||||||
fn resolve_path(&self, storage_path: &StoragePath) -> PathBuf;
|
fn resolve_path(&self, storage_path: &StoragePath) -> PathBuf;
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
use crate::application::dtos::recent_dto::RecentItemDto;
|
use crate::application::dtos::recent_dto::RecentItemDto;
|
||||||
use crate::common::errors::Result;
|
use crate::common::errors::Result;
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
/// Defines operations for managing user recent items
|
/// Defines operations for managing user recent items
|
||||||
#[async_trait]
|
|
||||||
pub trait RecentItemsUseCase: Send + Sync {
|
pub trait RecentItemsUseCase: Send + Sync {
|
||||||
/// Get all recent items for a user
|
/// Get all recent items for a user
|
||||||
async fn get_recent_items(
|
async fn get_recent_items(
|
||||||
@@ -36,7 +34,6 @@ pub trait RecentItemsUseCase: Send + Sync {
|
|||||||
///
|
///
|
||||||
/// Abstracts access to the `auth.user_recent_files` table so that
|
/// Abstracts access to the `auth.user_recent_files` table so that
|
||||||
/// `RecentService` does not depend directly on `PgPool`.
|
/// `RecentService` does not depend directly on `PgPool`.
|
||||||
#[async_trait]
|
|
||||||
pub trait RecentItemsRepositoryPort: Send + Sync + 'static {
|
pub trait RecentItemsRepositoryPort: Send + Sync + 'static {
|
||||||
/// Gets the latest recent items for a user (ordered by date desc).
|
/// Gets the latest recent items for a user (ordered by date desc).
|
||||||
async fn get_recent_items(&self, user_id: &str, limit: i32) -> Result<Vec<RecentItemDto>>;
|
async fn get_recent_items(&self, user_id: &str, limit: i32) -> Result<Vec<RecentItemDto>>;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
application::dtos::{
|
application::dtos::{
|
||||||
@@ -9,7 +8,6 @@ use crate::{
|
|||||||
domain::entities::share::ShareItemType,
|
domain::entities::share::ShareItemType,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait ShareUseCase: Send + Sync + 'static {
|
pub trait ShareUseCase: Send + Sync + 'static {
|
||||||
/// Create a new shared link for a file or folder
|
/// Create a new shared link for a file or folder
|
||||||
async fn create_shared_link(
|
async fn create_shared_link(
|
||||||
@@ -60,7 +58,6 @@ pub trait ShareUseCase: Send + Sync + 'static {
|
|||||||
async fn register_shared_link_access(&self, token: &str) -> Result<(), DomainError>;
|
async fn register_shared_link_access(&self, token: &str) -> Result<(), DomainError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait ShareStoragePort: Send + Sync + 'static {
|
pub trait ShareStoragePort: Send + Sync + 'static {
|
||||||
async fn save_share(
|
async fn save_share(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -25,7 +24,6 @@ pub use crate::domain::repositories::folder_repository::FolderRepository;
|
|||||||
///
|
///
|
||||||
/// Encapsulates every operation that queries state without modifying it:
|
/// Encapsulates every operation that queries state without modifying it:
|
||||||
/// get, list, content, stream, mmap, range, path resolution.
|
/// get, list, content, stream, mmap, range, path resolution.
|
||||||
#[async_trait]
|
|
||||||
pub trait FileReadPort: Send + Sync + 'static {
|
pub trait FileReadPort: Send + Sync + 'static {
|
||||||
/// Gets a file by its ID.
|
/// Gets a file by its ID.
|
||||||
async fn get_file(&self, id: &str) -> Result<File, DomainError>;
|
async fn get_file(&self, id: &str) -> Result<File, DomainError>;
|
||||||
@@ -199,7 +197,6 @@ pub struct CopyFolderTreeResult {
|
|||||||
///
|
///
|
||||||
/// Covers: upload (buffered + streaming), move, delete, update,
|
/// Covers: upload (buffered + streaming), move, delete, update,
|
||||||
/// and deferred registration for the write-behind cache.
|
/// and deferred registration for the write-behind cache.
|
||||||
#[async_trait]
|
|
||||||
pub trait FileWritePort: Send + Sync + 'static {
|
pub trait FileWritePort: Send + Sync + 'static {
|
||||||
/// Streaming upload — saves a file from a temp file already on disk.
|
/// Streaming upload — saves a file from a temp file already on disk.
|
||||||
///
|
///
|
||||||
@@ -307,7 +304,6 @@ pub trait FileWritePort: Send + Sync + 'static {
|
|||||||
// ─────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Secondary port for file path resolution
|
/// Secondary port for file path resolution
|
||||||
#[async_trait]
|
|
||||||
pub trait FilePathResolutionPort: Send + Sync + 'static {
|
pub trait FilePathResolutionPort: Send + Sync + 'static {
|
||||||
/// Gets the storage path of a file
|
/// Gets the storage path of a file
|
||||||
async fn get_file_path(&self, id: &str) -> Result<StoragePath, DomainError>;
|
async fn get_file_path(&self, id: &str) -> Result<StoragePath, DomainError>;
|
||||||
@@ -317,7 +313,6 @@ pub trait FilePathResolutionPort: Send + Sync + 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Secondary port for file/directory existence verification
|
/// Secondary port for file/directory existence verification
|
||||||
#[async_trait]
|
|
||||||
pub trait StorageVerificationPort: Send + Sync + 'static {
|
pub trait StorageVerificationPort: Send + Sync + 'static {
|
||||||
/// Checks whether a file exists at the given path
|
/// Checks whether a file exists at the given path
|
||||||
async fn file_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError>;
|
async fn file_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError>;
|
||||||
@@ -327,14 +322,12 @@ pub trait StorageVerificationPort: Send + Sync + 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Secondary port for directory management
|
/// Secondary port for directory management
|
||||||
#[async_trait]
|
|
||||||
pub trait DirectoryManagementPort: Send + Sync + 'static {
|
pub trait DirectoryManagementPort: Send + Sync + 'static {
|
||||||
/// Creates directories if they do not exist
|
/// Creates directories if they do not exist
|
||||||
async fn ensure_directory(&self, storage_path: &StoragePath) -> Result<(), DomainError>;
|
async fn ensure_directory(&self, storage_path: &StoragePath) -> Result<(), DomainError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Secondary port for storage usage management
|
/// Secondary port for storage usage management
|
||||||
#[async_trait]
|
|
||||||
pub trait StorageUsagePort: Send + Sync + 'static {
|
pub trait StorageUsagePort: Send + Sync + 'static {
|
||||||
/// Updates storage usage statistics for a user
|
/// Updates storage usage statistics for a user
|
||||||
async fn update_user_storage_usage(&self, user_id: &str) -> Result<i64, DomainError>;
|
async fn update_user_storage_usage(&self, user_id: &str) -> Result<i64, DomainError>;
|
||||||
@@ -362,7 +355,6 @@ pub trait StorageUsagePort: Send + Sync + 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generic storage service interface for calendar and contact services
|
/// Generic storage service interface for calendar and contact services
|
||||||
#[async_trait]
|
|
||||||
pub trait StorageUseCase: Send + Sync + 'static {
|
pub trait StorageUseCase: Send + Sync + 'static {
|
||||||
/// Handle a request with the specified action and parameters
|
/// Handle a request with the specified action and parameters
|
||||||
async fn handle_request(&self, action: &str, params: Value) -> Result<Value, DomainError>;
|
async fn handle_request(&self, action: &str, params: Value) -> Result<Value, DomainError>;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
//! image processing implementations.
|
//! image processing implementations.
|
||||||
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -63,7 +62,6 @@ pub struct ThumbnailStatsDto {
|
|||||||
/// Implementations handle the actual image processing, caching,
|
/// Implementations handle the actual image processing, caching,
|
||||||
/// and storage of thumbnails, while the application layer only
|
/// and storage of thumbnails, while the application layer only
|
||||||
/// interacts through this abstraction.
|
/// interacts through this abstraction.
|
||||||
#[async_trait]
|
|
||||||
pub trait ThumbnailPort: Send + Sync + 'static {
|
pub trait ThumbnailPort: Send + Sync + 'static {
|
||||||
/// Check if a file is an image that can have thumbnails.
|
/// Check if a file is an image that can have thumbnails.
|
||||||
fn is_supported_image(&self, mime_type: &str) -> bool;
|
fn is_supported_image(&self, mime_type: &str) -> bool;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
//! independent of specific image processing implementations.
|
//! independent of specific image processing implementations.
|
||||||
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
/// Supported output formats for image transcoding.
|
/// Supported output formats for image transcoding.
|
||||||
@@ -74,7 +73,6 @@ pub struct TranscodeStatsDto {
|
|||||||
/// Implementations handle the actual image conversion, caching,
|
/// Implementations handle the actual image conversion, caching,
|
||||||
/// and format detection, while the application layer only interacts
|
/// and format detection, while the application layer only interacts
|
||||||
/// through this abstraction.
|
/// through this abstraction.
|
||||||
#[async_trait]
|
|
||||||
pub trait ImageTranscodePort: Send + Sync + 'static {
|
pub trait ImageTranscodePort: Send + Sync + 'static {
|
||||||
/// Check if a MIME type can be transcoded.
|
/// Check if a MIME type can be transcoded.
|
||||||
fn can_transcode(&self, mime_type: &str) -> bool;
|
fn can_transcode(&self, mime_type: &str) -> bool;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
use crate::application::dtos::trash_dto::TrashedItemDto;
|
use crate::application::dtos::trash_dto::TrashedItemDto;
|
||||||
use crate::common::errors::Result;
|
use crate::common::errors::Result;
|
||||||
|
|
||||||
/// Port for trash-related use cases
|
/// Port for trash-related use cases
|
||||||
#[async_trait]
|
|
||||||
pub trait TrashUseCase: Send + Sync {
|
pub trait TrashUseCase: Send + Sync {
|
||||||
/// List items in the user's trash
|
/// List items in the user's trash
|
||||||
async fn get_trash_items(&self, user_id: &str) -> Result<Vec<TrashedItemDto>>;
|
async fn get_trash_items(&self, user_id: &str) -> Result<Vec<TrashedItemDto>>;
|
||||||
|
|||||||
@@ -5,14 +5,12 @@
|
|||||||
//! implementation details.
|
//! implementation details.
|
||||||
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
/// Port for ZIP archive operations.
|
/// Port for ZIP archive operations.
|
||||||
///
|
///
|
||||||
/// Implementations handle the actual ZIP file creation, compression,
|
/// Implementations handle the actual ZIP file creation, compression,
|
||||||
/// and recursive folder traversal.
|
/// and recursive folder traversal.
|
||||||
#[async_trait]
|
|
||||||
pub trait ZipPort: Send + Sync + 'static {
|
pub trait ZipPort: Send + Sync + 'static {
|
||||||
/// Create a ZIP archive containing the contents of a folder (recursively).
|
/// Create a ZIP archive containing the contents of a folder (recursively).
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ use crate::common::config::OidcConfig;
|
|||||||
use crate::common::errors::{DomainError, ErrorKind};
|
use crate::common::errors::{DomainError, ErrorKind};
|
||||||
use crate::domain::repositories::settings_repository::SettingsRepository;
|
use crate::domain::repositories::settings_repository::SettingsRepository;
|
||||||
use crate::infrastructure::services::oidc_service::OidcService;
|
use crate::infrastructure::services::oidc_service::OidcService;
|
||||||
|
use crate::infrastructure::repositories::pg::SettingsPgRepository;
|
||||||
|
|
||||||
/// Admin settings service — manages platform configuration in the database.
|
/// Admin settings service — manages platform configuration in the database.
|
||||||
///
|
///
|
||||||
/// Configuration priority: **env vars > DB settings > defaults**.
|
/// Configuration priority: **env vars > DB settings > defaults**.
|
||||||
/// Supports hot-reloading OIDC configuration without server restart.
|
/// Supports hot-reloading OIDC configuration without server restart.
|
||||||
pub struct AdminSettingsService {
|
pub struct AdminSettingsService {
|
||||||
settings_repo: Arc<dyn SettingsRepository>,
|
settings_repo: Arc<SettingsPgRepository>,
|
||||||
env_oidc_config: OidcConfig,
|
env_oidc_config: OidcConfig,
|
||||||
auth_app_service: Arc<AuthApplicationService>,
|
auth_app_service: Arc<AuthApplicationService>,
|
||||||
server_base_url: String,
|
server_base_url: String,
|
||||||
@@ -22,7 +23,7 @@ pub struct AdminSettingsService {
|
|||||||
|
|
||||||
impl AdminSettingsService {
|
impl AdminSettingsService {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
settings_repo: Arc<dyn SettingsRepository>,
|
settings_repo: Arc<SettingsPgRepository>,
|
||||||
env_oidc_config: OidcConfig,
|
env_oidc_config: OidcConfig,
|
||||||
auth_app_service: Arc<AuthApplicationService>,
|
auth_app_service: Arc<AuthApplicationService>,
|
||||||
server_base_url: String,
|
server_base_url: String,
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ use chrono::{Duration, Utc};
|
|||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration as StdDuration;
|
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).
|
/// App password token length (32 random alphanumeric chars after prefix).
|
||||||
const TOKEN_LENGTH: usize = 32;
|
const TOKEN_LENGTH: usize = 32;
|
||||||
@@ -39,9 +42,9 @@ struct CachedBasicAuthResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct AppPasswordService {
|
pub struct AppPasswordService {
|
||||||
repo: Arc<dyn AppPasswordStoragePort>,
|
repo: Arc<AppPasswordPgRepository>,
|
||||||
hasher: Arc<dyn PasswordHasherPort>,
|
hasher: Arc<Argon2PasswordHasher>,
|
||||||
user_repo: Arc<dyn UserStoragePort>,
|
user_repo: Arc<UserPgRepository>,
|
||||||
base_url: String,
|
base_url: String,
|
||||||
|
|
||||||
/// In-memory cache of successful Basic Auth verifications.
|
/// In-memory cache of successful Basic Auth verifications.
|
||||||
@@ -59,9 +62,9 @@ pub struct AppPasswordService {
|
|||||||
|
|
||||||
impl AppPasswordService {
|
impl AppPasswordService {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
repo: Arc<dyn AppPasswordStoragePort>,
|
repo: Arc<AppPasswordPgRepository>,
|
||||||
hasher: Arc<dyn PasswordHasherPort>,
|
hasher: Arc<Argon2PasswordHasher>,
|
||||||
user_repo: Arc<dyn UserStoragePort>,
|
user_repo: Arc<UserPgRepository>,
|
||||||
base_url: String,
|
base_url: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let auth_cache = Cache::builder()
|
let auth_cache = Cache::builder()
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ use std::path::PathBuf;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::time::Duration;
|
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)
|
/// Tracks a pending OIDC authorization flow (CSRF + PKCE + nonce)
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -31,7 +37,7 @@ struct PendingOidcToken {
|
|||||||
|
|
||||||
/// Interior state for OIDC — protected by RwLock for hot-reload.
|
/// Interior state for OIDC — protected by RwLock for hot-reload.
|
||||||
struct OidcState {
|
struct OidcState {
|
||||||
service: Option<Arc<dyn OidcServicePort>>,
|
service: Option<Arc<OidcService>>,
|
||||||
config: Option<OidcConfig>,
|
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
|
const DEFAULT_USER_QUOTA: i64 = 1_073_741_824; // 1 GB
|
||||||
|
|
||||||
pub struct AuthApplicationService {
|
pub struct AuthApplicationService {
|
||||||
user_storage: Arc<dyn UserStoragePort>,
|
user_storage: Arc<UserPgRepository>,
|
||||||
session_storage: Arc<dyn SessionStoragePort>,
|
session_storage: Arc<SessionPgRepository>,
|
||||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
password_hasher: Arc<Argon2PasswordHasher>,
|
||||||
token_service: Arc<dyn TokenServicePort>,
|
token_service: Arc<JwtTokenService>,
|
||||||
folder_service: Option<Arc<dyn FolderUseCase>>,
|
folder_service: Option<Arc<FolderService>>,
|
||||||
/// Path to the storage directory, used for disk-space–aware quota calculation
|
/// Path to the storage directory, used for disk-space–aware quota calculation
|
||||||
storage_path: PathBuf,
|
storage_path: PathBuf,
|
||||||
oidc: RwLock<OidcState>,
|
oidc: RwLock<OidcState>,
|
||||||
@@ -58,10 +64,10 @@ pub struct AuthApplicationService {
|
|||||||
|
|
||||||
impl AuthApplicationService {
|
impl AuthApplicationService {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
user_storage: Arc<dyn UserStoragePort>,
|
user_storage: Arc<UserPgRepository>,
|
||||||
session_storage: Arc<dyn SessionStoragePort>,
|
session_storage: Arc<SessionPgRepository>,
|
||||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
password_hasher: Arc<Argon2PasswordHasher>,
|
||||||
token_service: Arc<dyn TokenServicePort>,
|
token_service: Arc<JwtTokenService>,
|
||||||
storage_path: PathBuf,
|
storage_path: PathBuf,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -133,7 +139,7 @@ impl AuthApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Configures the folder service, needed to create personal folders
|
/// 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.folder_service = Some(folder_service);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -141,7 +147,7 @@ impl AuthApplicationService {
|
|||||||
/// Configures the OIDC service
|
/// Configures the OIDC service
|
||||||
pub fn with_oidc(
|
pub fn with_oidc(
|
||||||
self,
|
self,
|
||||||
oidc_service: Arc<dyn OidcServicePort>,
|
oidc_service: Arc<OidcService>,
|
||||||
oidc_config: OidcConfig,
|
oidc_config: OidcConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
{
|
{
|
||||||
@@ -153,7 +159,7 @@ impl AuthApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Hot-reload OIDC configuration at runtime (called from admin settings service)
|
/// 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();
|
let mut state = self.oidc.write().unwrap();
|
||||||
state.service = Some(oidc_service);
|
state.service = Some(oidc_service);
|
||||||
state.config = Some(oidc_config);
|
state.config = Some(oidc_config);
|
||||||
@@ -188,7 +194,7 @@ impl AuthApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an Arc clone of the OIDC service if available
|
/// 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();
|
let state = self.oidc.read().unwrap();
|
||||||
state.service.clone()
|
state.service.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ use crate::application::ports::trash_ports::TrashUseCase;
|
|||||||
use crate::application::services::folder_service::FolderService;
|
use crate::application::services::folder_service::FolderService;
|
||||||
use crate::common::config::AppConfig;
|
use crate::common::config::AppConfig;
|
||||||
use crate::common::errors::DomainError;
|
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
|
/// Specific errors for batch operations
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
@@ -65,18 +68,18 @@ pub struct BatchStats {
|
|||||||
|
|
||||||
/// Batch operations service
|
/// Batch operations service
|
||||||
pub struct BatchOperationService {
|
pub struct BatchOperationService {
|
||||||
file_retrieval: Arc<dyn FileRetrievalUseCase>,
|
file_retrieval: Arc<FileRetrievalService>,
|
||||||
file_management: Arc<dyn FileManagementUseCase>,
|
file_management: Arc<FileManagementService>,
|
||||||
folder_service: Arc<FolderService>,
|
folder_service: Arc<FolderService>,
|
||||||
trash_service: Option<Arc<dyn TrashUseCase>>,
|
trash_service: Option<Arc<TrashService>>,
|
||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BatchOperationService {
|
impl BatchOperationService {
|
||||||
/// Creates a new instance of the batch operations service
|
/// Creates a new instance of the batch operations service
|
||||||
pub fn new(
|
pub fn new(
|
||||||
file_retrieval: Arc<dyn FileRetrievalUseCase>,
|
file_retrieval: Arc<FileRetrievalService>,
|
||||||
file_management: Arc<dyn FileManagementUseCase>,
|
file_management: Arc<FileManagementService>,
|
||||||
folder_service: Arc<FolderService>,
|
folder_service: Arc<FolderService>,
|
||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -91,8 +94,8 @@ impl BatchOperationService {
|
|||||||
|
|
||||||
/// Creates a new instance with default configuration
|
/// Creates a new instance with default configuration
|
||||||
pub fn default(
|
pub fn default(
|
||||||
file_retrieval: Arc<dyn FileRetrievalUseCase>,
|
file_retrieval: Arc<FileRetrievalService>,
|
||||||
file_management: Arc<dyn FileManagementUseCase>,
|
file_management: Arc<FileManagementService>,
|
||||||
folder_service: Arc<FolderService>,
|
folder_service: Arc<FolderService>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
@@ -104,7 +107,7 @@ impl BatchOperationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set the optional trash service (enables batch trash operations)
|
/// 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.trash_service = Some(trash_service);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -8,18 +7,18 @@ use crate::application::dtos::calendar_dto::{
|
|||||||
};
|
};
|
||||||
use crate::application::ports::calendar_ports::{CalendarStoragePort, CalendarUseCase};
|
use crate::application::ports::calendar_ports::{CalendarStoragePort, CalendarUseCase};
|
||||||
use crate::common::errors::{DomainError, ErrorKind};
|
use crate::common::errors::{DomainError, ErrorKind};
|
||||||
|
use crate::infrastructure::adapters::calendar_storage_adapter::CalendarStorageAdapter;
|
||||||
|
|
||||||
pub struct CalendarService {
|
pub struct CalendarService {
|
||||||
calendar_storage: Arc<dyn CalendarStoragePort>,
|
calendar_storage: Arc<CalendarStorageAdapter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CalendarService {
|
impl CalendarService {
|
||||||
pub fn new(calendar_storage: Arc<dyn CalendarStoragePort>) -> Self {
|
pub fn new(calendar_storage: Arc<CalendarStorageAdapter>) -> Self {
|
||||||
Self { calendar_storage }
|
Self { calendar_storage }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl CalendarUseCase for CalendarService {
|
impl CalendarUseCase for CalendarService {
|
||||||
async fn create_calendar(
|
async fn create_calendar(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use sqlx::types::Uuid;
|
use sqlx::types::Uuid;
|
||||||
use std::sync::Arc;
|
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::entities::contact::{Address, AddressBook, Contact, ContactGroup, Email, Phone};
|
||||||
use crate::domain::repositories::address_book_repository::AddressBookRepository;
|
use crate::domain::repositories::address_book_repository::AddressBookRepository;
|
||||||
use crate::domain::repositories::contact_repository::{ContactGroupRepository, ContactRepository};
|
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 {
|
pub struct ContactService {
|
||||||
address_book_repository: Arc<dyn AddressBookRepository>,
|
address_book_repository: Arc<AddressBookPgRepository>,
|
||||||
contact_repository: Arc<dyn ContactRepository>,
|
contact_repository: Arc<ContactPgRepository>,
|
||||||
contact_group_repository: Arc<dyn ContactGroupRepository>,
|
contact_group_repository: Arc<ContactGroupPgRepository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContactService {
|
impl ContactService {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
address_book_repository: Arc<dyn AddressBookRepository>,
|
address_book_repository: Arc<AddressBookPgRepository>,
|
||||||
contact_repository: Arc<dyn ContactRepository>,
|
contact_repository: Arc<ContactPgRepository>,
|
||||||
contact_group_repository: Arc<dyn ContactGroupRepository>,
|
contact_group_repository: Arc<ContactGroupPgRepository>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
address_book_repository,
|
address_book_repository,
|
||||||
@@ -264,7 +266,6 @@ impl ContactService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl AddressBookUseCase for ContactService {
|
impl AddressBookUseCase for ContactService {
|
||||||
async fn create_address_book(
|
async fn create_address_book(
|
||||||
&self,
|
&self,
|
||||||
@@ -514,7 +515,6 @@ impl AddressBookUseCase for ContactService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ContactUseCase for ContactService {
|
impl ContactUseCase for ContactService {
|
||||||
async fn create_contact(&self, dto: CreateContactDto) -> Result<ContactDto, DomainError> {
|
async fn create_contact(&self, dto: CreateContactDto) -> Result<ContactDto, DomainError> {
|
||||||
let address_book_id = Uuid::parse_str(&dto.address_book_id)
|
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 {
|
impl StorageUseCase for ContactService {
|
||||||
async fn handle_request(
|
async fn handle_request(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ use crate::application::ports::auth_ports::{
|
|||||||
use crate::common::errors::{DomainError, ErrorKind};
|
use crate::common::errors::{DomainError, ErrorKind};
|
||||||
use crate::domain::entities::device_code::{DeviceCode, DeviceCodeStatus};
|
use crate::domain::entities::device_code::{DeviceCode, DeviceCodeStatus};
|
||||||
use crate::domain::entities::session::Session;
|
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).
|
/// Default device code lifetime: 15 minutes (RFC 8628 recommends 5-30 min).
|
||||||
const DEVICE_CODE_LIFETIME_SECS: i64 = 900;
|
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;
|
const USER_CODE_DIGIT_LEN: usize = 4;
|
||||||
|
|
||||||
pub struct DeviceAuthService {
|
pub struct DeviceAuthService {
|
||||||
device_code_storage: Arc<dyn DeviceCodeStoragePort>,
|
device_code_storage: Arc<DeviceCodePgRepository>,
|
||||||
token_service: Arc<dyn TokenServicePort>,
|
token_service: Arc<JwtTokenService>,
|
||||||
user_storage: Arc<dyn UserStoragePort>,
|
user_storage: Arc<UserPgRepository>,
|
||||||
session_storage: Arc<dyn SessionStoragePort>,
|
session_storage: Arc<SessionPgRepository>,
|
||||||
/// Base URL of the server (e.g. "https://cloud.example.com")
|
/// Base URL of the server (e.g. "https://cloud.example.com")
|
||||||
base_url: String,
|
base_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceAuthService {
|
impl DeviceAuthService {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
device_code_storage: Arc<dyn DeviceCodeStoragePort>,
|
device_code_storage: Arc<DeviceCodePgRepository>,
|
||||||
token_service: Arc<dyn TokenServicePort>,
|
token_service: Arc<JwtTokenService>,
|
||||||
user_storage: Arc<dyn UserStoragePort>,
|
user_storage: Arc<UserPgRepository>,
|
||||||
session_storage: Arc<dyn SessionStoragePort>,
|
session_storage: Arc<SessionPgRepository>,
|
||||||
base_url: String,
|
base_url: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -3,26 +3,25 @@ use crate::application::dtos::favorites_dto::{
|
|||||||
};
|
};
|
||||||
use crate::application::ports::favorites_ports::{FavoritesRepositoryPort, FavoritesUseCase};
|
use crate::application::ports::favorites_ports::{FavoritesRepositoryPort, FavoritesUseCase};
|
||||||
use crate::common::errors::{DomainError, ErrorKind, Result};
|
use crate::common::errors::{DomainError, ErrorKind, Result};
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
use crate::infrastructure::repositories::pg::FavoritesPgRepository;
|
||||||
|
|
||||||
/// Implementation of the FavoritesUseCase for managing user favorites.
|
/// Implementation of the FavoritesUseCase for managing user favorites.
|
||||||
///
|
///
|
||||||
/// Depends on `FavoritesRepositoryPort` (outbound port) instead of
|
/// Depends on `FavoritesRepositoryPort` (outbound port) instead of
|
||||||
/// accessing the database directly, following hexagonal architecture.
|
/// accessing the database directly, following hexagonal architecture.
|
||||||
pub struct FavoritesService {
|
pub struct FavoritesService {
|
||||||
repo: Arc<dyn FavoritesRepositoryPort>,
|
repo: Arc<FavoritesPgRepository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FavoritesService {
|
impl FavoritesService {
|
||||||
/// Create a new FavoritesService with the given repository port
|
/// 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 }
|
Self { repo }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FavoritesUseCase for FavoritesService {
|
impl FavoritesUseCase for FavoritesService {
|
||||||
/// Get all favorites for a user
|
/// Get all favorites for a user
|
||||||
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>> {
|
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 std::sync::Arc;
|
||||||
|
|
||||||
use crate::application::dtos::file_dto::FileDto;
|
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::application::ports::trash_ports::TrashUseCase;
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use tracing::{error, info, warn};
|
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).
|
/// 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
|
/// This service only orchestrates trash vs. permanent delete — it never
|
||||||
/// touches ref_count directly.
|
/// touches ref_count directly.
|
||||||
pub struct FileManagementService {
|
pub struct FileManagementService {
|
||||||
file_repository: Arc<dyn FileWritePort>,
|
file_repository: Arc<FileBlobWriteRepository>,
|
||||||
trash_service: Option<Arc<dyn TrashUseCase>>,
|
trash_service: Option<Arc<TrashService>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileManagementService {
|
impl FileManagementService {
|
||||||
/// Creates a new FileManagementService.
|
/// Creates a new FileManagementService.
|
||||||
pub fn new(file_repository: Arc<dyn FileWritePort>) -> Self {
|
pub fn new(file_repository: Arc<FileBlobWriteRepository>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_repository,
|
file_repository,
|
||||||
trash_service: None,
|
trash_service: None,
|
||||||
@@ -30,8 +31,8 @@ impl FileManagementService {
|
|||||||
|
|
||||||
/// Creates a FileManagementService with a trash service.
|
/// Creates a FileManagementService with a trash service.
|
||||||
pub fn with_trash(
|
pub fn with_trash(
|
||||||
file_repository: Arc<dyn FileWritePort>,
|
file_repository: Arc<FileBlobWriteRepository>,
|
||||||
trash_service: Option<Arc<dyn TrashUseCase>>,
|
trash_service: Option<Arc<TrashService>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_repository,
|
file_repository,
|
||||||
@@ -40,7 +41,6 @@ impl FileManagementService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileManagementUseCase for FileManagementService {
|
impl FileManagementUseCase for FileManagementService {
|
||||||
async fn move_file(
|
async fn move_file(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::application::dtos::file_dto::FileDto;
|
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::file_ports::{FileRetrievalUseCase, OptimizedFileContent};
|
||||||
use crate::application::ports::storage_ports::FileReadPort;
|
use crate::application::ports::storage_ports::FileReadPort;
|
||||||
use crate::application::ports::transcode_ports::{ImageTranscodePort, OutputFormat};
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use tracing::{debug, info};
|
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).
|
/// Threshold below which files are served from RAM cache (10 MB).
|
||||||
const CACHE_THRESHOLD: u64 = 10 * 1024 * 1024;
|
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 2: Memory-mapped I/O (10–100 MB)
|
||||||
/// - Tier 3: Streaming (≥100 MB)
|
/// - Tier 3: Streaming (≥100 MB)
|
||||||
pub struct FileRetrievalService {
|
pub struct FileRetrievalService {
|
||||||
file_read: Arc<dyn FileReadPort>,
|
file_read: Arc<FileBlobReadRepository>,
|
||||||
write_behind: Option<Arc<dyn WriteBehindCachePort>>,
|
content_cache: Option<Arc<FileContentCache>>,
|
||||||
content_cache: Option<Arc<dyn ContentCachePort>>,
|
transcode: Option<Arc<ImageTranscodeService>>,
|
||||||
transcode: Option<Arc<dyn ImageTranscodePort>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileRetrievalService {
|
impl FileRetrievalService {
|
||||||
/// Backward-compatible constructor (simple pass-through).
|
/// Backward-compatible constructor (simple pass-through).
|
||||||
pub fn new(file_repository: Arc<dyn FileReadPort>) -> Self {
|
pub fn new(file_repository: Arc<FileBlobReadRepository>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_read: file_repository,
|
file_read: file_repository,
|
||||||
write_behind: None,
|
|
||||||
content_cache: None,
|
content_cache: None,
|
||||||
transcode: 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.
|
/// Constructor for blob-storage model: read + content cache + transcode.
|
||||||
/// No write-behind needed — dedup handled at the repository layer.
|
|
||||||
pub fn new_with_cache(
|
pub fn new_with_cache(
|
||||||
file_read: Arc<dyn FileReadPort>,
|
file_read: Arc<FileBlobReadRepository>,
|
||||||
content_cache: Arc<dyn ContentCachePort>,
|
content_cache: Arc<FileContentCache>,
|
||||||
transcode: Arc<dyn ImageTranscodePort>,
|
transcode: Arc<ImageTranscodeService>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_read,
|
file_read,
|
||||||
write_behind: None,
|
|
||||||
content_cache: Some(content_cache),
|
content_cache: Some(content_cache),
|
||||||
transcode: Some(transcode),
|
transcode: Some(transcode),
|
||||||
}
|
}
|
||||||
@@ -85,7 +66,7 @@ impl FileRetrievalService {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let transcode = self.transcode.as_ref()?;
|
let transcode = self.transcode.as_ref()?;
|
||||||
if !transcode.should_transcode(mime, file_size) {
|
if !ImageTranscodeService::should_transcode(mime, file_size) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let format = OutputFormat::WebP;
|
let format = OutputFormat::WebP;
|
||||||
@@ -121,37 +102,6 @@ impl FileRetrievalService {
|
|||||||
let modified_at = dto.modified_at;
|
let modified_at = dto.modified_at;
|
||||||
let do_transcode = accept_webp && !prefer_original;
|
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) ──────────
|
// ── Tier 1: Hot cache + transcode (<10 MB) ──────────
|
||||||
if file_size < CACHE_THRESHOLD {
|
if file_size < CACHE_THRESHOLD {
|
||||||
// Check content cache first
|
// Check content cache first
|
||||||
@@ -243,7 +193,6 @@ impl FileRetrievalService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileRetrievalUseCase for FileRetrievalService {
|
impl FileRetrievalUseCase for FileRetrievalService {
|
||||||
async fn get_file(&self, id: &str) -> Result<FileDto, DomainError> {
|
async fn get_file(&self, id: &str) -> Result<FileDto, DomainError> {
|
||||||
let file = self.file_read.get_file(id).await?;
|
let file = self.file_read.get_file(id).await?;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
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::application::ports::storage_ports::{FileReadPort, FileWritePort};
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use tracing::{debug, info, warn};
|
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.
|
/// Helper function to extract username from folder path string.
|
||||||
/// e.g. "My Folder - user1/subfolder/file.txt" → "user1"
|
/// 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.
|
/// Peak RAM usage during any upload: ~256 KB (streaming hash) regardless of file size.
|
||||||
pub struct FileUploadService {
|
pub struct FileUploadService {
|
||||||
/// Write port — handles save, streaming, deferred registration
|
/// 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
|
/// Read port — needed for WebDAV create_file / update_file
|
||||||
file_read: Option<Arc<dyn FileReadPort>>,
|
file_read: Option<Arc<FileBlobReadRepository>>,
|
||||||
/// Optional storage usage tracking
|
/// Optional storage usage tracking
|
||||||
storage_usage_service:
|
storage_usage_service:
|
||||||
Option<Arc<dyn crate::application::ports::storage_ports::StorageUsagePort>>,
|
Option<Arc<StorageUsageService>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileUploadService {
|
impl FileUploadService {
|
||||||
/// Constructor with write port only (minimal).
|
/// Constructor with write port only (minimal).
|
||||||
pub fn new(file_repository: Arc<dyn FileWritePort>) -> Self {
|
pub fn new(file_repository: Arc<FileBlobWriteRepository>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_write: file_repository,
|
file_write: file_repository,
|
||||||
file_read: None,
|
file_read: None,
|
||||||
@@ -62,8 +64,8 @@ impl FileUploadService {
|
|||||||
|
|
||||||
/// Constructor for blob-storage model: write + read ports.
|
/// Constructor for blob-storage model: write + read ports.
|
||||||
pub fn new_with_read(
|
pub fn new_with_read(
|
||||||
file_write: Arc<dyn FileWritePort>,
|
file_write: Arc<FileBlobWriteRepository>,
|
||||||
file_read: Arc<dyn FileReadPort>,
|
file_read: Arc<FileBlobReadRepository>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_write,
|
file_write,
|
||||||
@@ -75,7 +77,7 @@ impl FileUploadService {
|
|||||||
/// Configures the storage usage service
|
/// Configures the storage usage service
|
||||||
pub fn with_storage_usage_service(
|
pub fn with_storage_usage_service(
|
||||||
mut self,
|
mut self,
|
||||||
storage_usage_service: Arc<dyn crate::application::ports::storage_ports::StorageUsagePort>,
|
storage_usage_service: Arc<StorageUsageService>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.storage_usage_service = Some(storage_usage_service);
|
self.storage_usage_service = Some(storage_usage_service);
|
||||||
self
|
self
|
||||||
@@ -106,7 +108,6 @@ impl FileUploadService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileUploadUseCase for FileUploadService {
|
impl FileUploadUseCase for FileUploadService {
|
||||||
/// Streaming upload from a temp file on disk.
|
/// Streaming upload from a temp file on disk.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::application::ports::file_ports::{
|
use crate::application::ports::file_ports::FileUseCaseFactory;
|
||||||
FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase, FileUseCaseFactory,
|
|
||||||
};
|
|
||||||
use crate::application::ports::storage_ports::{FileReadPort, FileWritePort};
|
|
||||||
use crate::application::services::file_management_service::FileManagementService;
|
use crate::application::services::file_management_service::FileManagementService;
|
||||||
use crate::application::services::file_retrieval_service::FileRetrievalService;
|
use crate::application::services::file_retrieval_service::FileRetrievalService;
|
||||||
use crate::application::services::file_upload_service::FileUploadService;
|
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
|
/// Factory for creating file use case implementations
|
||||||
pub struct AppFileUseCaseFactory {
|
pub struct AppFileUseCaseFactory {
|
||||||
file_read_repository: Arc<dyn FileReadPort>,
|
file_read_repository: Arc<FileBlobReadRepository>,
|
||||||
file_write_repository: Arc<dyn FileWritePort>,
|
file_write_repository: Arc<FileBlobWriteRepository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppFileUseCaseFactory {
|
impl AppFileUseCaseFactory {
|
||||||
/// Creates a new factory for file use cases
|
/// Creates a new factory for file use cases
|
||||||
pub fn new(
|
pub fn new(
|
||||||
file_read_repository: Arc<dyn FileReadPort>,
|
file_read_repository: Arc<FileBlobReadRepository>,
|
||||||
file_write_repository: Arc<dyn FileWritePort>,
|
file_write_repository: Arc<FileBlobWriteRepository>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_read_repository,
|
file_read_repository,
|
||||||
@@ -28,15 +27,15 @@ impl AppFileUseCaseFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FileUseCaseFactory for 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()))
|
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()))
|
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(
|
Arc::new(FileManagementService::new(
|
||||||
self.file_write_repository.clone(),
|
self.file_write_repository.clone(),
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -2,20 +2,20 @@ use crate::application::dtos::folder_dto::{
|
|||||||
CreateFolderDto, FolderDto, MoveFolderDto, RenameFolderDto,
|
CreateFolderDto, FolderDto, MoveFolderDto, RenameFolderDto,
|
||||||
};
|
};
|
||||||
use crate::application::ports::inbound::FolderUseCase;
|
use crate::application::ports::inbound::FolderUseCase;
|
||||||
use crate::application::ports::outbound::FolderStoragePort;
|
|
||||||
use crate::common::errors::{DomainError, ErrorKind};
|
use crate::common::errors::{DomainError, ErrorKind};
|
||||||
use crate::domain::services::path_service::StoragePath;
|
use crate::domain::services::path_service::StoragePath;
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::sync::Arc;
|
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
|
/// Implementation of the use case for folder operations
|
||||||
pub struct FolderService {
|
pub struct FolderService {
|
||||||
folder_storage: Arc<dyn FolderStoragePort>,
|
folder_storage: Arc<FolderDbRepository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FolderService {
|
impl FolderService {
|
||||||
/// Creates a new folder service
|
/// 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 }
|
Self { folder_storage }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +23,6 @@ impl FolderService {
|
|||||||
pub fn new_stub() -> impl FolderUseCase {
|
pub fn new_stub() -> impl FolderUseCase {
|
||||||
struct FolderServiceStub;
|
struct FolderServiceStub;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FolderUseCase for FolderServiceStub {
|
impl FolderUseCase for FolderServiceStub {
|
||||||
async fn create_folder(&self, _dto: CreateFolderDto) -> Result<FolderDto, DomainError> {
|
async fn create_folder(&self, _dto: CreateFolderDto) -> Result<FolderDto, DomainError> {
|
||||||
Ok(FolderDto::empty())
|
Ok(FolderDto::empty())
|
||||||
@@ -124,7 +123,6 @@ impl FolderService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FolderUseCase for FolderService {
|
impl FolderUseCase for FolderService {
|
||||||
/// Creates a new folder
|
/// Creates a new folder
|
||||||
async fn create_folder(&self, dto: CreateFolderDto) -> Result<FolderDto, DomainError> {
|
async fn create_folder(&self, dto: CreateFolderDto) -> Result<FolderDto, DomainError> {
|
||||||
|
|||||||
@@ -1,43 +1,23 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::domain::services::i18n_service::{I18nResult, I18nService, Locale};
|
use crate::domain::services::i18n_service::{I18nResult, I18nService, Locale};
|
||||||
|
use crate::infrastructure::services::file_system_i18n_service::FileSystemI18nService;
|
||||||
|
|
||||||
/// Service for i18n operations
|
/// Service for i18n operations
|
||||||
pub struct I18nApplicationService {
|
pub struct I18nApplicationService {
|
||||||
i18n_service: Arc<dyn I18nService>,
|
i18n_service: Arc<FileSystemI18nService>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl I18nApplicationService {
|
impl I18nApplicationService {
|
||||||
/// Creates a dummy service for testing
|
/// Creates a dummy service for testing
|
||||||
pub fn dummy() -> Self {
|
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 {
|
Self {
|
||||||
i18n_service: Arc::new(DummyI18nService),
|
i18n_service: Arc::new(FileSystemI18nService::dummy()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new i18n application service
|
/// 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 }
|
Self { i18n_service }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
use crate::application::dtos::recent_dto::RecentItemDto;
|
use crate::application::dtos::recent_dto::RecentItemDto;
|
||||||
use crate::application::ports::recent_ports::{RecentItemsRepositoryPort, RecentItemsUseCase};
|
use crate::application::ports::recent_ports::{RecentItemsRepositoryPort, RecentItemsUseCase};
|
||||||
use crate::common::errors::{DomainError, ErrorKind, Result};
|
use crate::common::errors::{DomainError, ErrorKind, Result};
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
use crate::infrastructure::repositories::pg::RecentItemsPgRepository;
|
||||||
|
|
||||||
/// Implementation of the use case for managing recent items.
|
/// Implementation of the use case for managing recent items.
|
||||||
///
|
///
|
||||||
/// Depends on `RecentItemsRepositoryPort` (outbound port) instead
|
/// Depends on `RecentItemsRepositoryPort` (outbound port) instead
|
||||||
/// of accessing `PgPool` directly, following the hexagonal architecture.
|
/// of accessing `PgPool` directly, following the hexagonal architecture.
|
||||||
pub struct RecentService {
|
pub struct RecentService {
|
||||||
repo: Arc<dyn RecentItemsRepositoryPort>,
|
repo: Arc<RecentItemsPgRepository>,
|
||||||
max_recent_items: i32,
|
max_recent_items: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RecentService {
|
impl RecentService {
|
||||||
/// Create a new recent items service
|
/// 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 {
|
Self {
|
||||||
repo,
|
repo,
|
||||||
max_recent_items: max_recent_items.clamp(1, 100),
|
max_recent_items: max_recent_items.clamp(1, 100),
|
||||||
@@ -24,7 +24,6 @@ impl RecentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl RecentItemsUseCase for RecentService {
|
impl RecentItemsUseCase for RecentService {
|
||||||
/// Get recent items for a user
|
/// Get recent items for a user
|
||||||
async fn get_recent_items(
|
async fn get_recent_items(
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@@ -13,11 +12,13 @@ use crate::application::dtos::search_dto::{
|
|||||||
SearchSuggestionItem, SearchSuggestionsDto,
|
SearchSuggestionItem, SearchSuggestionsDto,
|
||||||
};
|
};
|
||||||
use crate::application::ports::inbound::SearchUseCase;
|
use crate::application::ports::inbound::SearchUseCase;
|
||||||
use crate::application::ports::outbound::FolderStoragePort;
|
|
||||||
use crate::application::ports::storage_ports::FileReadPort;
|
use crate::application::ports::storage_ports::FileReadPort;
|
||||||
use crate::common::errors::Result;
|
use crate::common::errors::Result;
|
||||||
use crate::domain::entities::folder::Folder;
|
use crate::domain::entities::folder::Folder;
|
||||||
use std::hash::{Hash, Hasher};
|
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.
|
* High-performance search service implementation for files and folders.
|
||||||
@@ -37,10 +38,10 @@ use std::hash::{Hash, Hasher};
|
|||||||
*/
|
*/
|
||||||
pub struct SearchService {
|
pub struct SearchService {
|
||||||
/// Repository for file operations
|
/// Repository for file operations
|
||||||
file_repository: Arc<dyn FileReadPort>,
|
file_repository: Arc<FileBlobReadRepository>,
|
||||||
|
|
||||||
/// Repository for folder operations
|
/// Repository for folder operations
|
||||||
folder_repository: Arc<dyn FolderStoragePort>,
|
folder_repository: Arc<FolderDbRepository>,
|
||||||
|
|
||||||
/// Lock-free concurrent cache with automatic TTL and LRU eviction (moka).
|
/// Lock-free concurrent cache with automatic TTL and LRU eviction (moka).
|
||||||
/// Values are `Arc<SearchResultsDto>` so cache insert/hit is a single
|
/// 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.
|
* Creates a new instance of the search service.
|
||||||
*/
|
*/
|
||||||
pub fn new(
|
pub fn new(
|
||||||
file_repository: Arc<dyn FileReadPort>,
|
file_repository: Arc<FileBlobReadRepository>,
|
||||||
folder_repository: Arc<dyn FolderStoragePort>,
|
folder_repository: Arc<FolderDbRepository>,
|
||||||
cache_ttl: u64,
|
cache_ttl: u64,
|
||||||
max_cache_size: usize,
|
max_cache_size: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -255,7 +256,6 @@ impl SearchService {
|
|||||||
|
|
||||||
// ─── SearchUseCase trait implementation ──────────────────────────────────
|
// ─── SearchUseCase trait implementation ──────────────────────────────────
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SearchUseCase for SearchService {
|
impl SearchUseCase for SearchService {
|
||||||
/**
|
/**
|
||||||
* Performs a search based on the specified criteria.
|
* Performs a search based on the specified criteria.
|
||||||
@@ -489,7 +489,6 @@ impl SearchService {
|
|||||||
pub fn new_stub() -> impl SearchUseCase {
|
pub fn new_stub() -> impl SearchUseCase {
|
||||||
struct SearchServiceStub;
|
struct SearchServiceStub;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SearchUseCase for SearchServiceStub {
|
impl SearchUseCase for SearchServiceStub {
|
||||||
async fn search(
|
async fn search(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
|
|
||||||
@@ -12,7 +11,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
ports::{
|
ports::{
|
||||||
auth_ports::PasswordHasherPort,
|
auth_ports::PasswordHasherPort,
|
||||||
outbound::FolderStoragePort,
|
|
||||||
share_ports::{ShareStoragePort, ShareUseCase},
|
share_ports::{ShareStoragePort, ShareUseCase},
|
||||||
storage_ports::FileReadPort,
|
storage_ports::FileReadPort,
|
||||||
},
|
},
|
||||||
@@ -20,6 +18,11 @@ use crate::{
|
|||||||
common::{config::AppConfig, errors::DomainError},
|
common::{config::AppConfig, errors::DomainError},
|
||||||
domain::entities::share::{Share, ShareItemType, SharePermissions},
|
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)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ShareServiceError {
|
pub enum ShareServiceError {
|
||||||
@@ -67,10 +70,10 @@ const MAX_CONCURRENT_HASHES: usize = 2;
|
|||||||
|
|
||||||
pub struct ShareService {
|
pub struct ShareService {
|
||||||
config: Arc<AppConfig>,
|
config: Arc<AppConfig>,
|
||||||
share_repository: Arc<dyn ShareStoragePort>,
|
share_repository: Arc<SharePgRepository>,
|
||||||
file_repository: Arc<dyn FileReadPort>,
|
file_repository: Arc<FileBlobReadRepository>,
|
||||||
folder_repository: Arc<dyn FolderStoragePort>,
|
folder_repository: Arc<FolderDbRepository>,
|
||||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
password_hasher: Arc<Argon2PasswordHasher>,
|
||||||
/// Bounds the number of in-flight Argon2 password hashes to avoid
|
/// Bounds the number of in-flight Argon2 password hashes to avoid
|
||||||
/// saturating the blocking thread pool and consuming excessive RAM.
|
/// saturating the blocking thread pool and consuming excessive RAM.
|
||||||
hash_semaphore: Arc<Semaphore>,
|
hash_semaphore: Arc<Semaphore>,
|
||||||
@@ -79,10 +82,10 @@ pub struct ShareService {
|
|||||||
impl ShareService {
|
impl ShareService {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
config: Arc<AppConfig>,
|
config: Arc<AppConfig>,
|
||||||
share_repository: Arc<dyn ShareStoragePort>,
|
share_repository: Arc<SharePgRepository>,
|
||||||
file_repository: Arc<dyn FileReadPort>,
|
file_repository: Arc<FileBlobReadRepository>,
|
||||||
folder_repository: Arc<dyn FolderStoragePort>,
|
folder_repository: Arc<FolderDbRepository>,
|
||||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
password_hasher: Arc<Argon2PasswordHasher>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
@@ -139,7 +142,6 @@ impl ShareService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ShareUseCase for ShareService {
|
impl ShareUseCase for ShareService {
|
||||||
async fn create_shared_link(
|
async fn create_shared_link(
|
||||||
&self,
|
&self,
|
||||||
@@ -400,13 +402,11 @@ mod tests {
|
|||||||
use crate::application::ports::share_ports::ShareStoragePort;
|
use crate::application::ports::share_ports::ShareStoragePort;
|
||||||
use crate::common::config::AppConfig;
|
use crate::common::config::AppConfig;
|
||||||
use crate::domain::repositories::folder_repository::FolderRepository;
|
use crate::domain::repositories::folder_repository::FolderRepository;
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
struct MockPasswordHasher;
|
struct MockPasswordHasher;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl PasswordHasherPort for MockPasswordHasher {
|
impl PasswordHasherPort for MockPasswordHasher {
|
||||||
async fn hash_password(&self, password: &str) -> Result<String, DomainError> {
|
async fn hash_password(&self, password: &str) -> Result<String, DomainError> {
|
||||||
Ok(format!("hashed_{}", password))
|
Ok(format!("hashed_{}", password))
|
||||||
@@ -420,7 +420,6 @@ mod tests {
|
|||||||
struct MockFileRepository;
|
struct MockFileRepository;
|
||||||
struct MockFolderRepository;
|
struct MockFolderRepository;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileReadPort for MockFileRepository {
|
impl FileReadPort for MockFileRepository {
|
||||||
async fn get_file(
|
async fn get_file(
|
||||||
&self,
|
&self,
|
||||||
@@ -523,7 +522,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FolderRepository for MockFolderRepository {
|
impl FolderRepository for MockFolderRepository {
|
||||||
async fn create_folder(
|
async fn create_folder(
|
||||||
&self,
|
&self,
|
||||||
@@ -671,7 +669,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ShareStoragePort for MockShareRepository {
|
impl ShareStoragePort for MockShareRepository {
|
||||||
async fn save_share(&self, share: &Share) -> Result<Share, DomainError> {
|
async fn save_share(&self, share: &Share) -> Result<Share, DomainError> {
|
||||||
let mut shares = self.shares.lock().unwrap();
|
let mut shares = self.shares.lock().unwrap();
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::application::ports::auth_ports::UserStoragePort;
|
use crate::application::ports::auth_ports::UserStoragePort;
|
||||||
use crate::application::ports::storage_ports::StorageUsagePort;
|
use crate::application::ports::storage_ports::StorageUsagePort;
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
use crate::infrastructure::repositories::pg::UserPgRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for managing and updating user storage usage statistics.
|
* Service for managing and updating user storage usage statistics.
|
||||||
@@ -18,12 +18,12 @@ use tracing::{debug, error, info};
|
|||||||
*/
|
*/
|
||||||
pub struct StorageUsageService {
|
pub struct StorageUsageService {
|
||||||
pool: Arc<PgPool>,
|
pool: Arc<PgPool>,
|
||||||
user_repository: Arc<dyn UserStoragePort>,
|
user_repository: Arc<UserPgRepository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StorageUsageService {
|
impl StorageUsageService {
|
||||||
/// Creates a new storage usage service
|
/// 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 {
|
Self {
|
||||||
pool,
|
pool,
|
||||||
user_repository,
|
user_repository,
|
||||||
@@ -111,7 +111,6 @@ impl StorageUsageService {
|
|||||||
* Implementation of the StorageUsagePort trait to expose storage usage services
|
* Implementation of the StorageUsagePort trait to expose storage usage services
|
||||||
* to the application layer.
|
* to the application layer.
|
||||||
*/
|
*/
|
||||||
#[async_trait]
|
|
||||||
impl StorageUsagePort for StorageUsageService {
|
impl StorageUsagePort for StorageUsageService {
|
||||||
async fn update_user_storage_usage(&self, user_id: &str) -> Result<i64, DomainError> {
|
async fn update_user_storage_usage(&self, user_id: &str) -> Result<i64, DomainError> {
|
||||||
StorageUsageService::update_user_storage_usage(self, user_id).await
|
StorageUsageService::update_user_storage_usage(self, user_id).await
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{debug, error, info, instrument};
|
use tracing::{debug, error, info, instrument};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::application::dtos::trash_dto::TrashedItemDto;
|
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::storage_ports::{FileReadPort, FileWritePort};
|
||||||
use crate::application::ports::trash_ports::TrashUseCase;
|
use crate::application::ports::trash_ports::TrashUseCase;
|
||||||
use crate::common::errors::{DomainError, ErrorKind, Result};
|
use crate::common::errors::{DomainError, ErrorKind, Result};
|
||||||
use crate::domain::entities::trashed_item::{TrashedItem, TrashedItemType};
|
use crate::domain::entities::trashed_item::{TrashedItem, TrashedItemType};
|
||||||
use crate::domain::repositories::trash_repository::TrashRepository;
|
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.
|
* Application service for trash operations.
|
||||||
@@ -26,16 +29,16 @@ use crate::domain::repositories::trash_repository::TrashRepository;
|
|||||||
*/
|
*/
|
||||||
pub struct TrashService {
|
pub struct TrashService {
|
||||||
/// Repository for trash-specific operations like listing and retrieving trashed items
|
/// 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)
|
/// 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)
|
/// 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)
|
/// 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
|
/// Number of days items should be kept in trash before automatic cleanup
|
||||||
retention_days: u32,
|
retention_days: u32,
|
||||||
@@ -43,10 +46,10 @@ pub struct TrashService {
|
|||||||
|
|
||||||
impl TrashService {
|
impl TrashService {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
trash_repository: Arc<dyn TrashRepository>,
|
trash_repository: Arc<TrashDbRepository>,
|
||||||
file_read_port: Arc<dyn FileReadPort>,
|
file_read_port: Arc<FileBlobReadRepository>,
|
||||||
file_write_port: Arc<dyn FileWritePort>,
|
file_write_port: Arc<FileBlobWriteRepository>,
|
||||||
folder_storage_port: Arc<dyn FolderStoragePort>,
|
folder_storage_port: Arc<FolderDbRepository>,
|
||||||
retention_days: u32,
|
retention_days: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -118,7 +121,6 @@ impl TrashService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl TrashUseCase for TrashService {
|
impl TrashUseCase for TrashService {
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
async fn get_trash_items(&self, user_id: &str) -> Result<Vec<TrashedItemDto>> {
|
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 bytes::Bytes;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
@@ -40,7 +39,6 @@ impl MockTrashRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl TrashRepository for MockTrashRepository {
|
impl TrashRepository for MockTrashRepository {
|
||||||
async fn add_to_trash(&self, item: &TrashedItem) -> Result<()> {
|
async fn add_to_trash(&self, item: &TrashedItem) -> Result<()> {
|
||||||
let mut items = self.trash_items.lock().unwrap();
|
let mut items = self.trash_items.lock().unwrap();
|
||||||
@@ -135,7 +133,6 @@ impl MockFileRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileReadPort for MockFileRepository {
|
impl FileReadPort for MockFileRepository {
|
||||||
async fn get_file(&self, id: &str) -> std::result::Result<File, DomainError> {
|
async fn get_file(&self, id: &str) -> std::result::Result<File, DomainError> {
|
||||||
let files = self.files.lock().unwrap();
|
let files = self.files.lock().unwrap();
|
||||||
@@ -216,7 +213,6 @@ impl FileReadPort for MockFileRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileWritePort for MockFileRepository {
|
impl FileWritePort for MockFileRepository {
|
||||||
async fn save_file_from_temp(
|
async fn save_file_from_temp(
|
||||||
&self,
|
&self,
|
||||||
@@ -350,7 +346,6 @@ impl MockFolderRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FolderRepository for MockFolderRepository {
|
impl FolderRepository for MockFolderRepository {
|
||||||
async fn create_folder(
|
async fn create_folder(
|
||||||
&self,
|
&self,
|
||||||
@@ -514,8 +509,8 @@ mod tests {
|
|||||||
|
|
||||||
let service = TrashService::new(
|
let service = TrashService::new(
|
||||||
trash_repo.clone(),
|
trash_repo.clone(),
|
||||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||||
folder_repo.clone(),
|
folder_repo.clone(),
|
||||||
30, // 30 days retention
|
30, // 30 days retention
|
||||||
);
|
);
|
||||||
@@ -588,8 +583,8 @@ mod tests {
|
|||||||
|
|
||||||
let service = TrashService::new(
|
let service = TrashService::new(
|
||||||
trash_repo.clone(),
|
trash_repo.clone(),
|
||||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||||
folder_repo.clone(),
|
folder_repo.clone(),
|
||||||
30, // 30 days retention
|
30, // 30 days retention
|
||||||
);
|
);
|
||||||
@@ -653,8 +648,8 @@ mod tests {
|
|||||||
|
|
||||||
let service = TrashService::new(
|
let service = TrashService::new(
|
||||||
trash_repo.clone(),
|
trash_repo.clone(),
|
||||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||||
folder_repo.clone(),
|
folder_repo.clone(),
|
||||||
30, // 30 days retention
|
30, // 30 days retention
|
||||||
);
|
);
|
||||||
@@ -723,8 +718,8 @@ mod tests {
|
|||||||
|
|
||||||
let service = TrashService::new(
|
let service = TrashService::new(
|
||||||
trash_repo.clone(),
|
trash_repo.clone(),
|
||||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||||
folder_repo.clone(),
|
folder_repo.clone(),
|
||||||
30, // 30 days retention
|
30, // 30 days retention
|
||||||
);
|
);
|
||||||
@@ -792,8 +787,8 @@ mod tests {
|
|||||||
|
|
||||||
let service = TrashService::new(
|
let service = TrashService::new(
|
||||||
trash_repo.clone(),
|
trash_repo.clone(),
|
||||||
file_repo.clone() as Arc<dyn FileReadPort>,
|
file_repo.clone() as Arc<FileBlobReadRepository>,
|
||||||
file_repo.clone() as Arc<dyn FileWritePort>,
|
file_repo.clone() as Arc<FileBlobWriteRepository>,
|
||||||
folder_repo.clone(),
|
folder_repo.clone(),
|
||||||
30, // 30 days retention
|
30, // 30 days retention
|
||||||
);
|
);
|
||||||
|
|||||||
+87
-93
@@ -7,22 +7,7 @@ use crate::infrastructure::db::DbPools;
|
|||||||
use crate::application::services::admin_settings_service::AdminSettingsService;
|
use crate::application::services::admin_settings_service::AdminSettingsService;
|
||||||
use crate::application::services::auth_application_service::AuthApplicationService;
|
use crate::application::services::auth_application_service::AuthApplicationService;
|
||||||
|
|
||||||
use crate::application::ports::cache_ports::ContentCachePort;
|
use crate::application::ports::file_ports::FileUseCaseFactory;
|
||||||
use crate::application::ports::chunked_upload_ports::ChunkedUploadPort;
|
|
||||||
|
|
||||||
use crate::application::ports::dedup_ports::DedupPort;
|
|
||||||
use crate::application::ports::favorites_ports::FavoritesUseCase;
|
|
||||||
use crate::application::ports::file_ports::{
|
|
||||||
FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase, FileUseCaseFactory,
|
|
||||||
};
|
|
||||||
use crate::application::ports::inbound::{FolderUseCase, SearchUseCase};
|
|
||||||
use crate::application::ports::outbound::FolderStoragePort;
|
|
||||||
use crate::application::ports::recent_ports::RecentItemsUseCase;
|
|
||||||
use crate::application::ports::storage_ports::{FileReadPort, FileWritePort};
|
|
||||||
use crate::application::ports::thumbnail_ports::ThumbnailPort;
|
|
||||||
use crate::application::ports::transcode_ports::ImageTranscodePort;
|
|
||||||
use crate::application::ports::trash_ports::TrashUseCase;
|
|
||||||
use crate::application::ports::zip_ports::ZipPort;
|
|
||||||
use crate::application::services::favorites_service::FavoritesService;
|
use crate::application::services::favorites_service::FavoritesService;
|
||||||
use crate::application::services::folder_service::FolderService;
|
use crate::application::services::folder_service::FolderService;
|
||||||
use crate::application::services::i18n_application_service::I18nApplicationService;
|
use crate::application::services::i18n_application_service::I18nApplicationService;
|
||||||
@@ -35,7 +20,6 @@ use crate::application::services::{
|
|||||||
};
|
};
|
||||||
use crate::common::config::AppConfig;
|
use crate::common::config::AppConfig;
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use crate::domain::services::i18n_service::I18nService;
|
|
||||||
use crate::infrastructure::repositories::pg::SharePgRepository;
|
use crate::infrastructure::repositories::pg::SharePgRepository;
|
||||||
use crate::infrastructure::repositories::pg::{
|
use crate::infrastructure::repositories::pg::{
|
||||||
FileBlobReadRepository, FileBlobWriteRepository, FolderDbRepository, TrashDbRepository,
|
FileBlobReadRepository, FileBlobWriteRepository, FolderDbRepository, TrashDbRepository,
|
||||||
@@ -47,7 +31,29 @@ use crate::infrastructure::services::file_system_i18n_service::FileSystemI18nSer
|
|||||||
use crate::infrastructure::services::path_service::PathService;
|
use crate::infrastructure::services::path_service::PathService;
|
||||||
use crate::infrastructure::services::trash_cleanup_service::TrashCleanupService;
|
use crate::infrastructure::services::trash_cleanup_service::TrashCleanupService;
|
||||||
|
|
||||||
use crate::common::stubs::StubZipPort;
|
|
||||||
|
use crate::application::services::device_auth_service::DeviceAuthService;
|
||||||
|
use crate::application::services::app_password_service::AppPasswordService;
|
||||||
|
use crate::application::services::calendar_service::CalendarService;
|
||||||
|
use crate::application::services::storage_usage_service::StorageUsageService;
|
||||||
|
use crate::infrastructure::adapters::contact_storage_adapter::ContactStorageAdapter;
|
||||||
|
use crate::infrastructure::repositories::DeviceCodePgRepository;
|
||||||
|
use crate::infrastructure::repositories::AppPasswordPgRepository;
|
||||||
|
use crate::infrastructure::repositories::pg::{
|
||||||
|
AddressBookPgRepository, CalendarEventPgRepository, CalendarPgRepository,
|
||||||
|
ContactGroupPgRepository, ContactPgRepository, SessionPgRepository, UserPgRepository,
|
||||||
|
};
|
||||||
|
use crate::infrastructure::services::password_hasher::Argon2PasswordHasher;
|
||||||
|
use crate::infrastructure::services::jwt_service::JwtTokenService;
|
||||||
|
use crate::infrastructure::services::path_resolver_service::PathResolverService;
|
||||||
|
use crate::application::services::wopi_lock_service::WopiLockService;
|
||||||
|
use crate::application::services::wopi_token_service::WopiTokenService;
|
||||||
|
use crate::infrastructure::services::wopi_discovery_service::WopiDiscoveryService;
|
||||||
|
use crate::infrastructure::services::chunked_upload_service::ChunkedUploadService;
|
||||||
|
use crate::infrastructure::services::dedup_service::DedupService;
|
||||||
|
use crate::infrastructure::services::image_transcode_service::ImageTranscodeService;
|
||||||
|
use crate::infrastructure::services::thumbnail_service::ThumbnailService;
|
||||||
|
use crate::infrastructure::services::zip_service::ZipService;
|
||||||
|
|
||||||
/// Factory for the different application components
|
/// Factory for the different application components
|
||||||
///
|
///
|
||||||
@@ -161,7 +167,7 @@ impl AppServiceFactory {
|
|||||||
chunked_upload_service,
|
chunked_upload_service,
|
||||||
image_transcode_service,
|
image_transcode_service,
|
||||||
dedup_service,
|
dedup_service,
|
||||||
zip_service: Arc::new(StubZipPort), // Placeholder - replaced after app services init
|
zip_service: None, // Placeholder - replaced after app services init
|
||||||
config: self.config.clone(),
|
config: self.config.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -176,16 +182,16 @@ impl AppServiceFactory {
|
|||||||
) -> RepositoryServices {
|
) -> RepositoryServices {
|
||||||
// Folder repository — PostgreSQL-backed virtual folders
|
// Folder repository — PostgreSQL-backed virtual folders
|
||||||
let folder_repo_concrete = Arc::new(FolderDbRepository::new(db_pool.clone()));
|
let folder_repo_concrete = Arc::new(FolderDbRepository::new(db_pool.clone()));
|
||||||
let folder_repository: Arc<dyn FolderStoragePort> = folder_repo_concrete.clone();
|
let folder_repository: Arc<FolderDbRepository> = folder_repo_concrete.clone();
|
||||||
|
|
||||||
// File repositories — PostgreSQL metadata + blob content via DedupService
|
// File repositories — PostgreSQL metadata + blob content via DedupService
|
||||||
let file_read_repository: Arc<dyn FileReadPort> = Arc::new(FileBlobReadRepository::new(
|
let file_read_repository: Arc<FileBlobReadRepository> = Arc::new(FileBlobReadRepository::new(
|
||||||
db_pool.clone(),
|
db_pool.clone(),
|
||||||
core.dedup_service.clone(),
|
core.dedup_service.clone(),
|
||||||
folder_repo_concrete.clone(),
|
folder_repo_concrete.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let file_write_repository: Arc<dyn FileWritePort> = Arc::new(FileBlobWriteRepository::new(
|
let file_write_repository: Arc<FileBlobWriteRepository> = Arc::new(FileBlobWriteRepository::new(
|
||||||
db_pool.clone(),
|
db_pool.clone(),
|
||||||
core.dedup_service.clone(),
|
core.dedup_service.clone(),
|
||||||
folder_repo_concrete.clone(),
|
folder_repo_concrete.clone(),
|
||||||
@@ -201,7 +207,7 @@ impl AppServiceFactory {
|
|||||||
core.config.storage.trash_retention_days,
|
core.config.storage.trash_retention_days,
|
||||||
))
|
))
|
||||||
as Arc<
|
as Arc<
|
||||||
dyn crate::domain::repositories::trash_repository::TrashRepository,
|
TrashDbRepository,
|
||||||
>)
|
>)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -226,7 +232,7 @@ impl AppServiceFactory {
|
|||||||
&self,
|
&self,
|
||||||
core: &CoreServices,
|
core: &CoreServices,
|
||||||
repos: &RepositoryServices,
|
repos: &RepositoryServices,
|
||||||
trash_service: Option<Arc<dyn TrashUseCase>>,
|
trash_service: Option<Arc<TrashService>>,
|
||||||
) -> ApplicationServices {
|
) -> ApplicationServices {
|
||||||
// Main services
|
// Main services
|
||||||
let folder_service = Arc::new(FolderService::new(repos.folder_repository.clone()));
|
let folder_service = Arc::new(FolderService::new(repos.folder_repository.clone()));
|
||||||
@@ -258,7 +264,7 @@ impl AppServiceFactory {
|
|||||||
let i18n_service = Arc::new(I18nApplicationService::new(repos.i18n_repository.clone()));
|
let i18n_service = Arc::new(I18nApplicationService::new(repos.i18n_repository.clone()));
|
||||||
|
|
||||||
// Search service with cache
|
// Search service with cache
|
||||||
let search_service: Option<Arc<dyn SearchUseCase>> = Some(Arc::new(SearchService::new(
|
let search_service: Option<Arc<SearchService>> = Some(Arc::new(SearchService::new(
|
||||||
repos.file_read_repository.clone(),
|
repos.file_read_repository.clone(),
|
||||||
repos.folder_repository.clone(),
|
repos.folder_repository.clone(),
|
||||||
300, // Cache TTL in seconds (5 minutes)
|
300, // Cache TTL in seconds (5 minutes)
|
||||||
@@ -289,7 +295,7 @@ impl AppServiceFactory {
|
|||||||
pub async fn create_trash_service(
|
pub async fn create_trash_service(
|
||||||
&self,
|
&self,
|
||||||
repos: &RepositoryServices,
|
repos: &RepositoryServices,
|
||||||
) -> Option<Arc<dyn TrashUseCase>> {
|
) -> Option<Arc<TrashService>> {
|
||||||
if !self.config.features.enable_trash {
|
if !self.config.features.enable_trash {
|
||||||
tracing::info!("Trash service is disabled in configuration");
|
tracing::info!("Trash service is disabled in configuration");
|
||||||
return None;
|
return None;
|
||||||
@@ -315,7 +321,7 @@ impl AppServiceFactory {
|
|||||||
cleanup_service.start_cleanup_job().await;
|
cleanup_service.start_cleanup_job().await;
|
||||||
tracing::info!("Trash service initialized with daily cleanup schedule");
|
tracing::info!("Trash service initialized with daily cleanup schedule");
|
||||||
|
|
||||||
Some(service as Arc<dyn TrashUseCase>)
|
Some(service as Arc<TrashService>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the sharing service
|
/// Creates the sharing service
|
||||||
@@ -323,7 +329,7 @@ impl AppServiceFactory {
|
|||||||
&self,
|
&self,
|
||||||
repos: &RepositoryServices,
|
repos: &RepositoryServices,
|
||||||
db_pool: &Arc<PgPool>,
|
db_pool: &Arc<PgPool>,
|
||||||
) -> Option<Arc<dyn crate::application::ports::share_ports::ShareUseCase>> {
|
) -> Option<Arc<ShareService>> {
|
||||||
if !self.config.features.enable_file_sharing {
|
if !self.config.features.enable_file_sharing {
|
||||||
tracing::info!("File sharing service is disabled in configuration");
|
tracing::info!("File sharing service is disabled in configuration");
|
||||||
return None;
|
return None;
|
||||||
@@ -332,7 +338,7 @@ impl AppServiceFactory {
|
|||||||
let share_repository = Arc::new(SharePgRepository::new(db_pool.clone()));
|
let share_repository = Arc::new(SharePgRepository::new(db_pool.clone()));
|
||||||
|
|
||||||
// Build a password hasher for share password verification
|
// Build a password hasher for share password verification
|
||||||
let password_hasher: Arc<dyn crate::application::ports::auth_ports::PasswordHasherPort> =
|
let password_hasher: Arc<Argon2PasswordHasher> =
|
||||||
Arc::new(
|
Arc::new(
|
||||||
crate::infrastructure::services::password_hasher::Argon2PasswordHasher::new(
|
crate::infrastructure::services::password_hasher::Argon2PasswordHasher::new(
|
||||||
self.config.auth.hash_memory_cost,
|
self.config.auth.hash_memory_cost,
|
||||||
@@ -354,7 +360,7 @@ impl AppServiceFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the favorites service (requires database)
|
/// Creates the favorites service (requires database)
|
||||||
pub fn create_favorites_service(&self, db_pool: &Arc<PgPool>) -> Arc<dyn FavoritesUseCase> {
|
pub fn create_favorites_service(&self, db_pool: &Arc<PgPool>) -> Arc<FavoritesService> {
|
||||||
let repo = Arc::new(
|
let repo = Arc::new(
|
||||||
crate::infrastructure::repositories::pg::FavoritesPgRepository::new(db_pool.clone()),
|
crate::infrastructure::repositories::pg::FavoritesPgRepository::new(db_pool.clone()),
|
||||||
);
|
);
|
||||||
@@ -364,7 +370,7 @@ impl AppServiceFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the recent items service (requires database)
|
/// Creates the recent items service (requires database)
|
||||||
pub fn create_recent_service(&self, db_pool: &Arc<PgPool>) -> Arc<dyn RecentItemsUseCase> {
|
pub fn create_recent_service(&self, db_pool: &Arc<PgPool>) -> Arc<RecentService> {
|
||||||
let repo = Arc::new(
|
let repo = Arc::new(
|
||||||
crate::infrastructure::repositories::pg::RecentItemsPgRepository::new(db_pool.clone()),
|
crate::infrastructure::repositories::pg::RecentItemsPgRepository::new(db_pool.clone()),
|
||||||
);
|
);
|
||||||
@@ -406,7 +412,7 @@ impl AppServiceFactory {
|
|||||||
_repos: &RepositoryServices,
|
_repos: &RepositoryServices,
|
||||||
db_pool: &Arc<PgPool>,
|
db_pool: &Arc<PgPool>,
|
||||||
maintenance_pool: &Arc<PgPool>,
|
maintenance_pool: &Arc<PgPool>,
|
||||||
) -> Arc<dyn crate::application::ports::storage_ports::StorageUsagePort> {
|
) -> Arc<StorageUsageService> {
|
||||||
let user_repository = Arc::new(
|
let user_repository = Arc::new(
|
||||||
crate::infrastructure::repositories::pg::UserPgRepository::new(db_pool.clone()),
|
crate::infrastructure::repositories::pg::UserPgRepository::new(db_pool.clone()),
|
||||||
);
|
);
|
||||||
@@ -455,10 +461,10 @@ impl AppServiceFactory {
|
|||||||
apps.share_service = share_service.clone();
|
apps.share_service = share_service.clone();
|
||||||
|
|
||||||
// 6. Database-dependent services (PgPool always available in blob model)
|
// 6. Database-dependent services (PgPool always available in blob model)
|
||||||
let favorites_service: Option<Arc<dyn FavoritesUseCase>>;
|
let favorites_service: Option<Arc<FavoritesService>>;
|
||||||
let recent_service: Option<Arc<dyn RecentItemsUseCase>>;
|
let recent_service: Option<Arc<RecentService>>;
|
||||||
let storage_usage_service: Option<
|
let storage_usage_service: Option<
|
||||||
Arc<dyn crate::application::ports::storage_ports::StorageUsagePort>,
|
Arc<StorageUsageService>,
|
||||||
>;
|
>;
|
||||||
let mut auth_services: Option<crate::common::di::AuthServices> = None;
|
let mut auth_services: Option<crate::common::di::AuthServices> = None;
|
||||||
|
|
||||||
@@ -509,14 +515,14 @@ impl AppServiceFactory {
|
|||||||
self.preload_translations(&apps.i18n_service).await;
|
self.preload_translations(&apps.i18n_service).await;
|
||||||
|
|
||||||
// 8. Build the ZipService with real application services
|
// 8. Build the ZipService with real application services
|
||||||
let zip_service: Arc<dyn crate::application::ports::zip_ports::ZipPort> = Arc::new(
|
let zip_service: Arc<ZipService> = Arc::new(
|
||||||
crate::infrastructure::services::zip_service::ZipService::new(
|
crate::infrastructure::services::zip_service::ZipService::new(
|
||||||
apps.file_retrieval_service.clone(),
|
apps.file_retrieval_service.clone(),
|
||||||
apps.folder_service.clone(),
|
apps.folder_service.clone(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
let mut core = core;
|
let mut core = core;
|
||||||
core.zip_service = zip_service;
|
core.zip_service = Some(zip_service);
|
||||||
|
|
||||||
// 9. Assemble final AppState
|
// 9. Assemble final AppState
|
||||||
let mut app_state = AppState {
|
let mut app_state = AppState {
|
||||||
@@ -595,16 +601,14 @@ impl AppServiceFactory {
|
|||||||
|
|
||||||
// 9c. Wire Device Authorization Grant (RFC 8628) service
|
// 9c. Wire Device Authorization Grant (RFC 8628) service
|
||||||
{
|
{
|
||||||
use crate::application::services::device_auth_service::DeviceAuthService;
|
|
||||||
use crate::infrastructure::repositories::DeviceCodePgRepository;
|
|
||||||
|
|
||||||
let device_code_repo = Arc::new(DeviceCodePgRepository::new(pool.clone()));
|
let device_code_repo = Arc::new(DeviceCodePgRepository::new(pool.clone()));
|
||||||
let user_repo: Arc<dyn crate::application::ports::auth_ports::UserStoragePort> =
|
let user_repo: Arc<UserPgRepository> =
|
||||||
Arc::new(crate::infrastructure::repositories::UserPgRepository::new(
|
Arc::new(crate::infrastructure::repositories::UserPgRepository::new(
|
||||||
pool.clone(),
|
pool.clone(),
|
||||||
));
|
));
|
||||||
let session_repo: Arc<
|
let session_repo: Arc<
|
||||||
dyn crate::application::ports::auth_ports::SessionStoragePort,
|
SessionPgRepository,
|
||||||
> = Arc::new(
|
> = Arc::new(
|
||||||
crate::infrastructure::repositories::SessionPgRepository::new(pool.clone()),
|
crate::infrastructure::repositories::SessionPgRepository::new(pool.clone()),
|
||||||
);
|
);
|
||||||
@@ -623,13 +627,11 @@ impl AppServiceFactory {
|
|||||||
|
|
||||||
// 9d. Wire App Password service
|
// 9d. Wire App Password service
|
||||||
{
|
{
|
||||||
use crate::application::services::app_password_service::AppPasswordService;
|
|
||||||
use crate::infrastructure::repositories::AppPasswordPgRepository;
|
|
||||||
|
|
||||||
let app_pw_repo: Arc<
|
let app_pw_repo: Arc<
|
||||||
dyn crate::application::ports::auth_ports::AppPasswordStoragePort,
|
AppPasswordPgRepository,
|
||||||
> = Arc::new(AppPasswordPgRepository::new(pool.clone()));
|
> = Arc::new(AppPasswordPgRepository::new(pool.clone()));
|
||||||
let hasher: Arc<dyn crate::application::ports::auth_ports::PasswordHasherPort> =
|
let hasher: Arc<Argon2PasswordHasher> =
|
||||||
Arc::new(
|
Arc::new(
|
||||||
crate::infrastructure::services::password_hasher::Argon2PasswordHasher::new(
|
crate::infrastructure::services::password_hasher::Argon2PasswordHasher::new(
|
||||||
self.config.auth.hash_memory_cost,
|
self.config.auth.hash_memory_cost,
|
||||||
@@ -637,7 +639,7 @@ impl AppServiceFactory {
|
|||||||
self.config.auth.hash_parallelism,
|
self.config.auth.hash_parallelism,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
let user_repo: Arc<dyn crate::application::ports::auth_ports::UserStoragePort> =
|
let user_repo: Arc<UserPgRepository> =
|
||||||
Arc::new(crate::infrastructure::repositories::UserPgRepository::new(
|
Arc::new(crate::infrastructure::repositories::UserPgRepository::new(
|
||||||
pool.clone(),
|
pool.clone(),
|
||||||
));
|
));
|
||||||
@@ -656,7 +658,6 @@ impl AppServiceFactory {
|
|||||||
|
|
||||||
// 9e. Wire PathResolver for single-query WebDAV path resolution
|
// 9e. Wire PathResolver for single-query WebDAV path resolution
|
||||||
{
|
{
|
||||||
use crate::infrastructure::services::path_resolver_service::PathResolverService;
|
|
||||||
app_state.path_resolver = Some(Arc::new(PathResolverService::new(pool.clone())));
|
app_state.path_resolver = Some(Arc::new(PathResolverService::new(pool.clone())));
|
||||||
tracing::info!("PathResolver service initialized");
|
tracing::info!("PathResolver service initialized");
|
||||||
}
|
}
|
||||||
@@ -665,12 +666,12 @@ impl AppServiceFactory {
|
|||||||
{
|
{
|
||||||
// CalDAV
|
// CalDAV
|
||||||
let calendar_repo: Arc<
|
let calendar_repo: Arc<
|
||||||
dyn crate::domain::repositories::calendar_repository::CalendarRepository,
|
CalendarPgRepository,
|
||||||
> = Arc::new(
|
> = Arc::new(
|
||||||
crate::infrastructure::repositories::pg::CalendarPgRepository::new(pool.clone()),
|
crate::infrastructure::repositories::pg::CalendarPgRepository::new(pool.clone()),
|
||||||
);
|
);
|
||||||
let event_repo: Arc<
|
let event_repo: Arc<
|
||||||
dyn crate::domain::repositories::calendar_event_repository::CalendarEventRepository,
|
CalendarEventPgRepository,
|
||||||
> = Arc::new(
|
> = Arc::new(
|
||||||
crate::infrastructure::repositories::pg::CalendarEventPgRepository::new(
|
crate::infrastructure::repositories::pg::CalendarEventPgRepository::new(
|
||||||
pool.clone(),
|
pool.clone(),
|
||||||
@@ -689,22 +690,22 @@ impl AppServiceFactory {
|
|||||||
);
|
);
|
||||||
app_state.calendar_use_case = Some(
|
app_state.calendar_use_case = Some(
|
||||||
calendar_service
|
calendar_service
|
||||||
as Arc<dyn crate::application::ports::calendar_ports::CalendarUseCase>,
|
as Arc<CalendarService>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// CardDAV
|
// CardDAV
|
||||||
let address_book_repo: Arc<
|
let address_book_repo: Arc<
|
||||||
dyn crate::domain::repositories::address_book_repository::AddressBookRepository,
|
AddressBookPgRepository,
|
||||||
> = Arc::new(
|
> = Arc::new(
|
||||||
crate::infrastructure::repositories::pg::AddressBookPgRepository::new(pool.clone()),
|
crate::infrastructure::repositories::pg::AddressBookPgRepository::new(pool.clone()),
|
||||||
);
|
);
|
||||||
let contact_repo: Arc<
|
let contact_repo: Arc<
|
||||||
dyn crate::domain::repositories::contact_repository::ContactRepository,
|
ContactPgRepository,
|
||||||
> = Arc::new(
|
> = Arc::new(
|
||||||
crate::infrastructure::repositories::pg::ContactPgRepository::new(pool.clone()),
|
crate::infrastructure::repositories::pg::ContactPgRepository::new(pool.clone()),
|
||||||
);
|
);
|
||||||
let group_repo: Arc<
|
let group_repo: Arc<
|
||||||
dyn crate::domain::repositories::contact_repository::ContactGroupRepository,
|
ContactGroupPgRepository,
|
||||||
> = Arc::new(
|
> = Arc::new(
|
||||||
crate::infrastructure::repositories::pg::ContactGroupPgRepository::new(
|
crate::infrastructure::repositories::pg::ContactGroupPgRepository::new(
|
||||||
pool.clone(),
|
pool.clone(),
|
||||||
@@ -717,12 +718,8 @@ impl AppServiceFactory {
|
|||||||
group_repo,
|
group_repo,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
app_state.addressbook_use_case = Some(contact_storage.clone()
|
app_state.addressbook_use_case = Some(contact_storage.clone());
|
||||||
as Arc<dyn crate::application::ports::carddav_ports::AddressBookUseCase>);
|
app_state.contact_use_case = Some(contact_storage);
|
||||||
app_state.contact_use_case = Some(
|
|
||||||
contact_storage
|
|
||||||
as Arc<dyn crate::application::ports::carddav_ports::ContactUseCase>,
|
|
||||||
);
|
|
||||||
|
|
||||||
tracing::info!("CalDAV and CardDAV services initialized with PostgreSQL repositories");
|
tracing::info!("CalDAV and CardDAV services initialized with PostgreSQL repositories");
|
||||||
}
|
}
|
||||||
@@ -735,9 +732,6 @@ impl AppServiceFactory {
|
|||||||
"WOPI is enabled but WOPI_DISCOVERY_URL is empty — WOPI services will NOT be available"
|
"WOPI is enabled but WOPI_DISCOVERY_URL is empty — WOPI services will NOT be available"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
use crate::application::services::wopi_lock_service::WopiLockService;
|
|
||||||
use crate::application::services::wopi_token_service::WopiTokenService;
|
|
||||||
use crate::infrastructure::services::wopi_discovery_service::WopiDiscoveryService;
|
|
||||||
|
|
||||||
let wopi_secret = if self.config.wopi.secret.is_empty() {
|
let wopi_secret = if self.config.wopi.secret.is_empty() {
|
||||||
self.config.auth.jwt_secret.clone()
|
self.config.auth.jwt_secret.clone()
|
||||||
@@ -775,25 +769,25 @@ impl AppServiceFactory {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CoreServices {
|
pub struct CoreServices {
|
||||||
pub path_service: Arc<PathService>,
|
pub path_service: Arc<PathService>,
|
||||||
pub file_content_cache: Arc<dyn ContentCachePort>,
|
pub file_content_cache: Arc<FileContentCache>,
|
||||||
pub thumbnail_service: Arc<dyn ThumbnailPort>,
|
pub thumbnail_service: Arc<ThumbnailService>,
|
||||||
pub chunked_upload_service: Arc<dyn ChunkedUploadPort>,
|
pub chunked_upload_service: Arc<ChunkedUploadService>,
|
||||||
pub image_transcode_service: Arc<dyn ImageTranscodePort>,
|
pub image_transcode_service: Arc<ImageTranscodeService>,
|
||||||
pub dedup_service: Arc<dyn DedupPort>,
|
pub dedup_service: Arc<DedupService>,
|
||||||
pub zip_service: Arc<dyn ZipPort>,
|
pub zip_service: Option<Arc<ZipService>>,
|
||||||
pub config: AppConfig,
|
pub config: AppConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container for repository services
|
/// Container for repository services
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RepositoryServices {
|
pub struct RepositoryServices {
|
||||||
pub folder_repository: Arc<dyn FolderStoragePort>,
|
pub folder_repository: Arc<FolderDbRepository>,
|
||||||
pub folder_repo_concrete: Arc<FolderDbRepository>,
|
pub folder_repo_concrete: Arc<FolderDbRepository>,
|
||||||
pub file_read_repository: Arc<dyn FileReadPort>,
|
pub file_read_repository: Arc<FileBlobReadRepository>,
|
||||||
pub file_write_repository: Arc<dyn FileWritePort>,
|
pub file_write_repository: Arc<FileBlobWriteRepository>,
|
||||||
pub i18n_repository: Arc<dyn I18nService>,
|
pub i18n_repository: Arc<FileSystemI18nService>,
|
||||||
pub trash_repository:
|
pub trash_repository:
|
||||||
Option<Arc<dyn crate::domain::repositories::trash_repository::TrashRepository>>,
|
Option<Arc<TrashDbRepository>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container for application services
|
/// Container for application services
|
||||||
@@ -802,23 +796,23 @@ pub struct ApplicationServices {
|
|||||||
// Concrete types for compatibility with existing handlers
|
// Concrete types for compatibility with existing handlers
|
||||||
pub folder_service_concrete: Arc<FolderService>,
|
pub folder_service_concrete: Arc<FolderService>,
|
||||||
// Traits for abstraction
|
// Traits for abstraction
|
||||||
pub folder_service: Arc<dyn FolderUseCase>,
|
pub folder_service: Arc<FolderService>,
|
||||||
pub file_upload_service: Arc<dyn FileUploadUseCase>,
|
pub file_upload_service: Arc<FileUploadService>,
|
||||||
pub file_retrieval_service: Arc<dyn FileRetrievalUseCase>,
|
pub file_retrieval_service: Arc<FileRetrievalService>,
|
||||||
pub file_management_service: Arc<dyn FileManagementUseCase>,
|
pub file_management_service: Arc<FileManagementService>,
|
||||||
pub file_use_case_factory: Arc<dyn FileUseCaseFactory>,
|
pub file_use_case_factory: Arc<dyn FileUseCaseFactory>,
|
||||||
pub i18n_service: Arc<I18nApplicationService>,
|
pub i18n_service: Arc<I18nApplicationService>,
|
||||||
pub trash_service: Option<Arc<dyn TrashUseCase>>,
|
pub trash_service: Option<Arc<TrashService>>,
|
||||||
pub search_service: Option<Arc<dyn SearchUseCase>>,
|
pub search_service: Option<Arc<SearchService>>,
|
||||||
pub share_service: Option<Arc<dyn crate::application::ports::share_ports::ShareUseCase>>,
|
pub share_service: Option<Arc<ShareService>>,
|
||||||
pub favorites_service: Option<Arc<dyn FavoritesUseCase>>,
|
pub favorites_service: Option<Arc<FavoritesService>>,
|
||||||
pub recent_service: Option<Arc<dyn RecentItemsUseCase>>,
|
pub recent_service: Option<Arc<RecentService>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container for authentication services
|
/// Container for authentication services
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AuthServices {
|
pub struct AuthServices {
|
||||||
pub token_service: Arc<dyn crate::application::ports::auth_ports::TokenServicePort>,
|
pub token_service: Arc<JwtTokenService>,
|
||||||
pub auth_application_service: Arc<AuthApplicationService>,
|
pub auth_application_service: Arc<AuthApplicationService>,
|
||||||
pub login_lockout:
|
pub login_lockout:
|
||||||
Arc<crate::infrastructure::services::login_lockout_service::LoginLockoutService>,
|
Arc<crate::infrastructure::services::login_lockout_service::LoginLockoutService>,
|
||||||
@@ -835,19 +829,19 @@ pub struct AppState {
|
|||||||
pub maintenance_pool: Option<Arc<PgPool>>,
|
pub maintenance_pool: Option<Arc<PgPool>>,
|
||||||
pub auth_service: Option<AuthServices>,
|
pub auth_service: Option<AuthServices>,
|
||||||
pub admin_settings_service: Option<Arc<AdminSettingsService>>,
|
pub admin_settings_service: Option<Arc<AdminSettingsService>>,
|
||||||
pub trash_service: Option<Arc<dyn TrashUseCase>>,
|
pub trash_service: Option<Arc<TrashService>>,
|
||||||
pub share_service: Option<Arc<dyn crate::application::ports::share_ports::ShareUseCase>>,
|
pub share_service: Option<Arc<ShareService>>,
|
||||||
pub favorites_service: Option<Arc<dyn FavoritesUseCase>>,
|
pub favorites_service: Option<Arc<FavoritesService>>,
|
||||||
pub recent_service: Option<Arc<dyn RecentItemsUseCase>>,
|
pub recent_service: Option<Arc<RecentService>>,
|
||||||
pub storage_usage_service:
|
pub storage_usage_service:
|
||||||
Option<Arc<dyn crate::application::ports::storage_ports::StorageUsagePort>>,
|
Option<Arc<StorageUsageService>>,
|
||||||
pub calendar_service: Option<Arc<dyn crate::application::ports::storage_ports::StorageUseCase>>,
|
pub calendar_service: Option<Arc<CalendarService>>,
|
||||||
pub contact_service: Option<Arc<dyn crate::application::ports::storage_ports::StorageUseCase>>,
|
pub contact_service: Option<Arc<ContactStorageAdapter>>,
|
||||||
pub calendar_use_case:
|
pub calendar_use_case:
|
||||||
Option<Arc<dyn crate::application::ports::calendar_ports::CalendarUseCase>>,
|
Option<Arc<CalendarService>>,
|
||||||
pub addressbook_use_case:
|
pub addressbook_use_case:
|
||||||
Option<Arc<dyn crate::application::ports::carddav_ports::AddressBookUseCase>>,
|
Option<Arc<ContactStorageAdapter>>,
|
||||||
pub contact_use_case: Option<Arc<dyn crate::application::ports::carddav_ports::ContactUseCase>>,
|
pub contact_use_case: Option<Arc<ContactStorageAdapter>>,
|
||||||
pub wopi_token_service:
|
pub wopi_token_service:
|
||||||
Option<Arc<crate::application::services::wopi_token_service::WopiTokenService>>,
|
Option<Arc<crate::application::services::wopi_token_service::WopiTokenService>>,
|
||||||
pub wopi_lock_service:
|
pub wopi_lock_service:
|
||||||
|
|||||||
+1
-35
@@ -10,7 +10,6 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ use crate::application::dtos::search_dto::{
|
|||||||
SearchCriteriaDto, SearchResultsDto, SearchSuggestionsDto,
|
SearchCriteriaDto, SearchResultsDto, SearchSuggestionsDto,
|
||||||
};
|
};
|
||||||
use crate::application::ports::file_ports::{
|
use crate::application::ports::file_ports::{
|
||||||
FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase, FileUseCaseFactory,
|
FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase,
|
||||||
OptimizedFileContent,
|
OptimizedFileContent,
|
||||||
};
|
};
|
||||||
use crate::application::ports::inbound::{FolderUseCase, SearchUseCase};
|
use crate::application::ports::inbound::{FolderUseCase, SearchUseCase};
|
||||||
@@ -44,7 +43,6 @@ use crate::domain::services::path_service::StoragePath;
|
|||||||
/// are fully initialised.
|
/// are fully initialised.
|
||||||
pub struct StubZipPort;
|
pub struct StubZipPort;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ZipPort for StubZipPort {
|
impl ZipPort for StubZipPort {
|
||||||
async fn create_folder_zip(
|
async fn create_folder_zip(
|
||||||
&self,
|
&self,
|
||||||
@@ -64,7 +62,6 @@ impl ZipPort for StubZipPort {
|
|||||||
|
|
||||||
pub struct StubFileReadPort;
|
pub struct StubFileReadPort;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileReadPort for StubFileReadPort {
|
impl FileReadPort for StubFileReadPort {
|
||||||
async fn get_file(&self, _id: &str) -> Result<File, DomainError> {
|
async fn get_file(&self, _id: &str) -> Result<File, DomainError> {
|
||||||
Ok(File::default())
|
Ok(File::default())
|
||||||
@@ -136,7 +133,6 @@ impl FileReadPort for StubFileReadPort {
|
|||||||
|
|
||||||
pub struct StubFileWritePort;
|
pub struct StubFileWritePort;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileWritePort for StubFileWritePort {
|
impl FileWritePort for StubFileWritePort {
|
||||||
async fn save_file_from_temp(
|
async fn save_file_from_temp(
|
||||||
&self,
|
&self,
|
||||||
@@ -218,7 +214,6 @@ impl FileWritePort for StubFileWritePort {
|
|||||||
|
|
||||||
pub struct StubFolderStoragePort;
|
pub struct StubFolderStoragePort;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FolderRepository for StubFolderStoragePort {
|
impl FolderRepository for StubFolderStoragePort {
|
||||||
async fn create_folder(
|
async fn create_folder(
|
||||||
&self,
|
&self,
|
||||||
@@ -324,7 +319,6 @@ impl FolderRepository for StubFolderStoragePort {
|
|||||||
|
|
||||||
pub struct StubI18nService;
|
pub struct StubI18nService;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl I18nService for StubI18nService {
|
impl I18nService for StubI18nService {
|
||||||
async fn translate(&self, _key: &str, _locale: Locale) -> I18nResult<String> {
|
async fn translate(&self, _key: &str, _locale: Locale) -> I18nResult<String> {
|
||||||
Ok(String::new())
|
Ok(String::new())
|
||||||
@@ -349,7 +343,6 @@ impl I18nService for StubI18nService {
|
|||||||
|
|
||||||
pub struct StubFolderUseCase;
|
pub struct StubFolderUseCase;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FolderUseCase for StubFolderUseCase {
|
impl FolderUseCase for StubFolderUseCase {
|
||||||
async fn create_folder(&self, _dto: CreateFolderDto) -> Result<FolderDto, DomainError> {
|
async fn create_folder(&self, _dto: CreateFolderDto) -> Result<FolderDto, DomainError> {
|
||||||
Ok(FolderDto::default())
|
Ok(FolderDto::default())
|
||||||
@@ -429,7 +422,6 @@ impl FolderUseCase for StubFolderUseCase {
|
|||||||
|
|
||||||
pub struct StubFileUploadUseCase;
|
pub struct StubFileUploadUseCase;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileUploadUseCase for StubFileUploadUseCase {
|
impl FileUploadUseCase for StubFileUploadUseCase {
|
||||||
async fn upload_file_streaming(
|
async fn upload_file_streaming(
|
||||||
&self,
|
&self,
|
||||||
@@ -486,7 +478,6 @@ impl FileUploadUseCase for StubFileUploadUseCase {
|
|||||||
|
|
||||||
pub struct StubFileRetrievalUseCase;
|
pub struct StubFileRetrievalUseCase;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileRetrievalUseCase for StubFileRetrievalUseCase {
|
impl FileRetrievalUseCase for StubFileRetrievalUseCase {
|
||||||
async fn get_file(&self, _id: &str) -> Result<FileDto, DomainError> {
|
async fn get_file(&self, _id: &str) -> Result<FileDto, DomainError> {
|
||||||
Ok(FileDto::default())
|
Ok(FileDto::default())
|
||||||
@@ -548,7 +539,6 @@ impl FileRetrievalUseCase for StubFileRetrievalUseCase {
|
|||||||
|
|
||||||
pub struct StubFileManagementUseCase;
|
pub struct StubFileManagementUseCase;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileManagementUseCase for StubFileManagementUseCase {
|
impl FileManagementUseCase for StubFileManagementUseCase {
|
||||||
async fn move_file(
|
async fn move_file(
|
||||||
&self,
|
&self,
|
||||||
@@ -579,33 +569,12 @@ impl FileManagementUseCase for StubFileManagementUseCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// FileUseCaseFactory
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
pub struct StubFileUseCaseFactory;
|
|
||||||
|
|
||||||
impl FileUseCaseFactory for StubFileUseCaseFactory {
|
|
||||||
fn create_file_upload_use_case(&self) -> Arc<dyn FileUploadUseCase> {
|
|
||||||
Arc::new(StubFileUploadUseCase)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_file_retrieval_use_case(&self) -> Arc<dyn FileRetrievalUseCase> {
|
|
||||||
Arc::new(StubFileRetrievalUseCase)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_file_management_use_case(&self) -> Arc<dyn FileManagementUseCase> {
|
|
||||||
Arc::new(StubFileManagementUseCase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// SearchUseCase
|
// SearchUseCase
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
pub struct StubSearchUseCase;
|
pub struct StubSearchUseCase;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SearchUseCase for StubSearchUseCase {
|
impl SearchUseCase for StubSearchUseCase {
|
||||||
async fn search(
|
async fn search(
|
||||||
&self,
|
&self,
|
||||||
@@ -642,7 +611,6 @@ use crate::application::ports::dedup_ports::{
|
|||||||
|
|
||||||
pub struct StubDedupPort;
|
pub struct StubDedupPort;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl DedupPort for StubDedupPort {
|
impl DedupPort for StubDedupPort {
|
||||||
async fn store_bytes(
|
async fn store_bytes(
|
||||||
&self,
|
&self,
|
||||||
@@ -741,7 +709,6 @@ use crate::application::ports::cache_ports::{
|
|||||||
|
|
||||||
pub struct StubMetadataCachePort;
|
pub struct StubMetadataCachePort;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl MetadataCachePort for StubMetadataCachePort {
|
impl MetadataCachePort for StubMetadataCachePort {
|
||||||
async fn get_metadata(&self, _path: &Path) -> Option<CachedMetadataDto> {
|
async fn get_metadata(&self, _path: &Path) -> Option<CachedMetadataDto> {
|
||||||
None
|
None
|
||||||
@@ -774,7 +741,6 @@ impl MetadataCachePort for StubMetadataCachePort {
|
|||||||
|
|
||||||
pub struct StubContentCachePort;
|
pub struct StubContentCachePort;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ContentCachePort for StubContentCachePort {
|
impl ContentCachePort for StubContentCachePort {
|
||||||
fn should_cache(&self, _size: usize) -> bool {
|
fn should_cache(&self, _size: usize) -> bool {
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -7,7 +6,6 @@ use crate::domain::entities::contact::AddressBook;
|
|||||||
|
|
||||||
pub type AddressBookRepositoryResult<T> = Result<T, DomainError>;
|
pub type AddressBookRepositoryResult<T> = Result<T, DomainError>;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait AddressBookRepository: Send + Sync + 'static {
|
pub trait AddressBookRepository: Send + Sync + 'static {
|
||||||
async fn create_address_book(
|
async fn create_address_book(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use crate::domain::entities::calendar_event::CalendarEvent;
|
use crate::domain::entities::calendar_event::CalendarEvent;
|
||||||
use async_trait::async_trait;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub type CalendarEventRepositoryResult<T> = Result<T, DomainError>;
|
pub type CalendarEventRepositoryResult<T> = Result<T, DomainError>;
|
||||||
|
|
||||||
/// Repository interface for CalendarEvent entity operations
|
/// Repository interface for CalendarEvent entity operations
|
||||||
#[async_trait]
|
|
||||||
pub trait CalendarEventRepository: Send + Sync + 'static {
|
pub trait CalendarEventRepository: Send + Sync + 'static {
|
||||||
/// Creates a new calendar event
|
/// Creates a new calendar event
|
||||||
async fn create_event(
|
async fn create_event(
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use crate::domain::entities::calendar::Calendar;
|
use crate::domain::entities::calendar::Calendar;
|
||||||
use async_trait::async_trait;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub type CalendarRepositoryResult<T> = Result<T, DomainError>;
|
pub type CalendarRepositoryResult<T> = Result<T, DomainError>;
|
||||||
|
|
||||||
/// Repository interface for Calendar entity operations
|
/// Repository interface for Calendar entity operations
|
||||||
#[async_trait]
|
|
||||||
pub trait CalendarRepository: Send + Sync + 'static {
|
pub trait CalendarRepository: Send + Sync + 'static {
|
||||||
/// Creates a new calendar
|
/// Creates a new calendar
|
||||||
async fn create_calendar(&self, calendar: Calendar) -> CalendarRepositoryResult<Calendar>;
|
async fn create_calendar(&self, calendar: Calendar) -> CalendarRepositoryResult<Calendar>;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -7,7 +6,6 @@ use crate::domain::entities::contact::{Contact, ContactGroup};
|
|||||||
|
|
||||||
pub type ContactRepositoryResult<T> = Result<T, DomainError>;
|
pub type ContactRepositoryResult<T> = Result<T, DomainError>;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait ContactRepository: Send + Sync + 'static {
|
pub trait ContactRepository: Send + Sync + 'static {
|
||||||
async fn create_contact(&self, contact: Contact) -> ContactRepositoryResult<Contact>;
|
async fn create_contact(&self, contact: Contact) -> ContactRepositoryResult<Contact>;
|
||||||
async fn update_contact(&self, contact: Contact) -> ContactRepositoryResult<Contact>;
|
async fn update_contact(&self, contact: Contact) -> ContactRepositoryResult<Contact>;
|
||||||
@@ -32,7 +30,6 @@ pub trait ContactRepository: Send + Sync + 'static {
|
|||||||
) -> ContactRepositoryResult<Vec<Contact>>;
|
) -> ContactRepositoryResult<Vec<Contact>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait ContactGroupRepository: Send + Sync + 'static {
|
pub trait ContactGroupRepository: Send + Sync + 'static {
|
||||||
async fn create_group(&self, group: ContactGroup) -> ContactRepositoryResult<ContactGroup>;
|
async fn create_group(&self, group: ContactGroup) -> ContactRepositoryResult<ContactGroup>;
|
||||||
async fn update_group(&self, group: ContactGroup) -> ContactRepositoryResult<ContactGroup>;
|
async fn update_group(&self, group: ContactGroup) -> ContactRepositoryResult<ContactGroup>;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
|
|
||||||
@@ -26,7 +25,6 @@ use crate::domain::services::path_service::StoragePath;
|
|||||||
///
|
///
|
||||||
/// Encapsulates every operation that queries state without modifying it:
|
/// Encapsulates every operation that queries state without modifying it:
|
||||||
/// get, list, content, stream, mmap, range, path resolution.
|
/// get, list, content, stream, mmap, range, path resolution.
|
||||||
#[async_trait]
|
|
||||||
pub trait FileReadRepository: Send + Sync + 'static {
|
pub trait FileReadRepository: Send + Sync + 'static {
|
||||||
/// Gets a file by its ID.
|
/// Gets a file by its ID.
|
||||||
async fn get_file(&self, id: &str) -> Result<File, DomainError>;
|
async fn get_file(&self, id: &str) -> Result<File, DomainError>;
|
||||||
@@ -63,7 +61,6 @@ pub trait FileReadRepository: Send + Sync + 'static {
|
|||||||
///
|
///
|
||||||
/// Covers: upload (buffered + streaming), move, delete, update,
|
/// Covers: upload (buffered + streaming), move, delete, update,
|
||||||
/// and deferred registration for write-behind cache.
|
/// and deferred registration for write-behind cache.
|
||||||
#[async_trait]
|
|
||||||
pub trait FileWriteRepository: Send + Sync + 'static {
|
pub trait FileWriteRepository: Send + Sync + 'static {
|
||||||
/// Saves a new file from bytes.
|
/// Saves a new file from bytes.
|
||||||
async fn save_file(
|
async fn save_file(
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
//! Concrete implementations (filesystem, PostgreSQL, S3, etc.) live in
|
//! Concrete implementations (filesystem, PostgreSQL, S3, etc.) live in
|
||||||
//! the infrastructure layer.
|
//! the infrastructure layer.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use crate::domain::entities::folder::Folder;
|
use crate::domain::entities::folder::Folder;
|
||||||
@@ -18,7 +17,6 @@ use crate::domain::services::path_service::StoragePath;
|
|||||||
///
|
///
|
||||||
/// Defines the CRUD and management operations required for
|
/// Defines the CRUD and management operations required for
|
||||||
/// the Folder entity in the storage system.
|
/// the Folder entity in the storage system.
|
||||||
#[async_trait]
|
|
||||||
pub trait FolderRepository: Send + Sync + 'static {
|
pub trait FolderRepository: Send + Sync + 'static {
|
||||||
/// Creates a new folder
|
/// Creates a new folder
|
||||||
async fn create_folder(
|
async fn create_folder(
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use crate::domain::entities::session::Session;
|
use crate::domain::entities::session::Session;
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum SessionRepositoryError {
|
pub enum SessionRepositoryError {
|
||||||
@@ -29,7 +28,6 @@ impl From<SessionRepositoryError> for DomainError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait SessionRepository: Send + Sync + 'static {
|
pub trait SessionRepository: Send + Sync + 'static {
|
||||||
/// Creates a new session
|
/// Creates a new session
|
||||||
async fn create_session(&self, session: Session) -> SessionRepositoryResult<Session>;
|
async fn create_session(&self, session: Session) -> SessionRepositoryResult<Session>;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Repository for platform settings stored in the database.
|
/// Repository for platform settings stored in the database.
|
||||||
/// Settings are key-value pairs organized by category (e.g., "oidc", "general").
|
/// Settings are key-value pairs organized by category (e.g., "oidc", "general").
|
||||||
#[async_trait]
|
|
||||||
pub trait SettingsRepository: Send + Sync + 'static {
|
pub trait SettingsRepository: Send + Sync + 'static {
|
||||||
/// Get a single setting value by key
|
/// Get a single setting value by key
|
||||||
async fn get(&self, key: &str) -> Result<Option<String>, DomainError>;
|
async fn get(&self, key: &str) -> Result<Option<String>, DomainError>;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::domain::{
|
use crate::domain::{
|
||||||
@@ -20,7 +19,6 @@ pub enum ShareRepositoryError {
|
|||||||
AlreadyExists(String),
|
AlreadyExists(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait ShareRepository: Send + Sync + 'static {
|
pub trait ShareRepository: Send + Sync + 'static {
|
||||||
/// Save a new share or update an existing one
|
/// Save a new share or update an existing one
|
||||||
async fn save(&self, share: &Share) -> Result<Share, ShareRepositoryError>;
|
async fn save(&self, share: &Share) -> Result<Share, ShareRepositoryError>;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::common::errors::Result;
|
use crate::common::errors::Result;
|
||||||
use crate::domain::entities::trashed_item::TrashedItem;
|
use crate::domain::entities::trashed_item::TrashedItem;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait TrashRepository: Send + Sync {
|
pub trait TrashRepository: Send + Sync {
|
||||||
async fn add_to_trash(&self, item: &TrashedItem) -> Result<()>;
|
async fn add_to_trash(&self, item: &TrashedItem) -> Result<()>;
|
||||||
async fn get_trash_items(&self, user_id: &Uuid) -> Result<Vec<TrashedItem>>;
|
async fn get_trash_items(&self, user_id: &Uuid) -> Result<Vec<TrashedItem>>;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use crate::domain::entities::user::{User, UserRole};
|
use crate::domain::entities::user::{User, UserRole};
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum UserRepositoryError {
|
pub enum UserRepositoryError {
|
||||||
@@ -41,7 +40,6 @@ impl From<UserRepositoryError> for DomainError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait UserRepository: Send + Sync + 'static {
|
pub trait UserRepository: Send + Sync + 'static {
|
||||||
/// Creates a new user
|
/// Creates a new user
|
||||||
async fn create_user(&self, user: User) -> UserRepositoryResult<User>;
|
async fn create_user(&self, user: User) -> UserRepositoryResult<User>;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Error types for i18n service operations
|
/// Error types for i18n service operations
|
||||||
@@ -54,7 +53,6 @@ impl Locale {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Interface for i18n service (primary port)
|
/// Interface for i18n service (primary port)
|
||||||
#[async_trait]
|
|
||||||
pub trait I18nService: Send + Sync + 'static {
|
pub trait I18nService: Send + Sync + 'static {
|
||||||
/// Get a translation for a key and locale
|
/// Get a translation for a key and locale
|
||||||
async fn translate(&self, key: &str, locale: Locale) -> I18nResult<String>;
|
async fn translate(&self, key: &str, locale: Locale) -> I18nResult<String>;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
//! the `CalendarRepository` and `CalendarEventRepository` domain repositories.
|
//! the `CalendarRepository` and `CalendarEventRepository` domain repositories.
|
||||||
//! It bridges the gap between the application layer and the infrastructure layer.
|
//! It bridges the gap between the application layer and the infrastructure layer.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -20,18 +19,20 @@ use crate::domain::entities::calendar::Calendar;
|
|||||||
use crate::domain::entities::calendar_event::CalendarEvent;
|
use crate::domain::entities::calendar_event::CalendarEvent;
|
||||||
use crate::domain::repositories::calendar_event_repository::CalendarEventRepository;
|
use crate::domain::repositories::calendar_event_repository::CalendarEventRepository;
|
||||||
use crate::domain::repositories::calendar_repository::CalendarRepository;
|
use crate::domain::repositories::calendar_repository::CalendarRepository;
|
||||||
|
use crate::infrastructure::repositories::pg::CalendarEventPgRepository;
|
||||||
|
use crate::infrastructure::repositories::pg::CalendarPgRepository;
|
||||||
|
|
||||||
/// Adapter that implements CalendarStoragePort using domain repositories
|
/// Adapter that implements CalendarStoragePort using domain repositories
|
||||||
pub struct CalendarStorageAdapter {
|
pub struct CalendarStorageAdapter {
|
||||||
calendar_repository: Arc<dyn CalendarRepository>,
|
calendar_repository: Arc<CalendarPgRepository>,
|
||||||
event_repository: Arc<dyn CalendarEventRepository>,
|
event_repository: Arc<CalendarEventPgRepository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CalendarStorageAdapter {
|
impl CalendarStorageAdapter {
|
||||||
/// Creates a new CalendarStorageAdapter with the given repositories
|
/// Creates a new CalendarStorageAdapter with the given repositories
|
||||||
pub fn new(
|
pub fn new(
|
||||||
calendar_repository: Arc<dyn CalendarRepository>,
|
calendar_repository: Arc<CalendarPgRepository>,
|
||||||
event_repository: Arc<dyn CalendarEventRepository>,
|
event_repository: Arc<CalendarEventPgRepository>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
calendar_repository,
|
calendar_repository,
|
||||||
@@ -40,7 +41,6 @@ impl CalendarStorageAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl CalendarStoragePort for CalendarStorageAdapter {
|
impl CalendarStoragePort for CalendarStorageAdapter {
|
||||||
// Calendar operations
|
// Calendar operations
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
//! using the domain repositories. It bridges the gap between the application layer
|
//! using the domain repositories. It bridges the gap between the application layer
|
||||||
//! and the infrastructure layer for CardDAV functionality.
|
//! and the infrastructure layer for CardDAV functionality.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -22,20 +21,23 @@ use crate::common::errors::{DomainError, ErrorKind};
|
|||||||
use crate::domain::entities::contact::{Address, AddressBook, Contact, ContactGroup, Email, Phone};
|
use crate::domain::entities::contact::{Address, AddressBook, Contact, ContactGroup, Email, Phone};
|
||||||
use crate::domain::repositories::address_book_repository::AddressBookRepository;
|
use crate::domain::repositories::address_book_repository::AddressBookRepository;
|
||||||
use crate::domain::repositories::contact_repository::{ContactGroupRepository, ContactRepository};
|
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;
|
||||||
|
|
||||||
/// Adapter that implements AddressBookUseCase and ContactUseCase using domain repositories
|
/// Adapter that implements AddressBookUseCase and ContactUseCase using domain repositories
|
||||||
pub struct ContactStorageAdapter {
|
pub struct ContactStorageAdapter {
|
||||||
address_book_repository: Arc<dyn AddressBookRepository>,
|
address_book_repository: Arc<AddressBookPgRepository>,
|
||||||
contact_repository: Arc<dyn ContactRepository>,
|
contact_repository: Arc<ContactPgRepository>,
|
||||||
group_repository: Arc<dyn ContactGroupRepository>,
|
group_repository: Arc<ContactGroupPgRepository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContactStorageAdapter {
|
impl ContactStorageAdapter {
|
||||||
/// Creates a new ContactStorageAdapter with the given repositories
|
/// Creates a new ContactStorageAdapter with the given repositories
|
||||||
pub fn new(
|
pub fn new(
|
||||||
address_book_repository: Arc<dyn AddressBookRepository>,
|
address_book_repository: Arc<AddressBookPgRepository>,
|
||||||
contact_repository: Arc<dyn ContactRepository>,
|
contact_repository: Arc<ContactPgRepository>,
|
||||||
group_repository: Arc<dyn ContactGroupRepository>,
|
group_repository: Arc<ContactGroupPgRepository>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
address_book_repository,
|
address_book_repository,
|
||||||
@@ -217,7 +219,6 @@ impl ContactStorageAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl AddressBookUseCase for ContactStorageAdapter {
|
impl AddressBookUseCase for ContactStorageAdapter {
|
||||||
async fn create_address_book(
|
async fn create_address_book(
|
||||||
&self,
|
&self,
|
||||||
@@ -423,7 +424,6 @@ impl AddressBookUseCase for ContactStorageAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ContactUseCase for ContactStorageAdapter {
|
impl ContactUseCase for ContactStorageAdapter {
|
||||||
async fn create_contact(&self, dto: CreateContactDto) -> Result<ContactDto, DomainError> {
|
async fn create_contact(&self, dto: CreateContactDto) -> Result<ContactDto, DomainError> {
|
||||||
let address_book_id = Self::parse_uuid(&dto.address_book_id, "AddressBook")?;
|
let address_book_id = Self::parse_uuid(&dto.address_book_id, "AddressBook")?;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use anyhow::Result;
|
|||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::application::ports::auth_ports::TokenServicePort;
|
|
||||||
use crate::application::services::auth_application_service::AuthApplicationService;
|
use crate::application::services::auth_application_service::AuthApplicationService;
|
||||||
use crate::application::services::folder_service::FolderService;
|
use crate::application::services::folder_service::FolderService;
|
||||||
use crate::common::config::AppConfig;
|
use crate::common::config::AppConfig;
|
||||||
@@ -18,7 +17,7 @@ pub async fn create_auth_services(
|
|||||||
folder_service: Option<Arc<FolderService>>,
|
folder_service: Option<Arc<FolderService>>,
|
||||||
) -> Result<AuthServices> {
|
) -> Result<AuthServices> {
|
||||||
// Create JWT token service (TokenServicePort implementation)
|
// Create JWT token service (TokenServicePort implementation)
|
||||||
let token_service: Arc<dyn TokenServicePort> = Arc::new(JwtTokenService::new(
|
let token_service: Arc<JwtTokenService> = Arc::new(JwtTokenService::new(
|
||||||
config.auth.jwt_secret.clone(),
|
config.auth.jwt_secret.clone(),
|
||||||
config.auth.access_token_expiry_secs,
|
config.auth.access_token_expiry_secs,
|
||||||
config.auth.refresh_token_expiry_secs,
|
config.auth.refresh_token_expiry_secs,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use sqlx::{PgPool, Row, types::Uuid};
|
use sqlx::{PgPool, Row, types::Uuid};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -19,7 +18,6 @@ impl AddressBookPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl AddressBookRepository for AddressBookPgRepository {
|
impl AddressBookRepository for AddressBookPgRepository {
|
||||||
async fn create_address_book(
|
async fn create_address_book(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
use crate::application::ports::auth_ports::AppPasswordStoragePort;
|
use crate::application::ports::auth_ports::AppPasswordStoragePort;
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use crate::domain::entities::app_password::AppPassword;
|
use crate::domain::entities::app_password::AppPassword;
|
||||||
use async_trait::async_trait;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -22,7 +21,6 @@ impl AppPasswordPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl AppPasswordStoragePort for AppPasswordPgRepository {
|
impl AppPasswordStoragePort for AppPasswordPgRepository {
|
||||||
async fn create(&self, ap: AppPassword) -> Result<AppPassword, DomainError> {
|
async fn create(&self, ap: AppPassword) -> Result<AppPassword, DomainError> {
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sqlx::{PgPool, Row, types::Uuid};
|
use sqlx::{PgPool, Row, types::Uuid};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -19,7 +18,6 @@ impl CalendarEventPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl CalendarEventRepository for CalendarEventPgRepository {
|
impl CalendarEventRepository for CalendarEventPgRepository {
|
||||||
async fn create_event(
|
async fn create_event(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use sqlx::{PgPool, Row, types::Uuid};
|
use sqlx::{PgPool, Row, types::Uuid};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -19,7 +18,6 @@ impl CalendarPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl CalendarRepository for CalendarPgRepository {
|
impl CalendarRepository for CalendarPgRepository {
|
||||||
async fn create_calendar(&self, calendar: Calendar) -> CalendarRepositoryResult<Calendar> {
|
async fn create_calendar(&self, calendar: Calendar) -> CalendarRepositoryResult<Calendar> {
|
||||||
let row = sqlx::query(
|
let row = sqlx::query(
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
use sqlx::{PgPool, Row, types::Uuid};
|
use sqlx::{PgPool, Row, types::Uuid};
|
||||||
@@ -24,7 +23,6 @@ impl ContactGroupPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ContactGroupRepository for ContactGroupPgRepository {
|
impl ContactGroupRepository for ContactGroupPgRepository {
|
||||||
async fn create_group(&self, group: ContactGroup) -> ContactRepositoryResult<ContactGroup> {
|
async fn create_group(&self, group: ContactGroup) -> ContactRepositoryResult<ContactGroup> {
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
use sqlx::{PgPool, Row, types::Uuid};
|
use sqlx::{PgPool, Row, types::Uuid};
|
||||||
@@ -63,7 +62,6 @@ impl ContactPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ContactRepository for ContactPgRepository {
|
impl ContactRepository for ContactPgRepository {
|
||||||
async fn create_contact(&self, contact: Contact) -> ContactRepositoryResult<Contact> {
|
async fn create_contact(&self, contact: Contact) -> ContactRepositoryResult<Contact> {
|
||||||
// Convert domain entities to persistence DTOs for JSONB serialization
|
// Convert domain entities to persistence DTOs for JSONB serialization
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
//! PostgreSQL repository for Device Authorization Grant (RFC 8628) codes.
|
//! PostgreSQL repository for Device Authorization Grant (RFC 8628) codes.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use sqlx::{PgPool, Row};
|
use sqlx::{PgPool, Row};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -49,7 +48,6 @@ impl DeviceCodePgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl DeviceCodeStoragePort for DeviceCodePgRepository {
|
impl DeviceCodeStoragePort for DeviceCodePgRepository {
|
||||||
async fn create_device_code(&self, dc: DeviceCode) -> Result<DeviceCode, DomainError> {
|
async fn create_device_code(&self, dc: DeviceCode) -> Result<DeviceCode, DomainError> {
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use sqlx::{PgPool, Row};
|
use sqlx::{PgPool, Row};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
@@ -19,7 +18,6 @@ impl FavoritesPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FavoritesRepositoryPort for FavoritesPgRepository {
|
impl FavoritesRepositoryPort for FavoritesPgRepository {
|
||||||
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>> {
|
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>> {
|
||||||
let user_uuid = Uuid::parse_str(user_id)?;
|
let user_uuid = Uuid::parse_str(user_id)?;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
//! File paths are resolved by JOINing with `storage.folders.path` (the
|
//! File paths are resolved by JOINing with `storage.folders.path` (the
|
||||||
//! materialized path column), so no recursive CTEs or N+1 queries are needed.
|
//! materialized path column), so no recursive CTEs or N+1 queries are needed.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::{Stream, TryStreamExt};
|
use futures::{Stream, TryStreamExt};
|
||||||
use moka::sync::Cache;
|
use moka::sync::Cache;
|
||||||
@@ -17,16 +16,16 @@ use std::sync::Arc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::application::dtos::search_dto::SearchCriteriaDto;
|
use crate::application::dtos::search_dto::SearchCriteriaDto;
|
||||||
use crate::application::ports::dedup_ports::DedupPort;
|
|
||||||
use crate::application::ports::storage_ports::FileReadPort;
|
use crate::application::ports::storage_ports::FileReadPort;
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use crate::domain::entities::file::File;
|
use crate::domain::entities::file::File;
|
||||||
use crate::domain::services::path_service::StoragePath;
|
use crate::domain::services::path_service::StoragePath;
|
||||||
|
use crate::infrastructure::services::dedup_service::DedupService;
|
||||||
|
|
||||||
/// File read repository backed by PostgreSQL metadata + blob storage.
|
/// File read repository backed by PostgreSQL metadata + blob storage.
|
||||||
pub struct FileBlobReadRepository {
|
pub struct FileBlobReadRepository {
|
||||||
pool: Arc<PgPool>,
|
pool: Arc<PgPool>,
|
||||||
dedup: Arc<dyn DedupPort>,
|
dedup: Arc<DedupService>,
|
||||||
/// Lock-free cache: file_id → blob_hash.
|
/// Lock-free cache: file_id → blob_hash.
|
||||||
/// Populated by `get_file()` and `resolve_blob_hash()` (slow path).
|
/// Populated by `get_file()` and `resolve_blob_hash()` (slow path).
|
||||||
/// Entries persist until TTI expiry (30 s idle) or capacity eviction —
|
/// Entries persist until TTI expiry (30 s idle) or capacity eviction —
|
||||||
@@ -37,7 +36,7 @@ pub struct FileBlobReadRepository {
|
|||||||
impl FileBlobReadRepository {
|
impl FileBlobReadRepository {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
pool: Arc<PgPool>,
|
pool: Arc<PgPool>,
|
||||||
dedup: Arc<dyn DedupPort>,
|
dedup: Arc<DedupService>,
|
||||||
_folder_repo: Arc<super::folder_db_repository::FolderDbRepository>,
|
_folder_repo: Arc<super::folder_db_repository::FolderDbRepository>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -115,7 +114,6 @@ impl FileBlobReadRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileReadPort for FileBlobReadRepository {
|
impl FileReadPort for FileBlobReadRepository {
|
||||||
async fn get_file(&self, id: &str) -> Result<File, DomainError> {
|
async fn get_file(&self, id: &str) -> Result<File, DomainError> {
|
||||||
let row = sqlx::query_as::<
|
let row = sqlx::query_as::<
|
||||||
@@ -890,14 +888,13 @@ impl FileReadPort for FileBlobReadRepository {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::stubs::StubDedupPort;
|
use crate::common::stubs::StubDedupPort;
|
||||||
use crate::infrastructure::repositories::pg::folder_db_repository::FolderDbRepository;
|
|
||||||
|
|
||||||
/// Helper: build a `FileBlobReadRepository` without a real PgPool.
|
/// Helper: build a `FileBlobReadRepository` without a real PgPool.
|
||||||
/// Only the moka `hash_cache` is exercised — no SQL is executed.
|
/// Only the moka `hash_cache` is exercised — no SQL is executed.
|
||||||
fn make_repo() -> FileBlobReadRepository {
|
fn make_repo() -> FileBlobReadRepository {
|
||||||
let _folder_repo = Arc::new(FolderDbRepository::new_stub());
|
let _folder_repo = Arc::new(FolderDbRepository::new_stub());
|
||||||
// StubDedupPort satisfies the trait but is never called in cache-only tests
|
// StubDedupPort satisfies the trait but is never called in cache-only tests
|
||||||
let dedup: Arc<dyn DedupPort> = Arc::new(StubDedupPort);
|
let dedup: Arc<DedupService> = Arc::new(StubDedupPort);
|
||||||
// PgPool is required by the struct but we won't hit any SQL in these tests.
|
// PgPool is required by the struct but we won't hit any SQL in these tests.
|
||||||
// We create a repo with a stub pool placeholder — only hash_cache is tested.
|
// We create a repo with a stub pool placeholder — only hash_cache is tested.
|
||||||
FileBlobReadRepository {
|
FileBlobReadRepository {
|
||||||
|
|||||||
@@ -7,30 +7,29 @@
|
|||||||
//! File paths are resolved by querying the materialized `storage.folders.path`
|
//! File paths are resolved by querying the materialized `storage.folders.path`
|
||||||
//! column (O(1) per lookup), so no recursive CTEs are needed.
|
//! column (O(1) per lookup), so no recursive CTEs are needed.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::application::ports::dedup_ports::DedupPort;
|
|
||||||
use crate::application::ports::storage_ports::{CopyFolderTreeResult, FileWritePort};
|
use crate::application::ports::storage_ports::{CopyFolderTreeResult, FileWritePort};
|
||||||
use crate::common::errors::DomainError;
|
use crate::common::errors::DomainError;
|
||||||
use crate::domain::entities::file::File;
|
use crate::domain::entities::file::File;
|
||||||
use crate::domain::services::path_service::StoragePath;
|
use crate::domain::services::path_service::StoragePath;
|
||||||
|
|
||||||
use super::folder_db_repository::FolderDbRepository;
|
use super::folder_db_repository::FolderDbRepository;
|
||||||
|
use crate::infrastructure::services::dedup_service::DedupService;
|
||||||
|
|
||||||
/// File write repository backed by PostgreSQL metadata + blob storage.
|
/// File write repository backed by PostgreSQL metadata + blob storage.
|
||||||
pub struct FileBlobWriteRepository {
|
pub struct FileBlobWriteRepository {
|
||||||
pool: Arc<PgPool>,
|
pool: Arc<PgPool>,
|
||||||
dedup: Arc<dyn DedupPort>,
|
dedup: Arc<DedupService>,
|
||||||
folder_repo: Arc<FolderDbRepository>,
|
folder_repo: Arc<FolderDbRepository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileBlobWriteRepository {
|
impl FileBlobWriteRepository {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
pool: Arc<PgPool>,
|
pool: Arc<PgPool>,
|
||||||
dedup: Arc<dyn DedupPort>,
|
dedup: Arc<DedupService>,
|
||||||
folder_repo: Arc<FolderDbRepository>,
|
folder_repo: Arc<FolderDbRepository>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -180,7 +179,6 @@ impl FileBlobWriteRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FileWritePort for FileBlobWriteRepository {
|
impl FileWritePort for FileBlobWriteRepository {
|
||||||
async fn save_file_from_temp(
|
async fn save_file_from_temp(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
//! database triggers, so reading a folder's full path is always O(1) — no
|
//! database triggers, so reading a folder's full path is always O(1) — no
|
||||||
//! recursive CTEs or N+1 queries.
|
//! recursive CTEs or N+1 queries.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -72,7 +71,6 @@ impl FolderDbRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl FolderRepository for FolderDbRepository {
|
impl FolderRepository for FolderDbRepository {
|
||||||
async fn create_folder(
|
async fn create_folder(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use sqlx::{PgPool, Row};
|
use sqlx::{PgPool, Row};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
@@ -19,7 +18,6 @@ impl RecentItemsPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl RecentItemsRepositoryPort for RecentItemsPgRepository {
|
impl RecentItemsRepositoryPort for RecentItemsPgRepository {
|
||||||
async fn get_recent_items(&self, user_id: &str, limit: i32) -> Result<Vec<RecentItemDto>> {
|
async fn get_recent_items(&self, user_id: &str, limit: i32) -> Result<Vec<RecentItemDto>> {
|
||||||
let user_uuid = Uuid::parse_str(user_id)?;
|
let user_uuid = Uuid::parse_str(user_id)?;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
use sqlx::{PgPool, Row};
|
use sqlx::{PgPool, Row};
|
||||||
@@ -39,7 +38,6 @@ impl SessionPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SessionRepository for SessionPgRepository {
|
impl SessionRepository for SessionPgRepository {
|
||||||
/// Creates a new session using a transaction
|
/// Creates a new session using a transaction
|
||||||
async fn create_session(&self, session: Session) -> SessionRepositoryResult<Session> {
|
async fn create_session(&self, session: Session) -> SessionRepositoryResult<Session> {
|
||||||
@@ -291,7 +289,6 @@ impl SessionRepository for SessionPgRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of the storage port for the application layer
|
// Implementation of the storage port for the application layer
|
||||||
#[async_trait]
|
|
||||||
impl SessionStoragePort for SessionPgRepository {
|
impl SessionStoragePort for SessionPgRepository {
|
||||||
async fn create_session(&self, session: Session) -> Result<Session, DomainError> {
|
async fn create_session(&self, session: Session) -> Result<Session, DomainError> {
|
||||||
SessionRepository::create_session(self, session)
|
SessionRepository::create_session(self, session)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -16,7 +15,6 @@ impl SettingsPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SettingsRepository for SettingsPgRepository {
|
impl SettingsRepository for SettingsPgRepository {
|
||||||
async fn get(&self, key: &str) -> Result<Option<String>, DomainError> {
|
async fn get(&self, key: &str) -> Result<Option<String>, DomainError> {
|
||||||
let row =
|
let row =
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use sqlx::{PgPool, Row};
|
use sqlx::{PgPool, Row};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -72,7 +71,6 @@ impl SharePgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ShareStoragePort for SharePgRepository {
|
impl ShareStoragePort for SharePgRepository {
|
||||||
async fn save_share(&self, share: &Share) -> Result<Share, DomainError> {
|
async fn save_share(&self, share: &Share) -> Result<Share, DomainError> {
|
||||||
let row = sqlx::query(
|
let row = sqlx::query(
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
//! and `storage.folders`. There is no separate trash table — trashed items
|
//! and `storage.folders`. There is no separate trash table — trashed items
|
||||||
//! are files/folders with `is_trashed = TRUE`.
|
//! are files/folders with `is_trashed = TRUE`.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -65,7 +64,6 @@ impl TrashDbRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl TrashRepository for TrashDbRepository {
|
impl TrashRepository for TrashDbRepository {
|
||||||
async fn add_to_trash(&self, _item: &TrashedItem) -> Result<()> {
|
async fn add_to_trash(&self, _item: &TrashedItem) -> Result<()> {
|
||||||
// No-op: the actual flagging is done by FileWritePort::move_to_trash
|
// No-op: the actual flagging is done by FileWritePort::move_to_trash
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
use sqlx::{PgPool, Row};
|
use sqlx::{PgPool, Row};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -44,7 +43,6 @@ impl UserPgRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl UserRepository for UserPgRepository {
|
impl UserRepository for UserPgRepository {
|
||||||
/// Creates a new user using a transaction
|
/// Creates a new user using a transaction
|
||||||
async fn create_user(&self, user: User) -> UserRepositoryResult<User> {
|
async fn create_user(&self, user: User) -> UserRepositoryResult<User> {
|
||||||
@@ -619,7 +617,6 @@ impl UserRepository for UserPgRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Storage port implementation for the application layer
|
// Storage port implementation for the application layer
|
||||||
#[async_trait]
|
|
||||||
impl UserStoragePort for UserPgRepository {
|
impl UserStoragePort for UserPgRepository {
|
||||||
async fn create_user(&self, user: User) -> Result<User, DomainError> {
|
async fn create_user(&self, user: User) -> Result<User, DomainError> {
|
||||||
UserRepository::create_user(self, user)
|
UserRepository::create_user(self, user)
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
//! 3. HEAD /api/uploads/:id → Check progress
|
//! 3. HEAD /api/uploads/:id → Check progress
|
||||||
//! 4. POST /api/uploads/:id/complete → Finalize and assemble
|
//! 4. POST /api/uploads/:id/complete → Finalize and assemble
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -755,7 +754,6 @@ impl ChunkedUploadService {
|
|||||||
|
|
||||||
// ─── Port implementation ─────────────────────────────────────────────────────
|
// ─── Port implementation ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ChunkedUploadPort for ChunkedUploadService {
|
impl ChunkedUploadPort for ChunkedUploadService {
|
||||||
async fn create_session(
|
async fn create_session(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use flate2::bufread::GzDecoder;
|
use flate2::bufread::GzDecoder;
|
||||||
@@ -43,7 +42,6 @@ impl From<CompressionLevel> for Compression {
|
|||||||
const COMPRESSION_SIZE_THRESHOLD: u64 = 1024 * 50; // 50KB
|
const COMPRESSION_SIZE_THRESHOLD: u64 = 1024 * 50; // 50KB
|
||||||
|
|
||||||
/// Interface for compression services
|
/// Interface for compression services
|
||||||
#[async_trait]
|
|
||||||
pub trait CompressionService: Send + Sync {
|
pub trait CompressionService: Send + Sync {
|
||||||
/// Compresses data in memory
|
/// Compresses data in memory
|
||||||
async fn compress_data(&self, data: &[u8], level: CompressionLevel) -> io::Result<Vec<u8>>;
|
async fn compress_data(&self, data: &[u8], level: CompressionLevel) -> io::Result<Vec<u8>>;
|
||||||
@@ -88,7 +86,6 @@ impl GzipCompressionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl CompressionService for GzipCompressionService {
|
impl CompressionService for GzipCompressionService {
|
||||||
/// Compresses data in memory using Gzip
|
/// Compresses data in memory using Gzip
|
||||||
async fn compress_data(&self, data: &[u8], level: CompressionLevel) -> io::Result<Vec<u8>> {
|
async fn compress_data(&self, data: &[u8], level: CompressionLevel) -> io::Result<Vec<u8>> {
|
||||||
@@ -258,7 +255,6 @@ impl From<PortCompressionLevel> for CompressionLevel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl CompressionPort for GzipCompressionService {
|
impl CompressionPort for GzipCompressionService {
|
||||||
async fn compress_data(
|
async fn compress_data(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
//! - 30-50% storage reduction typical
|
//! - 30-50% storage reduction typical
|
||||||
//! - Faster uploads for existing content (instant dedup)
|
//! - Faster uploads for existing content (instant dedup)
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::stream::{self, StreamExt};
|
use futures::stream::{self, StreamExt};
|
||||||
use futures::{Stream, TryStreamExt};
|
use futures::{Stream, TryStreamExt};
|
||||||
@@ -802,7 +801,6 @@ impl DedupService {
|
|||||||
|
|
||||||
// ─── Port implementation ─────────────────────────────────────────────────────
|
// ─── Port implementation ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl DedupPort for DedupService {
|
impl DedupPort for DedupService {
|
||||||
async fn store_bytes(
|
async fn store_bytes(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -198,9 +198,7 @@ pub type SharedFileContentCache = Arc<FileContentCache>;
|
|||||||
// ─── ContentCachePort implementation ─────────────────────────
|
// ─── ContentCachePort implementation ─────────────────────────
|
||||||
|
|
||||||
use crate::application::ports::cache_ports::ContentCachePort;
|
use crate::application::ports::cache_ports::ContentCachePort;
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ContentCachePort for FileContentCache {
|
impl ContentCachePort for FileContentCache {
|
||||||
fn should_cache(&self, size: usize) -> bool {
|
fn should_cache(&self, size: usize) -> bool {
|
||||||
FileContentCache::should_cache(self, size)
|
FileContentCache::should_cache(self, size)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -63,7 +62,6 @@ impl FileSystemI18nService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl I18nService for FileSystemI18nService {
|
impl I18nService for FileSystemI18nService {
|
||||||
async fn translate(&self, key: &str, locale: Locale) -> I18nResult<String> {
|
async fn translate(&self, key: &str, locale: Locale) -> I18nResult<String> {
|
||||||
// Check if translations are cached
|
// Check if translations are cached
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
//! - Supports JPEG, PNG, GIF → WebP conversion
|
//! - Supports JPEG, PNG, GIF → WebP conversion
|
||||||
//! - Falls back to original if conversion fails or result is larger
|
//! - Falls back to original if conversion fails or result is larger
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use image::ImageFormat;
|
use image::ImageFormat;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@@ -392,7 +391,6 @@ impl From<PortOutputFormat> for OutputFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ImageTranscodePort for ImageTranscodeService {
|
impl ImageTranscodePort for ImageTranscodeService {
|
||||||
fn can_transcode(&self, mime_type: &str) -> bool {
|
fn can_transcode(&self, mime_type: &str) -> bool {
|
||||||
ImageTranscodeService::can_transcode(mime_type)
|
ImageTranscodeService::can_transcode(mime_type)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
//! Supports both RSA (RS256, RS384, RS512) and EC (ES256, ES384) algorithms.
|
//! Supports both RSA (RS256, RS384, RS512) and EC (ES256, ES384) algorithms.
|
||||||
//! Compatible with Authentik, Keycloak, and any standard OIDC provider.
|
//! Compatible with Authentik, Keycloak, and any standard OIDC provider.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@@ -293,7 +292,6 @@ impl OidcService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl OidcServicePort for OidcService {
|
impl OidcServicePort for OidcService {
|
||||||
async fn get_authorize_url(
|
async fn get_authorize_url(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
use argon2::password_hash::SaltString;
|
use argon2::password_hash::SaltString;
|
||||||
use argon2::{Algorithm, Argon2, Params, PasswordHash, PasswordHasher, PasswordVerifier, Version};
|
use argon2::{Algorithm, Argon2, Params, PasswordHash, PasswordHasher, PasswordVerifier, Version};
|
||||||
use async_trait::async_trait;
|
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use crate::application::ports::auth_ports::PasswordHasherPort;
|
use crate::application::ports::auth_ports::PasswordHasherPort;
|
||||||
@@ -57,7 +56,6 @@ impl Argon2PasswordHasher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl PasswordHasherPort for Argon2PasswordHasher {
|
impl PasswordHasherPort for Argon2PasswordHasher {
|
||||||
async fn hash_password(&self, password: &str) -> Result<String, DomainError> {
|
async fn hash_password(&self, password: &str) -> Result<String, DomainError> {
|
||||||
let pwd = password.to_owned();
|
let pwd = password.to_owned();
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
//!
|
//!
|
||||||
//! StoragePath (Value Object) remains in domain/services/path_service.rs
|
//! StoragePath (Value Object) remains in domain/services/path_service.rs
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
@@ -116,7 +115,6 @@ impl PathService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl StoragePort for PathService {
|
impl StoragePort for PathService {
|
||||||
fn resolve_path(&self, storage_path: &StoragePath) -> PathBuf {
|
fn resolve_path(&self, storage_path: &StoragePath) -> PathBuf {
|
||||||
let mut path = self.root_path.clone();
|
let mut path = self.root_path.clone();
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use image::{ImageFormat, imageops::FilterType};
|
use image::{ImageFormat, imageops::FilterType};
|
||||||
/**
|
/**
|
||||||
@@ -458,7 +457,6 @@ impl From<PortThumbnailSize> for ThumbnailSize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ThumbnailPort for ThumbnailService {
|
impl ThumbnailPort for ThumbnailService {
|
||||||
fn is_supported_image(&self, mime_type: &str) -> bool {
|
fn is_supported_image(&self, mime_type: &str) -> bool {
|
||||||
ThumbnailService::is_supported_image(mime_type)
|
ThumbnailService::is_supported_image(mime_type)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use tracing::{debug, error, info, instrument};
|
|||||||
|
|
||||||
use crate::common::errors::Result;
|
use crate::common::errors::Result;
|
||||||
use crate::domain::repositories::trash_repository::TrashRepository;
|
use crate::domain::repositories::trash_repository::TrashRepository;
|
||||||
|
use crate::infrastructure::repositories::pg::trash_db_repository::TrashDbRepository;
|
||||||
|
|
||||||
/// Service for automatic cleanup of expired items in the trash.
|
/// Service for automatic cleanup of expired items in the trash.
|
||||||
///
|
///
|
||||||
@@ -12,12 +13,12 @@ use crate::domain::repositories::trash_repository::TrashRepository;
|
|||||||
/// in **2 SQL statements inside a single transaction**, instead of the
|
/// in **2 SQL statements inside a single transaction**, instead of the
|
||||||
/// previous N+1 pattern that issued 3 queries per expired item.
|
/// previous N+1 pattern that issued 3 queries per expired item.
|
||||||
pub struct TrashCleanupService {
|
pub struct TrashCleanupService {
|
||||||
trash_repository: Arc<dyn TrashRepository>,
|
trash_repository: Arc<TrashDbRepository>,
|
||||||
cleanup_interval_hours: u64,
|
cleanup_interval_hours: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrashCleanupService {
|
impl TrashCleanupService {
|
||||||
pub fn new(trash_repository: Arc<dyn TrashRepository>, cleanup_interval_hours: u64) -> Self {
|
pub fn new(trash_repository: Arc<TrashDbRepository>, cleanup_interval_hours: u64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
trash_repository,
|
trash_repository,
|
||||||
cleanup_interval_hours: cleanup_interval_hours.max(1), // Minimum 1 hour
|
cleanup_interval_hours: cleanup_interval_hours.max(1), // Minimum 1 hour
|
||||||
@@ -57,7 +58,7 @@ impl TrashCleanupService {
|
|||||||
|
|
||||||
/// Bulk-delete all expired trash items in a single transaction.
|
/// Bulk-delete all expired trash items in a single transaction.
|
||||||
#[instrument(skip(trash_repository))]
|
#[instrument(skip(trash_repository))]
|
||||||
async fn cleanup_expired_items(trash_repository: Arc<dyn TrashRepository>) -> Result<()> {
|
async fn cleanup_expired_items(trash_repository: Arc<TrashDbRepository>) -> Result<()> {
|
||||||
debug!("Starting bulk cleanup of expired trash items");
|
debug!("Starting bulk cleanup of expired trash items");
|
||||||
|
|
||||||
let (files, folders) = trash_repository.delete_expired_bulk().await?;
|
let (files, folders) = trash_repository.delete_expired_bulk().await?;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::{
|
|||||||
application::ports::zip_ports::ZipPort,
|
application::ports::zip_ports::ZipPort,
|
||||||
common::errors::{DomainError, ErrorKind, Result},
|
common::errors::{DomainError, ErrorKind, Result},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
|
||||||
use async_zip::base::write::ZipFileWriter;
|
use async_zip::base::write::ZipFileWriter;
|
||||||
use async_zip::{Compression, ZipEntryBuilder};
|
use async_zip::{Compression, ZipEntryBuilder};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
@@ -17,6 +16,8 @@ use thiserror::Error;
|
|||||||
use tokio::io::BufWriter;
|
use tokio::io::BufWriter;
|
||||||
use tokio_util::compat::Compat;
|
use tokio_util::compat::Compat;
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
|
use crate::application::services::file_retrieval_service::FileRetrievalService;
|
||||||
|
use crate::application::services::folder_service::FolderService;
|
||||||
|
|
||||||
/// Error related to ZIP file creation
|
/// Error related to ZIP file creation
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
@@ -53,15 +54,15 @@ type AsyncZipWriter = ZipFileWriter<Compat<BufWriter<tokio::fs::File>>>;
|
|||||||
/// `tokio::io::BufWriter` → `tokio::fs::File`, so **no Tokio worker is ever
|
/// `tokio::io::BufWriter` → `tokio::fs::File`, so **no Tokio worker is ever
|
||||||
/// blocked** by disk I/O or compression.
|
/// blocked** by disk I/O or compression.
|
||||||
pub struct ZipService {
|
pub struct ZipService {
|
||||||
file_service: Arc<dyn FileRetrievalUseCase>,
|
file_service: Arc<FileRetrievalService>,
|
||||||
folder_service: Arc<dyn FolderUseCase>,
|
folder_service: Arc<FolderService>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZipService {
|
impl ZipService {
|
||||||
/// Creates a new instance of the ZIP service
|
/// Creates a new instance of the ZIP service
|
||||||
pub fn new(
|
pub fn new(
|
||||||
file_service: Arc<dyn FileRetrievalUseCase>,
|
file_service: Arc<FileRetrievalService>,
|
||||||
folder_service: Arc<dyn FolderUseCase>,
|
folder_service: Arc<FolderService>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_service,
|
file_service,
|
||||||
@@ -239,7 +240,6 @@ impl ZipService {
|
|||||||
|
|
||||||
// ─── Port implementation ─────────────────────────────────────────────────────
|
// ─── Port implementation ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ZipPort for ZipService {
|
impl ZipPort for ZipService {
|
||||||
async fn create_folder_zip(
|
async fn create_folder_zip(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use crate::application::dtos::settings_dto::{
|
|||||||
use crate::common::di::AppState;
|
use crate::common::di::AppState;
|
||||||
use crate::interfaces::errors::AppError;
|
use crate::interfaces::errors::AppError;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use crate::application::ports::auth_ports::TokenServicePort;
|
||||||
|
|
||||||
/// Admin API routes — all require admin role.
|
/// Admin API routes — all require admin role.
|
||||||
pub fn admin_routes() -> Router<Arc<AppState>> {
|
pub fn admin_routes() -> Router<Arc<AppState>> {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ use crate::application::ports::calendar_ports::CalendarUseCase;
|
|||||||
use crate::common::di::AppState;
|
use crate::common::di::AppState;
|
||||||
use crate::interfaces::errors::AppError;
|
use crate::interfaces::errors::AppError;
|
||||||
use crate::interfaces::middleware::auth::CurrentUser;
|
use crate::interfaces::middleware::auth::CurrentUser;
|
||||||
|
use crate::application::services::calendar_service::CalendarService;
|
||||||
|
|
||||||
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
|
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
|
||||||
|
|
||||||
@@ -127,7 +128,7 @@ fn extract_user(req: &Request<Body>) -> Result<CurrentUser, AppError> {
|
|||||||
.ok_or_else(|| AppError::unauthorized("Authentication required"))
|
.ok_or_else(|| AppError::unauthorized("Authentication required"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_calendar_service(state: &AppState) -> Result<&Arc<dyn CalendarUseCase>, AppError> {
|
fn get_calendar_service(state: &AppState) -> Result<&Arc<CalendarService>, AppError> {
|
||||||
state.calendar_use_case.as_ref().ok_or_else(|| {
|
state.calendar_use_case.as_ref().ok_or_else(|| {
|
||||||
AppError::new(
|
AppError::new(
|
||||||
StatusCode::NOT_IMPLEMENTED,
|
StatusCode::NOT_IMPLEMENTED,
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ use crate::application::ports::carddav_ports::{AddressBookUseCase, ContactUseCas
|
|||||||
use crate::common::di::AppState;
|
use crate::common::di::AppState;
|
||||||
use crate::interfaces::errors::AppError;
|
use crate::interfaces::errors::AppError;
|
||||||
use crate::interfaces::middleware::auth::CurrentUser;
|
use crate::interfaces::middleware::auth::CurrentUser;
|
||||||
|
use crate::infrastructure::adapters::contact_storage_adapter::ContactStorageAdapter;
|
||||||
|
|
||||||
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
|
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
|
||||||
|
|
||||||
@@ -115,7 +116,7 @@ fn extract_user(req: &Request<Body>) -> Result<CurrentUser, AppError> {
|
|||||||
.ok_or_else(|| AppError::unauthorized("Authentication required"))
|
.ok_or_else(|| AppError::unauthorized("Authentication required"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_addressbook_service(state: &AppState) -> Result<&Arc<dyn AddressBookUseCase>, AppError> {
|
fn get_addressbook_service(state: &AppState) -> Result<&Arc<ContactStorageAdapter>, AppError> {
|
||||||
state.addressbook_use_case.as_ref().ok_or_else(|| {
|
state.addressbook_use_case.as_ref().ok_or_else(|| {
|
||||||
AppError::new(
|
AppError::new(
|
||||||
StatusCode::NOT_IMPLEMENTED,
|
StatusCode::NOT_IMPLEMENTED,
|
||||||
@@ -125,7 +126,7 @@ fn get_addressbook_service(state: &AppState) -> Result<&Arc<dyn AddressBookUseCa
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_contact_service(state: &AppState) -> Result<&Arc<dyn ContactUseCase>, AppError> {
|
fn get_contact_service(state: &AppState) -> Result<&Arc<ContactStorageAdapter>, AppError> {
|
||||||
state.contact_use_case.as_ref().ok_or_else(|| {
|
state.contact_use_case.as_ref().ok_or_else(|| {
|
||||||
AppError::new(
|
AppError::new(
|
||||||
StatusCode::NOT_IMPLEMENTED,
|
StatusCode::NOT_IMPLEMENTED,
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ use crate::application::ports::chunked_upload_ports::DEFAULT_CHUNK_SIZE;
|
|||||||
use crate::common::di::AppState;
|
use crate::common::di::AppState;
|
||||||
use crate::domain::errors::ErrorKind;
|
use crate::domain::errors::ErrorKind;
|
||||||
use crate::interfaces::middleware::auth::AuthUser;
|
use crate::interfaces::middleware::auth::AuthUser;
|
||||||
|
use crate::application::ports::chunked_upload_ports::ChunkedUploadPort;
|
||||||
|
use crate::application::ports::file_ports::FileUploadUseCase;
|
||||||
|
use crate::application::ports::storage_ports::StorageUsagePort;
|
||||||
|
|
||||||
/// Request body for creating an upload session
|
/// Request body for creating an upload session
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use tracing::{error, info};
|
|||||||
|
|
||||||
use crate::application::ports::favorites_ports::FavoritesUseCase;
|
use crate::application::ports::favorites_ports::FavoritesUseCase;
|
||||||
use crate::interfaces::middleware::auth::AuthUser;
|
use crate::interfaces::middleware::auth::AuthUser;
|
||||||
|
use crate::application::services::favorites_service::FavoritesService;
|
||||||
|
|
||||||
/// Single item in a batch-add-favorites request.
|
/// Single item in a batch-add-favorites request.
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@@ -26,7 +27,7 @@ pub struct BatchFavoritesRequest {
|
|||||||
|
|
||||||
/// Handler for favorite-related API endpoints
|
/// Handler for favorite-related API endpoints
|
||||||
pub async fn get_favorites(
|
pub async fn get_favorites(
|
||||||
State(favorites_service): State<Arc<dyn FavoritesUseCase>>,
|
State(favorites_service): State<Arc<FavoritesService>>,
|
||||||
auth_user: AuthUser,
|
auth_user: AuthUser,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let user_id = &auth_user.id;
|
let user_id = &auth_user.id;
|
||||||
@@ -51,7 +52,7 @@ pub async fn get_favorites(
|
|||||||
|
|
||||||
/// Add an item to user's favorites
|
/// Add an item to user's favorites
|
||||||
pub async fn add_favorite(
|
pub async fn add_favorite(
|
||||||
State(favorites_service): State<Arc<dyn FavoritesUseCase>>,
|
State(favorites_service): State<Arc<FavoritesService>>,
|
||||||
auth_user: AuthUser,
|
auth_user: AuthUser,
|
||||||
Path((item_type, item_id)): Path<(String, String)>,
|
Path((item_type, item_id)): Path<(String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -94,7 +95,7 @@ pub async fn add_favorite(
|
|||||||
|
|
||||||
/// Remove an item from user's favorites
|
/// Remove an item from user's favorites
|
||||||
pub async fn remove_favorite(
|
pub async fn remove_favorite(
|
||||||
State(favorites_service): State<Arc<dyn FavoritesUseCase>>,
|
State(favorites_service): State<Arc<FavoritesService>>,
|
||||||
auth_user: AuthUser,
|
auth_user: AuthUser,
|
||||||
Path((item_type, item_id)): Path<(String, String)>,
|
Path((item_type, item_id)): Path<(String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -138,7 +139,7 @@ pub async fn remove_favorite(
|
|||||||
/// Add multiple items to favourites in a single transaction.
|
/// Add multiple items to favourites in a single transaction.
|
||||||
/// POST /api/favorites/batch
|
/// POST /api/favorites/batch
|
||||||
pub async fn batch_add_favorites(
|
pub async fn batch_add_favorites(
|
||||||
State(favorites_service): State<Arc<dyn FavoritesUseCase>>,
|
State(favorites_service): State<Arc<FavoritesService>>,
|
||||||
auth_user: AuthUser,
|
auth_user: AuthUser,
|
||||||
Json(body): Json<BatchFavoritesRequest>,
|
Json(body): Json<BatchFavoritesRequest>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ use crate::application::ports::file_ports::OptimizedFileContent;
|
|||||||
use crate::common::di::AppState;
|
use crate::common::di::AppState;
|
||||||
use crate::interfaces::middleware::auth::{AuthUser, OptionalUserId};
|
use crate::interfaces::middleware::auth::{AuthUser, OptionalUserId};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use crate::application::ports::file_ports::{FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase};
|
||||||
|
use crate::application::ports::storage_ports::StorageUsagePort;
|
||||||
|
use crate::application::ports::thumbnail_ports::ThumbnailPort;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type aliases for dependency injection state.
|
* Type aliases for dependency injection state.
|
||||||
@@ -306,7 +309,7 @@ impl FileHandler {
|
|||||||
let file_path = storage_root.join(&file.path);
|
let file_path = storage_root.join(&file.path);
|
||||||
|
|
||||||
match thumbnail_service
|
match thumbnail_service
|
||||||
.get_thumbnail(&id, thumb_size, &file_path)
|
.get_thumbnail(&id, thumb_size.into(), &file_path)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ use crate::application::services::folder_service::FolderService;
|
|||||||
use crate::common::di::AppState as GlobalAppState;
|
use crate::common::di::AppState as GlobalAppState;
|
||||||
use crate::common::errors::ErrorKind;
|
use crate::common::errors::ErrorKind;
|
||||||
use crate::interfaces::middleware::auth::AuthUser;
|
use crate::interfaces::middleware::auth::AuthUser;
|
||||||
|
use crate::application::ports::file_ports::FileRetrievalUseCase;
|
||||||
|
use crate::application::ports::trash_ports::TrashUseCase;
|
||||||
|
|
||||||
type AppState = Arc<FolderService>;
|
type AppState = Arc<FolderService>;
|
||||||
|
|
||||||
@@ -423,7 +425,17 @@ impl FolderHandler {
|
|||||||
tracing::info!("Preparing ZIP for folder: {} ({})", folder.name, id);
|
tracing::info!("Preparing ZIP for folder: {} ({})", folder.name, id);
|
||||||
|
|
||||||
// Use ZIP service from DI container
|
// Use ZIP service from DI container
|
||||||
let zip_service = &state.core.zip_service;
|
let zip_service = match &state.core.zip_service {
|
||||||
|
Some(svc) => svc,
|
||||||
|
None => {
|
||||||
|
tracing::error!("ZipService not initialized");
|
||||||
|
return (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
Json(serde_json::json!({ "error": "ZipService not initialized" })),
|
||||||
|
)
|
||||||
|
.into_response();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Create the ZIP archive (written to a temp file, O(1) RAM)
|
// Create the ZIP archive (written to a temp file, O(1) RAM)
|
||||||
match zip_service.create_folder_zip(&id, &folder.name).await {
|
match zip_service.create_folder_zip(&id, &folder.name).await {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use tracing::{error, info};
|
|||||||
|
|
||||||
use crate::application::ports::recent_ports::RecentItemsUseCase;
|
use crate::application::ports::recent_ports::RecentItemsUseCase;
|
||||||
use crate::interfaces::middleware::auth::AuthUser;
|
use crate::interfaces::middleware::auth::AuthUser;
|
||||||
|
use crate::application::services::recent_service::RecentService;
|
||||||
|
|
||||||
/// Query parameters for getting recent items
|
/// Query parameters for getting recent items
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -20,7 +21,7 @@ pub struct GetRecentParams {
|
|||||||
|
|
||||||
/// Get user's recent items
|
/// Get user's recent items
|
||||||
pub async fn get_recent_items(
|
pub async fn get_recent_items(
|
||||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
State(recent_service): State<Arc<RecentService>>,
|
||||||
auth_user: AuthUser,
|
auth_user: AuthUser,
|
||||||
Query(params): Query<GetRecentParams>,
|
Query(params): Query<GetRecentParams>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -46,7 +47,7 @@ pub async fn get_recent_items(
|
|||||||
|
|
||||||
/// Record access to an item
|
/// Record access to an item
|
||||||
pub async fn record_item_access(
|
pub async fn record_item_access(
|
||||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
State(recent_service): State<Arc<RecentService>>,
|
||||||
auth_user: AuthUser,
|
auth_user: AuthUser,
|
||||||
Path((item_type, item_id)): Path<(String, String)>,
|
Path((item_type, item_id)): Path<(String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -92,7 +93,7 @@ pub async fn record_item_access(
|
|||||||
|
|
||||||
/// Remove an item from recents
|
/// Remove an item from recents
|
||||||
pub async fn remove_from_recent(
|
pub async fn remove_from_recent(
|
||||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
State(recent_service): State<Arc<RecentService>>,
|
||||||
auth_user: AuthUser,
|
auth_user: AuthUser,
|
||||||
Path((item_type, item_id)): Path<(String, String)>,
|
Path((item_type, item_id)): Path<(String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -138,7 +139,7 @@ pub async fn remove_from_recent(
|
|||||||
|
|
||||||
/// Clear all recent items
|
/// Clear all recent items
|
||||||
pub async fn clear_recent_items(
|
pub async fn clear_recent_items(
|
||||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
State(recent_service): State<Arc<RecentService>>,
|
||||||
auth_user: AuthUser,
|
auth_user: AuthUser,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let user_id = &auth_user.id;
|
let user_id = &auth_user.id;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use crate::application::dtos::search_dto::SearchCriteriaDto;
|
|||||||
use crate::common::di::AppState;
|
use crate::common::di::AppState;
|
||||||
use crate::interfaces::middleware::auth::AuthUser;
|
use crate::interfaces::middleware::auth::AuthUser;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use crate::application::ports::inbound::SearchUseCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for search operations through the API.
|
* Handler for search operations through the API.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use crate::{
|
|||||||
domain::entities::share::ShareItemType,
|
domain::entities::share::ShareItemType,
|
||||||
interfaces::middleware::auth::OptionalAuthUser,
|
interfaces::middleware::auth::OptionalAuthUser,
|
||||||
};
|
};
|
||||||
|
use crate::application::services::share_service::ShareService;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct GetSharesQuery {
|
pub struct GetSharesQuery {
|
||||||
@@ -34,7 +35,7 @@ pub struct VerifyPasswordRequest {
|
|||||||
|
|
||||||
/// Create a new shared link
|
/// Create a new shared link
|
||||||
pub async fn create_shared_link(
|
pub async fn create_shared_link(
|
||||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
State(share_use_case): State<Arc<ShareService>>,
|
||||||
auth_user: OptionalAuthUser,
|
auth_user: OptionalAuthUser,
|
||||||
Json(dto): Json<CreateShareDto>,
|
Json(dto): Json<CreateShareDto>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -57,7 +58,7 @@ pub async fn create_shared_link(
|
|||||||
|
|
||||||
/// Get information about a specific shared link by ID
|
/// Get information about a specific shared link by ID
|
||||||
pub async fn get_shared_link(
|
pub async fn get_shared_link(
|
||||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
State(share_use_case): State<Arc<ShareService>>,
|
||||||
Path(id): Path<String>,
|
Path(id): Path<String>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match share_use_case.get_shared_link(&id).await {
|
match share_use_case.get_shared_link(&id).await {
|
||||||
@@ -75,7 +76,7 @@ pub async fn get_shared_link(
|
|||||||
/// Get all shared links created by the current user.
|
/// Get all shared links created by the current user.
|
||||||
/// Supports optional filtering by item_id + item_type query params.
|
/// Supports optional filtering by item_id + item_type query params.
|
||||||
pub async fn get_user_shares(
|
pub async fn get_user_shares(
|
||||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
State(share_use_case): State<Arc<ShareService>>,
|
||||||
auth_user: OptionalAuthUser,
|
auth_user: OptionalAuthUser,
|
||||||
Query(query): Query<GetSharesQuery>,
|
Query(query): Query<GetSharesQuery>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -128,7 +129,7 @@ pub async fn get_user_shares(
|
|||||||
|
|
||||||
/// Update a shared link's properties
|
/// Update a shared link's properties
|
||||||
pub async fn update_shared_link(
|
pub async fn update_shared_link(
|
||||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
State(share_use_case): State<Arc<ShareService>>,
|
||||||
Path(id): Path<String>,
|
Path(id): Path<String>,
|
||||||
Json(dto): Json<UpdateShareDto>,
|
Json(dto): Json<UpdateShareDto>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -148,7 +149,7 @@ pub async fn update_shared_link(
|
|||||||
|
|
||||||
/// Delete a shared link
|
/// Delete a shared link
|
||||||
pub async fn delete_shared_link(
|
pub async fn delete_shared_link(
|
||||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
State(share_use_case): State<Arc<ShareService>>,
|
||||||
Path(id): Path<String>,
|
Path(id): Path<String>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match share_use_case.delete_shared_link(&id).await {
|
match share_use_case.delete_shared_link(&id).await {
|
||||||
@@ -166,7 +167,7 @@ pub async fn delete_shared_link(
|
|||||||
|
|
||||||
/// Access a shared item via its token
|
/// Access a shared item via its token
|
||||||
pub async fn access_shared_item(
|
pub async fn access_shared_item(
|
||||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
State(share_use_case): State<Arc<ShareService>>,
|
||||||
Path(token): Path<String>,
|
Path(token): Path<String>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
// Register the access
|
// Register the access
|
||||||
@@ -204,7 +205,7 @@ pub async fn access_shared_item(
|
|||||||
|
|
||||||
/// Verify password for a password-protected shared item
|
/// Verify password for a password-protected shared item
|
||||||
pub async fn verify_shared_item_password(
|
pub async fn verify_shared_item_password(
|
||||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
State(share_use_case): State<Arc<ShareService>>,
|
||||||
Path(token): Path<String>,
|
Path(token): Path<String>,
|
||||||
Json(req): Json<VerifyPasswordRequest>,
|
Json(req): Json<VerifyPasswordRequest>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use axum::http::StatusCode;
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tracing::{debug, error, instrument, warn};
|
use tracing::{debug, error, instrument, warn};
|
||||||
|
|
||||||
// use crate::application::ports::trash_ports::TrashUseCase;
|
use crate::application::ports::trash_ports::TrashUseCase;
|
||||||
use crate::common::di::AppState;
|
use crate::common::di::AppState;
|
||||||
use crate::interfaces::middleware::auth::{AuthUser, OptionalAuthUser};
|
use crate::interfaces::middleware::auth::{AuthUser, OptionalAuthUser};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
@@ -28,8 +28,11 @@ use crate::common::di::AppState;
|
|||||||
use crate::infrastructure::services::path_resolver_service::ResolvedResource;
|
use crate::infrastructure::services::path_resolver_service::ResolvedResource;
|
||||||
use crate::interfaces::errors::AppError;
|
use crate::interfaces::errors::AppError;
|
||||||
use crate::interfaces::middleware::auth::CurrentUser;
|
use crate::interfaces::middleware::auth::CurrentUser;
|
||||||
|
use crate::application::services::file_retrieval_service::FileRetrievalService;
|
||||||
|
use crate::application::services::folder_service::FolderService;
|
||||||
use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, percent_decode_str, utf8_percent_encode};
|
use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, percent_decode_str, utf8_percent_encode};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use crate::application::ports::file_ports::{FileManagementUseCase, FileUploadUseCase};
|
||||||
|
|
||||||
/// Characters that MUST NOT be percent-encoded inside a URI path segment.
|
/// Characters that MUST NOT be percent-encoded inside a URI path segment.
|
||||||
/// RFC 3986 §3.3 pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
/// RFC 3986 §3.3 pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||||
@@ -394,8 +397,8 @@ async fn build_streaming_propfind_response(
|
|||||||
depth: &str,
|
depth: &str,
|
||||||
base_href: &str,
|
base_href: &str,
|
||||||
propfind_request: PropFindRequest,
|
propfind_request: PropFindRequest,
|
||||||
folder_service: std::sync::Arc<dyn FolderUseCase>,
|
folder_service: std::sync::Arc<FolderService>,
|
||||||
file_retrieval_service: std::sync::Arc<dyn FileRetrievalUseCase>,
|
file_retrieval_service: std::sync::Arc<FileRetrievalService>,
|
||||||
) -> Result<Response<Body>, AppError> {
|
) -> Result<Response<Body>, AppError> {
|
||||||
let depth = depth.to_string();
|
let depth = depth.to_string();
|
||||||
let base_href = base_href.to_string();
|
let base_href = base_href.to_string();
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use std::sync::Arc;
|
|||||||
use crate::application::services::wopi_lock_service::WopiLockService;
|
use crate::application::services::wopi_lock_service::WopiLockService;
|
||||||
use crate::application::services::wopi_token_service::WopiTokenService;
|
use crate::application::services::wopi_token_service::WopiTokenService;
|
||||||
use crate::infrastructure::services::wopi_discovery_service::WopiDiscoveryService;
|
use crate::infrastructure::services::wopi_discovery_service::WopiDiscoveryService;
|
||||||
|
use crate::application::ports::file_ports::{FileRetrievalUseCase, FileUploadUseCase};
|
||||||
|
|
||||||
/// Shared state for WOPI handlers.
|
/// Shared state for WOPI handlers.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use axum::{
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tower_http::{compression::CompressionLayer, trace::TraceLayer};
|
use tower_http::{compression::CompressionLayer, trace::TraceLayer};
|
||||||
|
use crate::application::services::batch_operations::BatchOperationService;
|
||||||
|
|
||||||
/// Returns the application version from Cargo.toml (compile-time constant)
|
/// Returns the application version from Cargo.toml (compile-time constant)
|
||||||
async fn get_version() -> AxumJson<serde_json::Value> {
|
async fn get_version() -> AxumJson<serde_json::Value> {
|
||||||
@@ -17,7 +18,6 @@ async fn get_version() -> AxumJson<serde_json::Value> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::application::services::batch_operations::BatchOperationService;
|
|
||||||
|
|
||||||
use crate::interfaces::api::handlers::admin_handler;
|
use crate::interfaces::api::handlers::admin_handler;
|
||||||
use crate::interfaces::api::handlers::batch_handler::{self, BatchHandlerState};
|
use crate::interfaces::api::handlers::batch_handler::{self, BatchHandlerState};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user