refactor: remove serde from domain entities for Clean Architecture compliance
- Remove Serialize/Deserialize from File, Folder, Session, User, Contact entities - Create contact_persistence_dto.rs for JSONB persistence in infrastructure layer - Update contact_pg_repository to use persistence DTOs - Fix dependency on zip crate (downgrade from 7.2.0 to 2.1.0) - Fix unused variable warnings in main.rs - Move PathService import from domain to infrastructure - Add missing fields to CoreServices and RepositoryServices - Create proper service initialization in main.rs Clean Architecture improvements: - Domain layer no longer depends on serde framework - Persistence concerns isolated to infrastructure layer - TokenClaims in auth_service.rs is only exception (required for JWT)
This commit is contained in:
@@ -48,7 +48,6 @@ impl CalDavAdapter {
|
||||
let mut in_sync_collection = false;
|
||||
let mut in_prop = false;
|
||||
let mut in_filter = false;
|
||||
let mut in_time_range = false;
|
||||
let mut start_time: Option<DateTime<Utc>> = None;
|
||||
let mut end_time: Option<DateTime<Utc>> = None;
|
||||
let mut props = Vec::new();
|
||||
@@ -68,8 +67,6 @@ impl CalDavAdapter {
|
||||
s if s == "prop" || s.ends_with(":prop") => in_prop = true,
|
||||
s if s == "filter" || s.ends_with(":filter") => in_filter = true,
|
||||
s if s == "time-range" || s.ends_with(":time-range") => {
|
||||
in_time_range = true;
|
||||
|
||||
// Parse time-range attributes
|
||||
for attr in e.attributes() {
|
||||
if let Ok(attr) = attr {
|
||||
@@ -106,7 +103,7 @@ impl CalDavAdapter {
|
||||
}
|
||||
},
|
||||
Ok(Event::Text(e)) => {
|
||||
let text = e.unescape().unwrap_or_default();
|
||||
let text = e.decode().unwrap_or_default();
|
||||
|
||||
// Check if we're in sync-token element
|
||||
if in_sync_collection && !in_prop && !in_filter {
|
||||
@@ -128,7 +125,7 @@ impl CalDavAdapter {
|
||||
s if s == "sync-collection" || s.ends_with(":sync-collection") => in_sync_collection = false,
|
||||
s if s == "prop" || s.ends_with(":prop") => in_prop = false,
|
||||
s if s == "filter" || s.ends_with(":filter") => in_filter = false,
|
||||
s if s == "time-range" || s.ends_with(":time-range") => in_time_range = false,
|
||||
s if s == "time-range" || s.ends_with(":time-range") => { /* time-range end, attributes already parsed */ },
|
||||
_ => ()
|
||||
}
|
||||
},
|
||||
@@ -771,7 +768,7 @@ impl CalDavAdapter {
|
||||
}
|
||||
},
|
||||
Ok(Event::Text(e)) => {
|
||||
let text = e.unescape().unwrap_or_default();
|
||||
let text = e.decode().unwrap_or_default();
|
||||
|
||||
if in_displayname {
|
||||
displayname = text.to_string();
|
||||
|
||||
@@ -685,7 +685,7 @@ impl WebDavAdapter {
|
||||
},
|
||||
Ok(Event::Text(e)) => {
|
||||
if current_prop.is_some() {
|
||||
current_text.push_str(&e.unescape().unwrap_or_default());
|
||||
current_text.push_str(&e.decode().unwrap_or_default());
|
||||
}
|
||||
},
|
||||
Ok(Event::End(ref e)) => {
|
||||
@@ -879,7 +879,7 @@ impl WebDavAdapter {
|
||||
},
|
||||
Ok(Event::Text(e)) => {
|
||||
if in_owner {
|
||||
owner_text.push_str(&e.unescape().unwrap_or_default());
|
||||
owner_text.push_str(&e.decode().unwrap_or_default());
|
||||
}
|
||||
},
|
||||
Ok(Event::End(ref e)) => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
use std::collections::HashMap;
|
||||
use crate::domain::entities::calendar::Calendar;
|
||||
use crate::domain::entities::calendar_event::CalendarEvent;
|
||||
|
||||
@@ -65,4 +65,13 @@ pub struct ChangePasswordDto {
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct RefreshTokenDto {
|
||||
pub refresh_token: String,
|
||||
}
|
||||
|
||||
/// Datos del usuario autenticado actual (para uso en servicios de application)
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct CurrentUser {
|
||||
pub id: String,
|
||||
pub username: String,
|
||||
pub email: String,
|
||||
pub role: String,
|
||||
}
|
||||
@@ -3,6 +3,66 @@ use crate::domain::entities::user::User;
|
||||
use crate::domain::entities::session::Session;
|
||||
use crate::common::errors::DomainError;
|
||||
|
||||
// ============================================================================
|
||||
// Cryptography Ports - Extracted from Domain to maintain Clean Architecture
|
||||
// ============================================================================
|
||||
|
||||
/// Port for password hashing operations.
|
||||
///
|
||||
/// This trait abstracts cryptographic password operations, allowing the domain
|
||||
/// layer to remain independent of specific hashing implementations (argon2, bcrypt, etc.)
|
||||
pub trait PasswordHasherPort: Send + Sync + 'static {
|
||||
/// Hash a plain text password
|
||||
fn hash_password(&self, password: &str) -> Result<String, DomainError>;
|
||||
|
||||
/// Verify a plain text password against a hash
|
||||
fn verify_password(&self, password: &str, hash: &str) -> Result<bool, DomainError>;
|
||||
}
|
||||
|
||||
/// Claims contained in a JWT token
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TokenClaims {
|
||||
/// Subject identifier (user ID)
|
||||
pub sub: String,
|
||||
/// Expiration timestamp (seconds since Unix epoch)
|
||||
pub exp: i64,
|
||||
/// Issued at timestamp (seconds since Unix epoch)
|
||||
pub iat: i64,
|
||||
/// JWT unique ID
|
||||
pub jti: String,
|
||||
/// Username
|
||||
pub username: String,
|
||||
/// User email
|
||||
pub email: String,
|
||||
/// User role
|
||||
pub role: String,
|
||||
}
|
||||
|
||||
/// Port for JWT token operations.
|
||||
///
|
||||
/// This trait abstracts token generation and validation, allowing the domain
|
||||
/// layer to remain independent of specific JWT implementations.
|
||||
pub trait TokenServicePort: Send + Sync + 'static {
|
||||
/// Generate an access token for a user
|
||||
fn generate_access_token(&self, user: &User) -> Result<String, DomainError>;
|
||||
|
||||
/// Validate a token and extract its claims
|
||||
fn validate_token(&self, token: &str) -> Result<TokenClaims, DomainError>;
|
||||
|
||||
/// Generate a refresh token
|
||||
fn generate_refresh_token(&self) -> String;
|
||||
|
||||
/// Get refresh token expiry in seconds
|
||||
fn refresh_token_expiry_secs(&self) -> i64;
|
||||
|
||||
/// Get refresh token expiry in days
|
||||
fn refresh_token_expiry_days(&self) -> i64;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Storage Ports
|
||||
// ============================================================================
|
||||
|
||||
#[async_trait]
|
||||
pub trait UserStoragePort: Send + Sync + 'static {
|
||||
/// Crea un nuevo usuario
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
use crate::domain::entities::user::{User, UserRole};
|
||||
use crate::domain::entities::session::Session;
|
||||
use crate::domain::services::auth_service::AuthService;
|
||||
use crate::application::ports::auth_ports::{UserStoragePort, SessionStoragePort};
|
||||
use crate::application::ports::auth_ports::{UserStoragePort, SessionStoragePort, PasswordHasherPort, TokenServicePort};
|
||||
use crate::application::dtos::user_dto::{UserDto, RegisterDto, LoginDto, AuthResponseDto, ChangePasswordDto, RefreshTokenDto};
|
||||
use crate::application::dtos::folder_dto::CreateFolderDto;
|
||||
use crate::application::ports::inbound::FolderUseCase;
|
||||
@@ -11,7 +10,8 @@ use crate::common::errors::{DomainError, ErrorKind};
|
||||
pub struct AuthApplicationService {
|
||||
user_storage: Arc<dyn UserStoragePort>,
|
||||
session_storage: Arc<dyn SessionStoragePort>,
|
||||
auth_service: Arc<AuthService>,
|
||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
||||
token_service: Arc<dyn TokenServicePort>,
|
||||
folder_service: Option<Arc<dyn FolderUseCase>>,
|
||||
}
|
||||
|
||||
@@ -19,12 +19,14 @@ impl AuthApplicationService {
|
||||
pub fn new(
|
||||
user_storage: Arc<dyn UserStoragePort>,
|
||||
session_storage: Arc<dyn SessionStoragePort>,
|
||||
auth_service: Arc<AuthService>,
|
||||
password_hasher: Arc<dyn PasswordHasherPort>,
|
||||
token_service: Arc<dyn TokenServicePort>,
|
||||
) -> Self {
|
||||
Self {
|
||||
user_storage,
|
||||
session_storage,
|
||||
auth_service,
|
||||
password_hasher,
|
||||
token_service,
|
||||
folder_service: None,
|
||||
}
|
||||
}
|
||||
@@ -127,11 +129,23 @@ impl AuthApplicationService {
|
||||
1024 * 1024 * 1024 // 1GB para usuarios normales
|
||||
};
|
||||
|
||||
// Crear usuario
|
||||
// Validar longitud de password antes de hashear
|
||||
if dto.password.len() < 8 {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"User",
|
||||
"Password debe tener al menos 8 caracteres"
|
||||
));
|
||||
}
|
||||
|
||||
// Hashear el password usando el servicio de infraestructura
|
||||
let password_hash = self.password_hasher.hash_password(&dto.password)?;
|
||||
|
||||
// Crear usuario con el hash pre-generado
|
||||
let user = User::new(
|
||||
dto.username.clone(),
|
||||
dto.email,
|
||||
dto.password,
|
||||
password_hash,
|
||||
role,
|
||||
quota,
|
||||
).map_err(|e| DomainError::new(
|
||||
@@ -203,13 +217,8 @@ impl AuthApplicationService {
|
||||
));
|
||||
}
|
||||
|
||||
// Verificar contraseña
|
||||
let is_valid = user.verify_password(&dto.password)
|
||||
.map_err(|_| DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Auth",
|
||||
"Credenciales inválidas"
|
||||
))?;
|
||||
// Verificar contraseña usando el hasher inyectado
|
||||
let is_valid = self.password_hasher.verify_password(&dto.password, user.password_hash())?;
|
||||
|
||||
if !is_valid {
|
||||
return Err(DomainError::new(
|
||||
@@ -223,11 +232,10 @@ impl AuthApplicationService {
|
||||
user.register_login();
|
||||
self.user_storage.update_user(user.clone()).await?;
|
||||
|
||||
// Generar tokens
|
||||
let access_token = self.auth_service.generate_access_token(&user)
|
||||
.map_err(DomainError::from)?;
|
||||
// Generar tokens usando el servicio de tokens inyectado
|
||||
let access_token = self.token_service.generate_access_token(&user)?;
|
||||
|
||||
let refresh_token = self.auth_service.generate_refresh_token();
|
||||
let refresh_token = self.token_service.generate_refresh_token();
|
||||
|
||||
// Guardar sesión
|
||||
let session = Session::new(
|
||||
@@ -235,7 +243,7 @@ impl AuthApplicationService {
|
||||
refresh_token.clone(),
|
||||
None, // IP (se puede añadir desde la capa HTTP)
|
||||
None, // User-Agent (se puede añadir desde la capa HTTP)
|
||||
self.auth_service.refresh_token_expiry_days(),
|
||||
self.token_service.refresh_token_expiry_days(),
|
||||
);
|
||||
|
||||
self.session_storage.create_session(session).await?;
|
||||
@@ -246,7 +254,7 @@ impl AuthApplicationService {
|
||||
access_token,
|
||||
refresh_token,
|
||||
token_type: "Bearer".to_string(),
|
||||
expires_in: self.auth_service.refresh_token_expiry_secs(),
|
||||
expires_in: self.token_service.refresh_token_expiry_secs(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -283,10 +291,9 @@ impl AuthApplicationService {
|
||||
self.session_storage.revoke_session(session.id()).await?;
|
||||
|
||||
// Generar nuevos tokens
|
||||
let access_token = self.auth_service.generate_access_token(&user)
|
||||
.map_err(DomainError::from)?;
|
||||
let access_token = self.token_service.generate_access_token(&user)?;
|
||||
|
||||
let new_refresh_token = self.auth_service.generate_refresh_token();
|
||||
let new_refresh_token = self.token_service.generate_refresh_token();
|
||||
|
||||
// Crear nueva sesión
|
||||
let new_session = Session::new(
|
||||
@@ -294,7 +301,7 @@ impl AuthApplicationService {
|
||||
new_refresh_token.clone(),
|
||||
None,
|
||||
None,
|
||||
self.auth_service.refresh_token_expiry_days(),
|
||||
self.token_service.refresh_token_expiry_days(),
|
||||
);
|
||||
|
||||
self.session_storage.create_session(new_session).await?;
|
||||
@@ -304,7 +311,7 @@ impl AuthApplicationService {
|
||||
access_token,
|
||||
refresh_token: new_refresh_token,
|
||||
token_type: "Bearer".to_string(),
|
||||
expires_in: self.auth_service.refresh_token_expiry_secs(),
|
||||
expires_in: self.token_service.refresh_token_expiry_secs(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -342,13 +349,8 @@ impl AuthApplicationService {
|
||||
// Obtener usuario
|
||||
let mut user = self.user_storage.get_user_by_id(user_id).await?;
|
||||
|
||||
// Verificar contraseña actual
|
||||
let is_valid = user.verify_password(&dto.current_password)
|
||||
.map_err(|_| DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Auth",
|
||||
"Contraseña actual incorrecta"
|
||||
))?;
|
||||
// Verificar contraseña actual usando el hasher inyectado
|
||||
let is_valid = self.password_hasher.verify_password(&dto.current_password, user.password_hash())?;
|
||||
|
||||
if !is_valid {
|
||||
return Err(DomainError::new(
|
||||
@@ -358,13 +360,18 @@ impl AuthApplicationService {
|
||||
));
|
||||
}
|
||||
|
||||
// Actualizar contraseña
|
||||
user.update_password(dto.new_password.clone())
|
||||
.map_err(|e| DomainError::new(
|
||||
// Validar nueva contraseña
|
||||
if dto.new_password.len() < 8 {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"User",
|
||||
format!("Error al cambiar contraseña: {}", e)
|
||||
))?;
|
||||
"Password debe tener al menos 8 caracteres"
|
||||
));
|
||||
}
|
||||
|
||||
// Hashear nueva contraseña y actualizar usuario
|
||||
let new_hash = self.password_hasher.hash_password(&dto.new_password)?;
|
||||
user.update_password_hash(new_hash);
|
||||
|
||||
// Guardar usuario actualizado
|
||||
self.user_storage.update_user(user).await?;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use futures::{future::join_all, Future};
|
||||
use tokio::sync::Semaphore;
|
||||
use tracing::{info, error};
|
||||
use tracing::info;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::application::services::file_service::FileService;
|
||||
use crate::application::services::folder_service::FolderService;
|
||||
use crate::domain::services::path_service::StoragePath;
|
||||
use crate::common::errors::DomainError;
|
||||
use crate::common::config::AppConfig;
|
||||
use crate::application::ports::inbound::FolderUseCase;
|
||||
@@ -15,7 +14,6 @@ use crate::application::dtos::folder_dto::FolderDto;
|
||||
|
||||
/// Errores específicos para operaciones por lotes
|
||||
#[derive(Debug, Error)]
|
||||
#[allow(dead_code)]
|
||||
pub enum BatchOperationError {
|
||||
#[error("Error de dominio: {0}")]
|
||||
Domain(#[from] DomainError),
|
||||
@@ -59,52 +57,6 @@ pub struct BatchStats {
|
||||
pub max_concurrency: usize,
|
||||
}
|
||||
|
||||
/// Tipo de entidad para operaciones por lotes
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum EntityType {
|
||||
File,
|
||||
Folder,
|
||||
}
|
||||
|
||||
/// Tipo de operación por lotes
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum BatchOperationType {
|
||||
Create,
|
||||
Read,
|
||||
Update,
|
||||
Delete,
|
||||
Copy,
|
||||
Move,
|
||||
}
|
||||
|
||||
/// Identificador para una entidad (ID o ruta)
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum EntityIdentifier {
|
||||
Id(String),
|
||||
Path(StoragePath),
|
||||
}
|
||||
|
||||
impl EntityIdentifier {
|
||||
#[allow(dead_code)]
|
||||
pub fn as_id(&self) -> Option<&str> {
|
||||
match self {
|
||||
EntityIdentifier::Id(id) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn as_path(&self) -> Option<&StoragePath> {
|
||||
match self {
|
||||
EntityIdentifier::Path(path) => Some(path),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Servicio de operaciones por lotes
|
||||
pub struct BatchOperationService {
|
||||
file_service: Arc<FileService>,
|
||||
@@ -494,7 +446,6 @@ impl BatchOperationService {
|
||||
}
|
||||
|
||||
/// Operación genérica de lote para cualquier tipo de función asíncrona
|
||||
#[allow(dead_code)]
|
||||
pub async fn generic_batch_operation<T, F, Fut>(
|
||||
&self,
|
||||
items: Vec<T>,
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::dtos::calendar_dto::{
|
||||
CalendarDto, CalendarEventDto, CreateCalendarDto, UpdateCalendarDto,
|
||||
CreateEventDto, UpdateEventDto, CreateEventICalDto
|
||||
};
|
||||
use crate::application::ports::calendar_ports::{CalendarStoragePort, CalendarUseCase};
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::common::errors::{DomainError, ErrorKind};
|
||||
|
||||
pub struct CalendarService {
|
||||
|
||||
@@ -9,12 +9,11 @@ use crate::application::dtos::address_book_dto::{
|
||||
};
|
||||
use crate::application::dtos::contact_dto::{
|
||||
ContactDto, CreateContactDto, UpdateContactDto, CreateContactVCardDto,
|
||||
ContactGroupDto, CreateContactGroupDto, UpdateContactGroupDto, GroupMembershipDto,
|
||||
EmailDto, PhoneDto, AddressDto
|
||||
ContactGroupDto, CreateContactGroupDto, UpdateContactGroupDto, GroupMembershipDto
|
||||
};
|
||||
use crate::application::ports::carddav_ports::{AddressBookUseCase, ContactUseCase};
|
||||
use crate::application::ports::storage_ports::StorageUseCase;
|
||||
use crate::common::errors::{DomainError, ErrorContext};
|
||||
use crate::common::errors::DomainError;
|
||||
use crate::domain::entities::contact::{AddressBook, Contact, ContactGroup, Email, Phone, Address};
|
||||
use crate::domain::repositories::address_book_repository::AddressBookRepository;
|
||||
use crate::domain::repositories::contact_repository::{ContactRepository, ContactGroupRepository};
|
||||
|
||||
@@ -16,13 +16,6 @@ impl FileManagementService {
|
||||
pub fn new(file_repository: Arc<dyn FileWritePort>) -> Self {
|
||||
Self { file_repository }
|
||||
}
|
||||
|
||||
/// Creates a stub for testing
|
||||
pub fn default_stub() -> Self {
|
||||
Self {
|
||||
file_repository: Arc::new(crate::infrastructure::repositories::FileFsWriteRepository::default_stub())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -18,13 +18,6 @@ impl FileRetrievalService {
|
||||
pub fn new(file_repository: Arc<dyn FileReadPort>) -> Self {
|
||||
Self { file_repository }
|
||||
}
|
||||
|
||||
/// Crea un stub para pruebas
|
||||
pub fn default_stub() -> Self {
|
||||
Self {
|
||||
file_repository: Arc::new(crate::infrastructure::repositories::FileFsReadRepository::default_stub())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -48,14 +48,6 @@ impl FileUploadService {
|
||||
self.storage_usage_service = Some(storage_usage_service);
|
||||
self
|
||||
}
|
||||
|
||||
/// Crea un stub para pruebas
|
||||
pub fn default_stub() -> Self {
|
||||
Self {
|
||||
file_repository: Arc::new(crate::infrastructure::repositories::FileFsWriteRepository::default_stub()),
|
||||
storage_usage_service: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -23,14 +23,6 @@ impl AppFileUseCaseFactory {
|
||||
file_write_repository,
|
||||
}
|
||||
}
|
||||
|
||||
/// Crea un stub para pruebas
|
||||
pub fn default_stub() -> Self {
|
||||
Self {
|
||||
file_read_repository: Arc::new(crate::infrastructure::repositories::FileFsReadRepository::default_stub()),
|
||||
file_write_repository: Arc::new(crate::infrastructure::repositories::FileFsWriteRepository::default_stub()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileUseCaseFactory for AppFileUseCaseFactory {
|
||||
|
||||
@@ -51,7 +51,6 @@ impl I18nApplicationService {
|
||||
}
|
||||
|
||||
/// Load translations for all available locales
|
||||
#[allow(dead_code)]
|
||||
pub async fn load_all_translations(&self) -> Vec<(Locale, I18nResult<()>)> {
|
||||
let locales = self.i18n_service.available_locales().await;
|
||||
let mut results = Vec::new();
|
||||
@@ -70,7 +69,6 @@ impl I18nApplicationService {
|
||||
}
|
||||
|
||||
/// Check if a locale is supported
|
||||
#[allow(dead_code)]
|
||||
pub async fn is_supported(&self, locale: Locale) -> bool {
|
||||
self.i18n_service.is_supported(locale).await
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use thiserror::Error;
|
||||
use crate::domain::entities::folder::Folder;
|
||||
use crate::domain::repositories::folder_repository::{FolderRepository, FolderRepositoryError};
|
||||
use crate::domain::repositories::file_repository::FileRepositoryError;
|
||||
use crate::domain::services::path_service::{PathService, StoragePath};
|
||||
use crate::application::ports::outbound::IdMappingPort;
|
||||
use crate::domain::services::path_service::StoragePath;
|
||||
use crate::application::ports::outbound::{IdMappingPort, StoragePort};
|
||||
|
||||
/// Errores específicos del mediador de almacenamiento
|
||||
#[derive(Debug, Error)]
|
||||
@@ -99,12 +99,12 @@ pub trait StorageMediator: Send + Sync + 'static {
|
||||
/// Implementación concreta del mediador de almacenamiento
|
||||
pub struct FileSystemStorageMediator {
|
||||
pub folder_repository: Arc<dyn FolderRepository>,
|
||||
pub path_service: Arc<PathService>,
|
||||
pub path_service: Arc<dyn StoragePort>,
|
||||
pub id_mapping: Arc<dyn IdMappingPort>,
|
||||
}
|
||||
|
||||
impl FileSystemStorageMediator {
|
||||
pub fn new(folder_repository: Arc<dyn FolderRepository>, path_service: Arc<PathService>, id_mapping: Arc<dyn IdMappingPort>) -> Self {
|
||||
pub fn new(folder_repository: Arc<dyn FolderRepository>, path_service: Arc<dyn StoragePort>, id_mapping: Arc<dyn IdMappingPort>) -> Self {
|
||||
Self { folder_repository, path_service, id_mapping }
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ impl FileSystemStorageMediator {
|
||||
/// Overload para implementar inicialización diferida con repository placeholder
|
||||
pub fn new_with_lazy_folder(
|
||||
_folder_repository: Arc<RwLock<Option<Arc<dyn FolderRepository>>>>,
|
||||
path_service: Arc<PathService>,
|
||||
path_service: Arc<dyn StoragePort>,
|
||||
id_mapping: Arc<dyn IdMappingPort>
|
||||
) -> Self {
|
||||
// Create temporary stub repository
|
||||
@@ -208,16 +208,18 @@ impl FolderRepository for FolderRepositoryStub {
|
||||
}
|
||||
|
||||
/// Stub implementation for initialization dependency issues
|
||||
pub struct StubStorageMediator {
|
||||
#[allow(dead_code)]
|
||||
_path_service: Arc<PathService>,
|
||||
}
|
||||
/// This is a minimal stub that doesn't require any infrastructure dependencies
|
||||
pub struct StubStorageMediator;
|
||||
|
||||
impl StubStorageMediator {
|
||||
pub fn new() -> Self {
|
||||
let root_path = PathBuf::from("/tmp");
|
||||
let path_service = Arc::new(PathService::new(root_path));
|
||||
Self { _path_service: path_service }
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for StubStorageMediator {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use async_trait::async_trait;
|
||||
use tokio::task;
|
||||
use crate::common::errors::DomainError;
|
||||
use crate::application::ports::auth_ports::UserStoragePort;
|
||||
use crate::domain::repositories::file_repository::FileRepository;
|
||||
use crate::application::ports::outbound::FileStoragePort;
|
||||
use crate::application::ports::storage_ports::StorageUsagePort;
|
||||
use tracing::{info, error, debug};
|
||||
|
||||
@@ -14,14 +14,14 @@ use tracing::{info, error, debug};
|
||||
* is using and updating this information in the user records.
|
||||
*/
|
||||
pub struct StorageUsageService {
|
||||
file_repository: Arc<dyn FileRepository>,
|
||||
file_repository: Arc<dyn FileStoragePort>,
|
||||
user_repository: Arc<dyn UserStoragePort>,
|
||||
}
|
||||
|
||||
impl StorageUsageService {
|
||||
/// Creates a new storage usage service
|
||||
pub fn new(
|
||||
file_repository: Arc<dyn FileRepository>,
|
||||
file_repository: Arc<dyn FileStoragePort>,
|
||||
user_repository: Arc<dyn UserStoragePort>,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -90,7 +90,7 @@ impl StorageUsageService {
|
||||
async fn calculate_folder_size(&self, folder_id: &str) -> Result<i64, DomainError> {
|
||||
// Implementation with explicit boxing to handle recursion in async functions
|
||||
async fn inner_calculate_size(
|
||||
repo: Arc<dyn FileRepository>,
|
||||
repo: Arc<dyn FileStoragePort>,
|
||||
folder_id: &str,
|
||||
) -> Result<i64, DomainError> {
|
||||
let mut total_size: i64 = 0;
|
||||
|
||||
@@ -37,7 +37,6 @@ impl StorageTransaction {
|
||||
}
|
||||
|
||||
/// Añade una operación sin rollback (para limpieza o logging)
|
||||
#[allow(dead_code)]
|
||||
pub fn add_finalizer<F>(&mut self, finalizer: F)
|
||||
where
|
||||
F: Future<Output = Result<(), DomainError>> + Send + 'static,
|
||||
|
||||
Reference in New Issue
Block a user