refactoring hexagonal and clean architecture
This commit is contained in:
@@ -1,292 +0,0 @@
|
||||
//! Adaptadores para convertir entre interfaces de dominio y aplicación
|
||||
//!
|
||||
//! Este módulo contiene adaptadores que permiten usar repositorios que implementan
|
||||
//! `FileStoragePort` y `FolderStoragePort` donde se espera `FileRepository` y `FolderRepository`.
|
||||
|
||||
use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::application::ports::outbound::{FileStoragePort, FolderStoragePort};
|
||||
use crate::domain::entities::file::File;
|
||||
use crate::domain::entities::folder::Folder;
|
||||
use crate::domain::repositories::file_repository::{FileRepository, FileRepositoryResult, FileRepositoryError};
|
||||
use crate::domain::repositories::folder_repository::{FolderRepository, FolderRepositoryResult, FolderRepositoryError};
|
||||
use crate::domain::services::path_service::StoragePath;
|
||||
|
||||
/// Adaptador que convierte FileStoragePort a FileRepository
|
||||
pub struct DomainFileRepoAdapter {
|
||||
repo: Arc<dyn FileStoragePort>,
|
||||
}
|
||||
|
||||
impl DomainFileRepoAdapter {
|
||||
pub fn new(repo: Arc<dyn FileStoragePort>) -> Self {
|
||||
Self { repo }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FileRepository for DomainFileRepoAdapter {
|
||||
async fn save_file_from_bytes(
|
||||
&self,
|
||||
name: String,
|
||||
folder_id: Option<String>,
|
||||
content_type: String,
|
||||
content: Vec<u8>,
|
||||
) -> FileRepositoryResult<File> {
|
||||
self.repo.save_file(name, folder_id, content_type, content)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn save_file_from_stream(
|
||||
&self,
|
||||
name: String,
|
||||
folder_id: Option<String>,
|
||||
content_type: String,
|
||||
stream: std::pin::Pin<Box<dyn futures::Stream<Item = Result<bytes::Bytes, std::io::Error>> + Send>>,
|
||||
) -> FileRepositoryResult<File> {
|
||||
self.repo.save_file_from_stream(name, folder_id, content_type, stream)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn save_file_with_id(
|
||||
&self,
|
||||
_id: String,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_content: Vec<u8>,
|
||||
) -> FileRepositoryResult<File> {
|
||||
Err(FileRepositoryError::Other("Not implemented".to_string()))
|
||||
}
|
||||
|
||||
async fn get_file_by_id(&self, id: &str) -> FileRepositoryResult<File> {
|
||||
self.repo.get_file(id)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn list_files(&self, folder_id: Option<&str>) -> FileRepositoryResult<Vec<File>> {
|
||||
self.repo.list_files(folder_id)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn delete_file(&self, id: &str) -> FileRepositoryResult<()> {
|
||||
self.repo.delete_file(id)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn delete_file_entry(&self, id: &str) -> FileRepositoryResult<()> {
|
||||
self.delete_file(id).await
|
||||
}
|
||||
|
||||
async fn get_file_content(&self, id: &str) -> FileRepositoryResult<Vec<u8>> {
|
||||
self.repo.get_file_content(id)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn get_file_stream(&self, id: &str) -> FileRepositoryResult<Box<dyn futures::Stream<Item = Result<bytes::Bytes, std::io::Error>> + Send>> {
|
||||
self.repo.get_file_stream(id)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn get_file_range_stream(
|
||||
&self,
|
||||
id: &str,
|
||||
start: u64,
|
||||
end: Option<u64>
|
||||
) -> FileRepositoryResult<Box<dyn futures::Stream<Item = Result<bytes::Bytes, std::io::Error>> + Send>> {
|
||||
self.repo.get_file_range_stream(id, start, end)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn get_file_mmap(&self, id: &str) -> FileRepositoryResult<bytes::Bytes> {
|
||||
self.repo.get_file_mmap(id)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn move_file(&self, id: &str, target_folder_id: Option<String>) -> FileRepositoryResult<File> {
|
||||
self.repo.move_file(id, target_folder_id)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn get_file_path(&self, id: &str) -> FileRepositoryResult<StoragePath> {
|
||||
self.repo.get_file_path(id)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn move_to_trash(&self, file_id: &str) -> FileRepositoryResult<()> {
|
||||
self.repo.delete_file(file_id)
|
||||
.await
|
||||
.map_err(|e| FileRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn restore_from_trash(&self, file_id: &str, original_path: &str) -> FileRepositoryResult<()> {
|
||||
tracing::info!("Restoring file from trash: {} to {}", file_id, original_path);
|
||||
|
||||
match self.repo.get_file(file_id).await {
|
||||
Ok(_) => {
|
||||
let path_components: Vec<&str> = original_path.split('/').collect();
|
||||
let parent_folder: Option<String> = if path_components.len() > 1 {
|
||||
tracing::info!("Attempting to restore to parent folder from path: {}", original_path);
|
||||
None
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match self.repo.move_file(file_id, parent_folder).await {
|
||||
Ok(_) => {
|
||||
tracing::info!("Successfully restored file from trash: {}", file_id);
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to restore file from trash: {}", e);
|
||||
Err(FileRepositoryError::Other(format!("Failed to restore file: {}", e)))
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("File not found in trash: {}", e);
|
||||
Err(FileRepositoryError::NotFound(file_id.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete_file_permanently(&self, file_id: &str) -> FileRepositoryResult<()> {
|
||||
tracing::info!("Permanently deleting file: {}", file_id);
|
||||
|
||||
match self.repo.delete_file(file_id).await {
|
||||
Ok(_) => {
|
||||
tracing::info!("Successfully deleted file permanently: {}", file_id);
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to permanently delete file: {}", e);
|
||||
Err(FileRepositoryError::Other(format!("Failed to delete file permanently: {}", e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_file_content(&self, file_id: &str, content: Vec<u8>) -> FileRepositoryResult<()> {
|
||||
tracing::info!("Updating content for file: {}", file_id);
|
||||
|
||||
self.repo.update_file_content(file_id, content)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("Failed to update file content: {}", e);
|
||||
FileRepositoryError::Other(format!("Failed to update file content: {}", e))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Adaptador que convierte FolderStoragePort a FolderRepository
|
||||
pub struct DomainFolderRepoAdapter {
|
||||
repo: Arc<dyn FolderStoragePort>,
|
||||
}
|
||||
|
||||
impl DomainFolderRepoAdapter {
|
||||
pub fn new(repo: Arc<dyn FolderStoragePort>) -> Self {
|
||||
Self { repo }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FolderRepository for DomainFolderRepoAdapter {
|
||||
async fn create_folder(&self, name: String, parent_id: Option<String>) -> FolderRepositoryResult<Folder> {
|
||||
self.repo.create_folder(name, parent_id)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn get_folder_by_id(&self, id: &str) -> FolderRepositoryResult<Folder> {
|
||||
self.repo.get_folder(id)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn get_folder_by_storage_path(&self, storage_path: &StoragePath) -> FolderRepositoryResult<Folder> {
|
||||
self.repo.get_folder_by_path(storage_path)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn list_folders(&self, parent_id: Option<&str>) -> FolderRepositoryResult<Vec<Folder>> {
|
||||
self.repo.list_folders(parent_id)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn list_folders_paginated(
|
||||
&self,
|
||||
parent_id: Option<&str>,
|
||||
offset: usize,
|
||||
limit: usize,
|
||||
include_total: bool
|
||||
) -> FolderRepositoryResult<(Vec<Folder>, Option<usize>)> {
|
||||
self.repo.list_folders_paginated(parent_id, offset, limit, include_total)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn rename_folder(&self, id: &str, new_name: String) -> FolderRepositoryResult<Folder> {
|
||||
self.repo.rename_folder(id, new_name)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn move_folder(&self, id: &str, new_parent_id: Option<&str>) -> FolderRepositoryResult<Folder> {
|
||||
self.repo.move_folder(id, new_parent_id)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn delete_folder(&self, id: &str) -> FolderRepositoryResult<()> {
|
||||
self.repo.delete_folder(id)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn folder_exists_at_storage_path(&self, storage_path: &StoragePath) -> FolderRepositoryResult<bool> {
|
||||
self.repo.folder_exists(storage_path)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn get_folder_storage_path(&self, id: &str) -> FolderRepositoryResult<StoragePath> {
|
||||
self.repo.get_folder_path(id)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn folder_exists(&self, _path: &std::path::PathBuf) -> FolderRepositoryResult<bool> {
|
||||
Err(FolderRepositoryError::Other("Not implemented".to_string()))
|
||||
}
|
||||
|
||||
async fn get_folder_by_path(&self, _path: &std::path::PathBuf) -> FolderRepositoryResult<Folder> {
|
||||
Err(FolderRepositoryError::Other("Not implemented".to_string()))
|
||||
}
|
||||
|
||||
async fn move_to_trash(&self, folder_id: &str) -> FolderRepositoryResult<()> {
|
||||
self.repo.delete_folder(folder_id)
|
||||
.await
|
||||
.map_err(|e| FolderRepositoryError::Other(format!("{}", e)))
|
||||
}
|
||||
|
||||
async fn restore_from_trash(&self, _folder_id: &str, _original_path: &str) -> FolderRepositoryResult<()> {
|
||||
Err(FolderRepositoryError::Other(
|
||||
"Restore from trash should be handled by TrashService, not through this adapter".to_string()))
|
||||
}
|
||||
|
||||
async fn delete_folder_permanently(&self, folder_id: &str) -> FolderRepositoryResult<()> {
|
||||
self.delete_folder(folder_id).await
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
use anyhow::Result;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::application::ports::auth_ports::TokenServicePort;
|
||||
use crate::application::services::auth_application_service::AuthApplicationService;
|
||||
use crate::application::services::folder_service::FolderService;
|
||||
use crate::infrastructure::repositories::{UserPgRepository, SessionPgRepository};
|
||||
use crate::infrastructure::services::password_hasher::Argon2PasswordHasher;
|
||||
use crate::infrastructure::services::jwt_service::JwtTokenService;
|
||||
use crate::common::config::AppConfig;
|
||||
use crate::common::di::AuthServices;
|
||||
|
||||
pub async fn create_auth_services(
|
||||
config: &AppConfig,
|
||||
pool: Arc<PgPool>,
|
||||
folder_service: Option<Arc<FolderService>>
|
||||
) -> Result<AuthServices> {
|
||||
// Crear servicio de tokens JWT (implementación de TokenServicePort)
|
||||
let token_service: Arc<dyn TokenServicePort> = Arc::new(JwtTokenService::new(
|
||||
config.auth.jwt_secret.clone(),
|
||||
config.auth.access_token_expiry_secs,
|
||||
config.auth.refresh_token_expiry_secs,
|
||||
));
|
||||
|
||||
// Crear servicio de hashing de contraseñas
|
||||
let password_hasher = Arc::new(Argon2PasswordHasher::new());
|
||||
|
||||
// Crear repositorios PostgreSQL
|
||||
let user_repository = Arc::new(UserPgRepository::new(pool.clone()));
|
||||
let session_repository = Arc::new(SessionPgRepository::new(pool.clone()));
|
||||
|
||||
// Crear servicio de aplicación de autenticación
|
||||
let mut auth_app_service = AuthApplicationService::new(
|
||||
user_repository,
|
||||
session_repository,
|
||||
password_hasher,
|
||||
token_service.clone(),
|
||||
);
|
||||
|
||||
// Configurar servicio de carpetas si está disponible
|
||||
if let Some(folder_svc) = folder_service {
|
||||
auth_app_service = auth_app_service.with_folder_service(folder_svc);
|
||||
}
|
||||
|
||||
// Empaquetar servicio en Arc
|
||||
let auth_application_service = Arc::new(auth_app_service);
|
||||
|
||||
Ok(AuthServices {
|
||||
token_service,
|
||||
auth_application_service,
|
||||
})
|
||||
}
|
||||
+21
-1
@@ -252,7 +252,10 @@ pub struct AuthConfig {
|
||||
impl Default for AuthConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
jwt_secret: "ox1cl0ud-sup3r-s3cr3t-k3y-f0r-t0k3n-s1gn1ng".to_string(),
|
||||
// SECURITY: This default is intentionally insecure to force operators
|
||||
// to set OXICLOUD_JWT_SECRET in production. The from_env() method
|
||||
// will validate this and warn/panic if not configured.
|
||||
jwt_secret: String::new(),
|
||||
access_token_expiry_secs: 3600, // 1 hora
|
||||
refresh_token_expiry_secs: 2592000, // 30 días
|
||||
hash_memory_cost: 65536, // 64MB
|
||||
@@ -377,6 +380,23 @@ impl AppConfig {
|
||||
if let Ok(jwt_secret) = env::var("OXICLOUD_JWT_SECRET") {
|
||||
config.auth.jwt_secret = jwt_secret;
|
||||
}
|
||||
|
||||
// SECURITY: Validate JWT secret when auth is enabled
|
||||
if config.features.enable_auth && config.auth.jwt_secret.is_empty() {
|
||||
// Generate a random secret for this session and warn loudly
|
||||
use rand_core::{OsRng, RngCore};
|
||||
let mut key = [0u8; 32];
|
||||
OsRng.fill_bytes(&mut key);
|
||||
let generated_secret: String = key.iter().map(|b| format!("{:02x}", b)).collect();
|
||||
config.auth.jwt_secret = generated_secret;
|
||||
|
||||
tracing::error!("==========================================================");
|
||||
tracing::error!("SECURITY WARNING: OXICLOUD_JWT_SECRET is not set!");
|
||||
tracing::error!("A random secret has been generated for this session.");
|
||||
tracing::error!("All tokens will be INVALIDATED on restart.");
|
||||
tracing::error!("Set OXICLOUD_JWT_SECRET env var for production use.");
|
||||
tracing::error!("==========================================================");
|
||||
}
|
||||
|
||||
if let Ok(access_token_expiry) = env::var("OXICLOUD_ACCESS_TOKEN_EXPIRY_SECS")
|
||||
.map(|v| v.parse::<i64>()) {
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
use sqlx::{postgres::PgPoolOptions, PgPool, Row};
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
use crate::common::config::AppConfig;
|
||||
|
||||
pub async fn create_database_pool(config: &AppConfig) -> Result<PgPool> {
|
||||
tracing::info!("Inicializando conexión a PostgreSQL con URL: {}",
|
||||
config.database.connection_string.replace("postgres://", "postgres://[user]:[pass]@"));
|
||||
|
||||
// Add a more robust connection attempt with retries
|
||||
let mut attempt = 0;
|
||||
const MAX_ATTEMPTS: usize = 3;
|
||||
|
||||
while attempt < MAX_ATTEMPTS {
|
||||
attempt += 1;
|
||||
tracing::info!("Intento de conexión a PostgreSQL #{}", attempt);
|
||||
|
||||
// Crear el pool de conexiones con las opciones de configuración
|
||||
match PgPoolOptions::new()
|
||||
.max_connections(config.database.max_connections)
|
||||
.min_connections(config.database.min_connections)
|
||||
.acquire_timeout(Duration::from_secs(config.database.connect_timeout_secs))
|
||||
.idle_timeout(Duration::from_secs(config.database.idle_timeout_secs))
|
||||
.max_lifetime(Duration::from_secs(config.database.max_lifetime_secs))
|
||||
.connect(&config.database.connection_string)
|
||||
.await {
|
||||
Ok(pool) => {
|
||||
// Verificar la conexión
|
||||
match sqlx::query("SELECT 1").execute(&pool).await {
|
||||
Ok(_) => {
|
||||
tracing::info!("Conexión a PostgreSQL establecida correctamente");
|
||||
|
||||
// Verify if migrations have been applied
|
||||
let migration_check = sqlx::query("SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'auth' AND tablename = 'users')")
|
||||
.fetch_one(&pool)
|
||||
.await;
|
||||
|
||||
match migration_check {
|
||||
Ok(row) => {
|
||||
let tables_exist: bool = row.get(0);
|
||||
if !tables_exist {
|
||||
tracing::warn!("Las tablas de la base de datos no existen. Por favor, ejecuta las migraciones con: cargo run --bin migrate --features migrations");
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
tracing::warn!("No se pudo verificar el estado de las migraciones. Por favor, ejecuta las migraciones con: cargo run --bin migrate --features migrations");
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(pool);
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Error al verificar conexión: {}", e);
|
||||
tracing::warn!("La base de datos parece no estar configurada. Por favor, ejecuta las migraciones con: cargo run --bin migrate --features migrations");
|
||||
if attempt >= MAX_ATTEMPTS {
|
||||
return Err(anyhow::anyhow!("Error al verificar la conexión a PostgreSQL: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Error al conectar a PostgreSQL: {}", e);
|
||||
if attempt >= MAX_ATTEMPTS {
|
||||
return Err(anyhow::anyhow!("Error en la conexión a PostgreSQL: {}", e));
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!("No se pudo establecer la conexión a PostgreSQL después de {} intentos", MAX_ATTEMPTS))
|
||||
}
|
||||
+332
-639
File diff suppressed because it is too large
Load Diff
+1
-3
@@ -1,6 +1,4 @@
|
||||
pub mod errors;
|
||||
pub mod config;
|
||||
pub mod di;
|
||||
pub mod db;
|
||||
pub mod auth_factory;
|
||||
pub mod adapters;
|
||||
pub mod stubs;
|
||||
@@ -0,0 +1,752 @@
|
||||
//! Stub/Dummy implementations for dependency injection.
|
||||
//!
|
||||
//! These no-op implementations are used exclusively by `AppState::default()`
|
||||
//! to provide a minimal, valid state for the auth middleware and route
|
||||
//! construction before the real services are wired in `main.rs`.
|
||||
//!
|
||||
//! **None of these stubs should ever handle real user requests.**
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use futures::Stream;
|
||||
|
||||
use crate::application::dtos::file_dto::FileDto;
|
||||
use crate::application::dtos::folder_dto::{CreateFolderDto, FolderDto, MoveFolderDto, RenameFolderDto};
|
||||
use crate::application::dtos::pagination::{PaginatedResponseDto, PaginationRequestDto};
|
||||
use crate::application::dtos::search_dto::{SearchCriteriaDto, SearchResultsDto};
|
||||
use crate::application::ports::compression_ports::{CompressionLevel, CompressionPort};
|
||||
use crate::application::ports::file_ports::{
|
||||
FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase, FileUseCaseFactory,
|
||||
UploadStrategy, OptimizedFileContent,
|
||||
};
|
||||
use crate::application::ports::inbound::{FolderUseCase, SearchUseCase};
|
||||
use crate::application::ports::outbound::IdMappingPort;
|
||||
use crate::domain::repositories::folder_repository::FolderRepository;
|
||||
use crate::application::ports::storage_ports::{FileReadPort, FileWritePort};
|
||||
use crate::application::ports::zip_ports::ZipPort;
|
||||
use crate::application::services::storage_mediator::{StorageMediator, StorageMediatorError};
|
||||
use crate::common::errors::DomainError;
|
||||
use crate::domain::entities::file::File;
|
||||
use crate::domain::entities::folder::Folder;
|
||||
use crate::domain::services::i18n_service::{I18nResult, I18nService, Locale};
|
||||
use crate::domain::services::path_service::StoragePath;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ZipPort
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Placeholder ZipPort that always errors. Replaced after application services
|
||||
/// are fully initialised.
|
||||
pub struct StubZipPort;
|
||||
|
||||
#[async_trait]
|
||||
impl ZipPort for StubZipPort {
|
||||
async fn create_folder_zip(
|
||||
&self,
|
||||
_folder_id: &str,
|
||||
_folder_name: &str,
|
||||
) -> Result<Vec<u8>, DomainError> {
|
||||
Err(DomainError::internal_error(
|
||||
"ZipService",
|
||||
"ZipService not initialized",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CompressionPort
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubCompressionPort;
|
||||
|
||||
#[async_trait]
|
||||
impl CompressionPort for StubCompressionPort {
|
||||
async fn compress_data(
|
||||
&self,
|
||||
_data: &[u8],
|
||||
_level: CompressionLevel,
|
||||
) -> Result<Vec<u8>, DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn decompress_data(&self, _compressed_data: &[u8]) -> Result<Vec<u8>, DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn should_compress(&self, _mime_type: &str, _size: u64) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IdMappingPort
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubIdMappingService;
|
||||
|
||||
#[async_trait]
|
||||
impl IdMappingPort for StubIdMappingService {
|
||||
async fn get_or_create_id(
|
||||
&self,
|
||||
_path: &StoragePath,
|
||||
) -> Result<String, DomainError> {
|
||||
Ok("dummy-id".to_string())
|
||||
}
|
||||
|
||||
async fn get_path_by_id(&self, _id: &str) -> Result<StoragePath, DomainError> {
|
||||
Ok(StoragePath::from_string("/"))
|
||||
}
|
||||
|
||||
async fn update_path(
|
||||
&self,
|
||||
_id: &str,
|
||||
_new_path: &StoragePath,
|
||||
) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_id(&self, _id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn save_changes(&self) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// StorageMediator
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubStorageMediator;
|
||||
|
||||
#[async_trait]
|
||||
impl StorageMediator for StubStorageMediator {
|
||||
async fn get_folder_path(
|
||||
&self,
|
||||
_folder_id: &str,
|
||||
) -> Result<PathBuf, StorageMediatorError> {
|
||||
Ok(PathBuf::from("/tmp"))
|
||||
}
|
||||
|
||||
async fn get_folder_storage_path(
|
||||
&self,
|
||||
_folder_id: &str,
|
||||
) -> Result<StoragePath, StorageMediatorError> {
|
||||
Ok(StoragePath::root())
|
||||
}
|
||||
|
||||
async fn get_folder(
|
||||
&self,
|
||||
_folder_id: &str,
|
||||
) -> Result<Folder, StorageMediatorError> {
|
||||
Err(StorageMediatorError::NotFound(
|
||||
"Stub not implemented".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn file_exists_at_path(
|
||||
&self,
|
||||
_path: &Path,
|
||||
) -> Result<bool, StorageMediatorError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn file_exists_at_storage_path(
|
||||
&self,
|
||||
_storage_path: &StoragePath,
|
||||
) -> Result<bool, StorageMediatorError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn folder_exists_at_path(
|
||||
&self,
|
||||
_path: &Path,
|
||||
) -> Result<bool, StorageMediatorError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn folder_exists_at_storage_path(
|
||||
&self,
|
||||
_storage_path: &StoragePath,
|
||||
) -> Result<bool, StorageMediatorError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn resolve_path(&self, _relative_path: &Path) -> PathBuf {
|
||||
PathBuf::from("/tmp")
|
||||
}
|
||||
|
||||
fn resolve_storage_path(&self, _storage_path: &StoragePath) -> PathBuf {
|
||||
PathBuf::from("/tmp")
|
||||
}
|
||||
|
||||
async fn ensure_directory(
|
||||
&self,
|
||||
_path: &Path,
|
||||
) -> Result<(), StorageMediatorError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn ensure_storage_directory(
|
||||
&self,
|
||||
_storage_path: &StoragePath,
|
||||
) -> Result<(), StorageMediatorError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IdMappingPort
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubIdMappingPort;
|
||||
|
||||
#[async_trait]
|
||||
impl IdMappingPort for StubIdMappingPort {
|
||||
async fn get_or_create_id(&self, _path: &StoragePath) -> Result<String, DomainError> {
|
||||
Ok("stub-id".to_string())
|
||||
}
|
||||
async fn get_path_by_id(&self, _id: &str) -> Result<StoragePath, DomainError> {
|
||||
Ok(StoragePath::from_string("/"))
|
||||
}
|
||||
async fn update_path(&self, _id: &str, _new_path: &StoragePath) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
async fn remove_id(&self, _id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
async fn save_changes(&self) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FileReadPort
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubFileReadPort;
|
||||
|
||||
#[async_trait]
|
||||
impl FileReadPort for StubFileReadPort {
|
||||
async fn get_file(&self, _id: &str) -> Result<File, DomainError> {
|
||||
Ok(File::default())
|
||||
}
|
||||
|
||||
async fn list_files(
|
||||
&self,
|
||||
_folder_id: Option<&str>,
|
||||
) -> Result<Vec<File>, DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_content(&self, _id: &str) -> Result<Vec<u8>, DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_stream(
|
||||
&self,
|
||||
_id: &str,
|
||||
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError> {
|
||||
let empty_stream = futures::stream::empty::<Result<Bytes, std::io::Error>>();
|
||||
Ok(Box::new(empty_stream))
|
||||
}
|
||||
|
||||
async fn get_file_range_stream(
|
||||
&self,
|
||||
_id: &str,
|
||||
_start: u64,
|
||||
_end: Option<u64>,
|
||||
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError> {
|
||||
let empty_stream = futures::stream::empty::<Result<Bytes, std::io::Error>>();
|
||||
Ok(Box::new(empty_stream))
|
||||
}
|
||||
|
||||
async fn get_file_mmap(&self, _id: &str) -> Result<Bytes, DomainError> {
|
||||
Ok(Bytes::new())
|
||||
}
|
||||
|
||||
async fn get_file_path(&self, _id: &str) -> Result<StoragePath, DomainError> {
|
||||
Ok(StoragePath::from_string("/"))
|
||||
}
|
||||
|
||||
async fn get_parent_folder_id(&self, _path: &str) -> Result<String, DomainError> {
|
||||
Ok("root".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FileWritePort
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubFileWritePort;
|
||||
|
||||
#[async_trait]
|
||||
impl FileWritePort for StubFileWritePort {
|
||||
async fn save_file(
|
||||
&self,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_content: Vec<u8>,
|
||||
) -> Result<File, DomainError> {
|
||||
Ok(File::default())
|
||||
}
|
||||
|
||||
async fn save_file_from_stream(
|
||||
&self,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_stream: std::pin::Pin<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>>,
|
||||
) -> Result<File, DomainError> {
|
||||
Ok(File::default())
|
||||
}
|
||||
|
||||
async fn move_file(
|
||||
&self,
|
||||
_file_id: &str,
|
||||
_target_folder_id: Option<String>,
|
||||
) -> Result<File, DomainError> {
|
||||
Ok(File::default())
|
||||
}
|
||||
|
||||
async fn delete_file(&self, _id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_file_content(
|
||||
&self,
|
||||
_file_id: &str,
|
||||
_content: Vec<u8>,
|
||||
) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn register_file_deferred(
|
||||
&self,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_size: u64,
|
||||
) -> Result<(File, PathBuf), DomainError> {
|
||||
Ok((File::default(), PathBuf::from("/tmp/dummy")))
|
||||
}
|
||||
|
||||
async fn move_to_trash(&self, _file_id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn restore_from_trash(&self, _file_id: &str, _original_path: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_file_permanently(&self, _file_id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FolderStoragePort
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubFolderStoragePort;
|
||||
|
||||
#[async_trait]
|
||||
impl FolderRepository for StubFolderStoragePort {
|
||||
async fn create_folder(
|
||||
&self,
|
||||
_name: String,
|
||||
_parent_id: Option<String>,
|
||||
) -> Result<Folder, DomainError> {
|
||||
Ok(Folder::default())
|
||||
}
|
||||
|
||||
async fn get_folder(&self, _id: &str) -> Result<Folder, DomainError> {
|
||||
Ok(Folder::default())
|
||||
}
|
||||
|
||||
async fn get_folder_by_path(
|
||||
&self,
|
||||
_storage_path: &StoragePath,
|
||||
) -> Result<Folder, DomainError> {
|
||||
Ok(Folder::default())
|
||||
}
|
||||
|
||||
async fn list_folders(
|
||||
&self,
|
||||
_parent_id: Option<&str>,
|
||||
) -> Result<Vec<Folder>, DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn list_folders_paginated(
|
||||
&self,
|
||||
_parent_id: Option<&str>,
|
||||
_offset: usize,
|
||||
_limit: usize,
|
||||
_include_total: bool,
|
||||
) -> Result<(Vec<Folder>, Option<usize>), DomainError> {
|
||||
Ok((Vec::new(), Some(0)))
|
||||
}
|
||||
|
||||
async fn rename_folder(
|
||||
&self,
|
||||
_id: &str,
|
||||
_new_name: String,
|
||||
) -> Result<Folder, DomainError> {
|
||||
Ok(Folder::default())
|
||||
}
|
||||
|
||||
async fn move_folder(
|
||||
&self,
|
||||
_id: &str,
|
||||
_new_parent_id: Option<&str>,
|
||||
) -> Result<Folder, DomainError> {
|
||||
Ok(Folder::default())
|
||||
}
|
||||
|
||||
async fn delete_folder(&self, _id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn folder_exists(
|
||||
&self,
|
||||
_storage_path: &StoragePath,
|
||||
) -> Result<bool, DomainError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn get_folder_path(
|
||||
&self,
|
||||
_id: &str,
|
||||
) -> Result<StoragePath, DomainError> {
|
||||
Ok(StoragePath::from_string("/"))
|
||||
}
|
||||
|
||||
async fn move_to_trash(&self, _folder_id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn restore_from_trash(&self, _folder_id: &str, _original_path: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_folder_permanently(&self, _folder_id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// I18nService
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubI18nService;
|
||||
|
||||
#[async_trait]
|
||||
impl I18nService for StubI18nService {
|
||||
async fn translate(&self, _key: &str, _locale: Locale) -> I18nResult<String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
async fn load_translations(&self, _locale: Locale) -> I18nResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn available_locales(&self) -> Vec<Locale> {
|
||||
vec![Locale::default()]
|
||||
}
|
||||
|
||||
async fn is_supported(&self, _locale: Locale) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FolderUseCase
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubFolderUseCase;
|
||||
|
||||
#[async_trait]
|
||||
impl FolderUseCase for StubFolderUseCase {
|
||||
async fn create_folder(
|
||||
&self,
|
||||
_dto: CreateFolderDto,
|
||||
) -> Result<FolderDto, DomainError> {
|
||||
Ok(FolderDto::default())
|
||||
}
|
||||
|
||||
async fn get_folder(&self, _id: &str) -> Result<FolderDto, DomainError> {
|
||||
Ok(FolderDto::default())
|
||||
}
|
||||
|
||||
async fn get_folder_by_path(
|
||||
&self,
|
||||
_path: &str,
|
||||
) -> Result<FolderDto, DomainError> {
|
||||
Ok(FolderDto::default())
|
||||
}
|
||||
|
||||
async fn list_folders(
|
||||
&self,
|
||||
_parent_id: Option<&str>,
|
||||
) -> Result<Vec<FolderDto>, DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn list_folders_paginated(
|
||||
&self,
|
||||
_parent_id: Option<&str>,
|
||||
_pagination: &PaginationRequestDto,
|
||||
) -> Result<PaginatedResponseDto<FolderDto>, DomainError> {
|
||||
Ok(PaginatedResponseDto::new(Vec::new(), 0, 10, 0))
|
||||
}
|
||||
|
||||
async fn rename_folder(
|
||||
&self,
|
||||
_id: &str,
|
||||
_dto: RenameFolderDto,
|
||||
) -> Result<FolderDto, DomainError> {
|
||||
Ok(FolderDto::default())
|
||||
}
|
||||
|
||||
async fn move_folder(
|
||||
&self,
|
||||
_id: &str,
|
||||
_dto: MoveFolderDto,
|
||||
) -> Result<FolderDto, DomainError> {
|
||||
Ok(FolderDto::default())
|
||||
}
|
||||
|
||||
async fn delete_folder(&self, _id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FileUploadUseCase
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubFileUploadUseCase;
|
||||
|
||||
#[async_trait]
|
||||
impl FileUploadUseCase for StubFileUploadUseCase {
|
||||
async fn upload_file(
|
||||
&self,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_content: Vec<u8>,
|
||||
) -> Result<FileDto, DomainError> {
|
||||
Ok(FileDto::default())
|
||||
}
|
||||
|
||||
async fn smart_upload(
|
||||
&self,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_chunks: Vec<Bytes>,
|
||||
_total_size: usize,
|
||||
) -> Result<(FileDto, UploadStrategy), DomainError> {
|
||||
Ok((FileDto::default(), UploadStrategy::Buffered))
|
||||
}
|
||||
|
||||
async fn create_file(&self, _parent_path: &str, _filename: &str, _content: &[u8], _content_type: &str) -> Result<FileDto, DomainError> {
|
||||
Ok(FileDto::default())
|
||||
}
|
||||
|
||||
async fn update_file(&self, _path: &str, _content: &[u8]) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FileRetrievalUseCase
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubFileRetrievalUseCase;
|
||||
|
||||
#[async_trait]
|
||||
impl FileRetrievalUseCase for StubFileRetrievalUseCase {
|
||||
async fn get_file(&self, _id: &str) -> Result<FileDto, DomainError> {
|
||||
Ok(FileDto::default())
|
||||
}
|
||||
|
||||
async fn list_files(
|
||||
&self,
|
||||
_folder_id: Option<&str>,
|
||||
) -> Result<Vec<FileDto>, DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_content(&self, _id: &str) -> Result<Vec<u8>, DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_stream(
|
||||
&self,
|
||||
_id: &str,
|
||||
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError> {
|
||||
let empty_stream = futures::stream::empty::<Result<Bytes, std::io::Error>>();
|
||||
Ok(Box::new(empty_stream))
|
||||
}
|
||||
|
||||
async fn get_file_optimized(
|
||||
&self,
|
||||
_id: &str,
|
||||
_accept_webp: bool,
|
||||
_prefer_original: bool,
|
||||
) -> Result<(FileDto, OptimizedFileContent), DomainError> {
|
||||
Ok((FileDto::default(), OptimizedFileContent::Bytes {
|
||||
data: Bytes::new(),
|
||||
mime_type: String::new(),
|
||||
was_transcoded: false,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn get_file_range_stream(
|
||||
&self,
|
||||
_id: &str,
|
||||
_start: u64,
|
||||
_end: Option<u64>,
|
||||
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError> {
|
||||
let empty_stream = futures::stream::empty::<Result<Bytes, std::io::Error>>();
|
||||
Ok(Box::new(empty_stream))
|
||||
}
|
||||
|
||||
async fn get_file_by_path(&self, _path: &str) -> Result<FileDto, DomainError> {
|
||||
Err(DomainError::not_found("File", "stub"))
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FileManagementUseCase
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubFileManagementUseCase;
|
||||
|
||||
#[async_trait]
|
||||
impl FileManagementUseCase for StubFileManagementUseCase {
|
||||
async fn move_file(
|
||||
&self,
|
||||
_file_id: &str,
|
||||
_folder_id: Option<String>,
|
||||
) -> Result<FileDto, DomainError> {
|
||||
Ok(FileDto::default())
|
||||
}
|
||||
|
||||
async fn delete_file(&self, _id: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_with_cleanup(
|
||||
&self,
|
||||
_id: &str,
|
||||
_user_id: &str,
|
||||
) -> Result<bool, DomainError> {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubSearchUseCase;
|
||||
|
||||
#[async_trait]
|
||||
impl SearchUseCase for StubSearchUseCase {
|
||||
async fn search(
|
||||
&self,
|
||||
_criteria: SearchCriteriaDto,
|
||||
) -> Result<SearchResultsDto, DomainError> {
|
||||
Ok(SearchResultsDto::empty())
|
||||
}
|
||||
|
||||
async fn clear_search_cache(&self) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MetadataCachePort
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
use crate::application::ports::cache_ports::{MetadataCachePort, CachedMetadataDto, ContentCachePort};
|
||||
|
||||
pub struct StubMetadataCachePort;
|
||||
|
||||
#[async_trait]
|
||||
impl MetadataCachePort for StubMetadataCachePort {
|
||||
async fn get_metadata(&self, _path: &Path) -> Option<CachedMetadataDto> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn is_file(&self, _path: &Path) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn refresh_metadata(&self, path: &Path) -> Result<CachedMetadataDto, DomainError> {
|
||||
Ok(CachedMetadataDto {
|
||||
path: path.to_path_buf(),
|
||||
exists: false,
|
||||
is_file: false,
|
||||
size: None,
|
||||
mime_type: None,
|
||||
created_at: None,
|
||||
modified_at: None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn invalidate(&self, _path: &Path) {}
|
||||
|
||||
async fn invalidate_directory(&self, _dir_path: &Path) {}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ContentCachePort
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub struct StubContentCachePort;
|
||||
|
||||
#[async_trait]
|
||||
impl ContentCachePort for StubContentCachePort {
|
||||
fn should_cache(&self, _size: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn get(&self, _file_id: &str) -> Option<(Bytes, String, String)> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn put(&self, _file_id: String, _content: Bytes, _etag: String, _content_type: String) {}
|
||||
|
||||
async fn invalidate(&self, _file_id: &str) {}
|
||||
|
||||
async fn clear(&self) {}
|
||||
}
|
||||
Reference in New Issue
Block a user