adding user authentication
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
use std::sync::Arc;
|
||||
use anyhow::Result;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::domain::services::auth_service::AuthService;
|
||||
use crate::application::services::auth_application_service::AuthApplicationService;
|
||||
use crate::infrastructure::repositories::{UserPgRepository, SessionPgRepository};
|
||||
use crate::common::config::AppConfig;
|
||||
use crate::common::di::AuthServices;
|
||||
|
||||
pub async fn create_auth_services(config: &AppConfig, pool: Arc<PgPool>) -> Result<AuthServices> {
|
||||
// Crear servicio de dominio de autenticación
|
||||
let auth_service = Arc::new(AuthService::new(
|
||||
config.auth.jwt_secret.clone(),
|
||||
config.auth.access_token_expiry_secs,
|
||||
config.auth.refresh_token_expiry_secs,
|
||||
));
|
||||
|
||||
// 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 auth_application_service = Arc::new(AuthApplicationService::new(
|
||||
user_repository,
|
||||
session_repository,
|
||||
auth_service.clone(),
|
||||
));
|
||||
|
||||
Ok(AuthServices {
|
||||
auth_service,
|
||||
auth_application_service,
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::time::Duration;
|
||||
use std::path::PathBuf;
|
||||
use std::env;
|
||||
|
||||
/// Configuración de caché
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -52,6 +54,21 @@ impl TimeoutConfig {
|
||||
Duration::from_millis(self.file_operation_ms)
|
||||
}
|
||||
|
||||
/// Obtiene un Duration para operaciones de escritura de archivo
|
||||
pub fn file_write_timeout(&self) -> Duration {
|
||||
Duration::from_millis(self.file_operation_ms)
|
||||
}
|
||||
|
||||
/// Obtiene un Duration para operaciones de lectura de archivo
|
||||
pub fn file_read_timeout(&self) -> Duration {
|
||||
Duration::from_millis(self.file_operation_ms)
|
||||
}
|
||||
|
||||
/// Obtiene un Duration para operaciones de eliminación de archivo
|
||||
pub fn file_delete_timeout(&self) -> Duration {
|
||||
Duration::from_millis(self.file_operation_ms)
|
||||
}
|
||||
|
||||
/// Obtiene un Duration para operaciones de directorio
|
||||
pub fn dir_timeout(&self) -> Duration {
|
||||
Duration::from_millis(self.dir_operation_ms)
|
||||
@@ -178,9 +195,81 @@ impl Default for ConcurrencyConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuración de base de datos
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DatabaseConfig {
|
||||
pub connection_string: String,
|
||||
pub max_connections: u32,
|
||||
pub min_connections: u32,
|
||||
pub connect_timeout_secs: u64,
|
||||
pub idle_timeout_secs: u64,
|
||||
pub max_lifetime_secs: u64,
|
||||
}
|
||||
|
||||
impl Default for DatabaseConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
connection_string: "postgres://postgres:postgres@localhost/oxicloud".to_string(),
|
||||
max_connections: 20,
|
||||
min_connections: 5,
|
||||
connect_timeout_secs: 10,
|
||||
idle_timeout_secs: 300,
|
||||
max_lifetime_secs: 1800,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuración de autenticación
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AuthConfig {
|
||||
pub jwt_secret: String,
|
||||
pub access_token_expiry_secs: i64,
|
||||
pub refresh_token_expiry_secs: i64,
|
||||
pub hash_memory_cost: u32,
|
||||
pub hash_time_cost: u32,
|
||||
}
|
||||
|
||||
impl Default for AuthConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
jwt_secret: "ox1cl0ud-sup3r-s3cr3t-k3y-f0r-t0k3n-s1gn1ng".to_string(),
|
||||
access_token_expiry_secs: 3600, // 1 hora
|
||||
refresh_token_expiry_secs: 2592000, // 30 días
|
||||
hash_memory_cost: 65536, // 64MB
|
||||
hash_time_cost: 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuración de funcionalidades (feature flags)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FeaturesConfig {
|
||||
pub enable_auth: bool,
|
||||
pub enable_user_storage_quotas: bool,
|
||||
pub enable_file_sharing: bool,
|
||||
}
|
||||
|
||||
impl Default for FeaturesConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enable_auth: false,
|
||||
enable_user_storage_quotas: false,
|
||||
enable_file_sharing: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuración global de la aplicación
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AppConfig {
|
||||
/// Ruta del directorio de almacenamiento
|
||||
pub storage_path: PathBuf,
|
||||
/// Ruta del directorio de archivos estáticos
|
||||
pub static_path: PathBuf,
|
||||
/// Puerto del servidor
|
||||
pub server_port: u16,
|
||||
/// Host del servidor
|
||||
pub server_host: String,
|
||||
/// Configuración de caché
|
||||
pub cache: CacheConfig,
|
||||
/// Configuración de timeouts
|
||||
@@ -189,19 +278,125 @@ pub struct AppConfig {
|
||||
pub resources: ResourceConfig,
|
||||
/// Configuración de concurrencia
|
||||
pub concurrency: ConcurrencyConfig,
|
||||
/// Configuración de base de datos
|
||||
pub database: DatabaseConfig,
|
||||
/// Configuración de autenticación
|
||||
pub auth: AuthConfig,
|
||||
/// Configuración de funcionalidades
|
||||
pub features: FeaturesConfig,
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
storage_path: PathBuf::from("./storage"),
|
||||
static_path: PathBuf::from("./static"),
|
||||
server_port: 8085,
|
||||
server_host: "127.0.0.1".to_string(),
|
||||
cache: CacheConfig::default(),
|
||||
timeouts: TimeoutConfig::default(),
|
||||
resources: ResourceConfig::default(),
|
||||
concurrency: ConcurrencyConfig::default(),
|
||||
database: DatabaseConfig::default(),
|
||||
auth: AuthConfig::default(),
|
||||
features: FeaturesConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
pub fn from_env() -> Self {
|
||||
let mut config = Self::default();
|
||||
|
||||
// Usar variables de entorno para sobrescribir valores por defecto
|
||||
if let Ok(storage_path) = env::var("OXICLOUD_STORAGE_PATH") {
|
||||
config.storage_path = PathBuf::from(storage_path);
|
||||
}
|
||||
|
||||
if let Ok(static_path) = env::var("OXICLOUD_STATIC_PATH") {
|
||||
config.static_path = PathBuf::from(static_path);
|
||||
}
|
||||
|
||||
if let Ok(server_port) = env::var("OXICLOUD_SERVER_PORT") {
|
||||
if let Ok(port) = server_port.parse::<u16>() {
|
||||
config.server_port = port;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(server_host) = env::var("OXICLOUD_SERVER_HOST") {
|
||||
config.server_host = server_host;
|
||||
}
|
||||
|
||||
// Configuración de Database
|
||||
if let Ok(connection_string) = env::var("OXICLOUD_DB_CONNECTION_STRING") {
|
||||
config.database.connection_string = connection_string;
|
||||
}
|
||||
|
||||
if let Ok(max_connections) = env::var("OXICLOUD_DB_MAX_CONNECTIONS")
|
||||
.map(|v| v.parse::<u32>()) {
|
||||
if let Ok(val) = max_connections {
|
||||
config.database.max_connections = val;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(min_connections) = env::var("OXICLOUD_DB_MIN_CONNECTIONS")
|
||||
.map(|v| v.parse::<u32>()) {
|
||||
if let Ok(val) = min_connections {
|
||||
config.database.min_connections = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Configuración Auth
|
||||
if let Ok(jwt_secret) = env::var("OXICLOUD_JWT_SECRET") {
|
||||
config.auth.jwt_secret = jwt_secret;
|
||||
}
|
||||
|
||||
if let Ok(access_token_expiry) = env::var("OXICLOUD_ACCESS_TOKEN_EXPIRY_SECS")
|
||||
.map(|v| v.parse::<i64>()) {
|
||||
if let Ok(val) = access_token_expiry {
|
||||
config.auth.access_token_expiry_secs = val;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(refresh_token_expiry) = env::var("OXICLOUD_REFRESH_TOKEN_EXPIRY_SECS")
|
||||
.map(|v| v.parse::<i64>()) {
|
||||
if let Ok(val) = refresh_token_expiry {
|
||||
config.auth.refresh_token_expiry_secs = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Feature flags
|
||||
if let Ok(enable_auth) = env::var("OXICLOUD_ENABLE_AUTH")
|
||||
.map(|v| v.parse::<bool>()) {
|
||||
if let Ok(val) = enable_auth {
|
||||
config.features.enable_auth = val;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(enable_user_storage_quotas) = env::var("OXICLOUD_ENABLE_USER_STORAGE_QUOTAS")
|
||||
.map(|v| v.parse::<bool>()) {
|
||||
if let Ok(val) = enable_user_storage_quotas {
|
||||
config.features.enable_user_storage_quotas = val;
|
||||
}
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
pub fn with_features(mut self, features: FeaturesConfig) -> Self {
|
||||
self.features = features;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn db_enabled(&self) -> bool {
|
||||
self.features.enable_auth
|
||||
}
|
||||
|
||||
pub fn auth_enabled(&self) -> bool {
|
||||
self.features.enable_auth
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtenemos una configuración global por defecto
|
||||
#[allow(dead_code)]
|
||||
pub fn default_config() -> AppConfig {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
use sqlx::{postgres::PgPoolOptions, PgPool};
|
||||
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...");
|
||||
|
||||
// Crear el pool de conexiones con las opciones de configuración
|
||||
let pool = 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?;
|
||||
|
||||
// Verificar la conexión
|
||||
sqlx::query("SELECT 1").execute(&pool).await?;
|
||||
|
||||
tracing::info!("Conexión a PostgreSQL establecida correctamente");
|
||||
Ok(pool)
|
||||
}
|
||||
+517
-1
@@ -1,6 +1,10 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::domain::services::auth_service::AuthService;
|
||||
use crate::application::services::auth_application_service::AuthApplicationService;
|
||||
|
||||
use crate::domain::services::path_service::PathService;
|
||||
use crate::infrastructure::repositories::folder_fs_repository::FolderFsRepository;
|
||||
@@ -224,7 +228,7 @@ impl AppServiceFactory {
|
||||
pub struct CoreServices {
|
||||
pub path_service: Arc<PathService>,
|
||||
pub cache_manager: Arc<StorageCacheManager>,
|
||||
pub id_mapping_service: Arc<IdMappingService>,
|
||||
pub id_mapping_service: Arc<dyn crate::application::ports::outbound::IdMappingPort>,
|
||||
pub config: AppConfig,
|
||||
}
|
||||
|
||||
@@ -251,4 +255,516 @@ pub struct ApplicationServices {
|
||||
pub file_management_service: Arc<dyn FileManagementUseCase>,
|
||||
pub file_use_case_factory: Arc<dyn FileUseCaseFactory>,
|
||||
pub i18n_service: Arc<I18nApplicationService>,
|
||||
}
|
||||
|
||||
/// Contenedor para servicios de autenticación
|
||||
#[allow(dead_code)]
|
||||
pub struct AuthServices {
|
||||
pub auth_service: Arc<AuthService>,
|
||||
pub auth_application_service: Arc<AuthApplicationService>,
|
||||
}
|
||||
|
||||
/// Estado global de la aplicación para dependency injection
|
||||
pub struct AppState {
|
||||
pub core: CoreServices,
|
||||
pub repositories: RepositoryServices,
|
||||
pub applications: ApplicationServices,
|
||||
pub db_pool: Option<Arc<PgPool>>,
|
||||
pub auth_service: Option<AuthServices>,
|
||||
}
|
||||
|
||||
impl Default for AppState {
|
||||
fn default() -> Self {
|
||||
// This is just a minimal stub version for auth middleware
|
||||
// We'll need to create proper instance in main.rs
|
||||
|
||||
let config = crate::common::config::AppConfig::default();
|
||||
let path_service = Arc::new(
|
||||
crate::domain::services::path_service::PathService::new(
|
||||
std::path::PathBuf::from("./storage")
|
||||
)
|
||||
);
|
||||
|
||||
// Create stub service implementations
|
||||
struct DummyIdMappingService;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::outbound::IdMappingPort for DummyIdMappingService {
|
||||
async fn get_or_create_id(&self, _path: &crate::domain::services::path_service::StoragePath) -> Result<String, crate::common::errors::DomainError> {
|
||||
Ok("dummy-id".to_string())
|
||||
}
|
||||
|
||||
async fn get_path_by_id(&self, _id: &str) -> Result<crate::domain::services::path_service::StoragePath, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::services::path_service::StoragePath::from_string("/"))
|
||||
}
|
||||
|
||||
async fn update_path(&self, _id: &str, _new_path: &crate::domain::services::path_service::StoragePath) -> Result<(), crate::common::errors::DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_id(&self, _id: &str) -> Result<(), crate::common::errors::DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn save_changes(&self) -> Result<(), crate::common::errors::DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyStorageMediator;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::services::storage_mediator::StorageMediator for DummyStorageMediator {
|
||||
async fn get_folder_path(&self, _folder_id: &str) -> Result<std::path::PathBuf, crate::application::services::storage_mediator::StorageMediatorError> {
|
||||
Ok(std::path::PathBuf::from("/tmp"))
|
||||
}
|
||||
|
||||
async fn get_folder_storage_path(&self, _folder_id: &str) -> Result<crate::domain::services::path_service::StoragePath, crate::application::services::storage_mediator::StorageMediatorError> {
|
||||
Ok(crate::domain::services::path_service::StoragePath::root())
|
||||
}
|
||||
|
||||
async fn get_folder(&self, _folder_id: &str) -> Result<crate::domain::entities::folder::Folder, crate::application::services::storage_mediator::StorageMediatorError> {
|
||||
Err(crate::application::services::storage_mediator::StorageMediatorError::NotFound("Stub not implemented".to_string()))
|
||||
}
|
||||
|
||||
async fn file_exists_at_path(&self, _path: &std::path::Path) -> Result<bool, crate::application::services::storage_mediator::StorageMediatorError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn file_exists_at_storage_path(&self, _storage_path: &crate::domain::services::path_service::StoragePath) -> Result<bool, crate::application::services::storage_mediator::StorageMediatorError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn folder_exists_at_path(&self, _path: &std::path::Path) -> Result<bool, crate::application::services::storage_mediator::StorageMediatorError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn folder_exists_at_storage_path(&self, _storage_path: &crate::domain::services::path_service::StoragePath) -> Result<bool, crate::application::services::storage_mediator::StorageMediatorError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn resolve_path(&self, _relative_path: &std::path::Path) -> std::path::PathBuf {
|
||||
std::path::PathBuf::from("/tmp")
|
||||
}
|
||||
|
||||
fn resolve_storage_path(&self, _storage_path: &crate::domain::services::path_service::StoragePath) -> std::path::PathBuf {
|
||||
std::path::PathBuf::from("/tmp")
|
||||
}
|
||||
|
||||
async fn ensure_directory(&self, _path: &std::path::Path) -> Result<(), crate::application::services::storage_mediator::StorageMediatorError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn ensure_storage_directory(&self, _storage_path: &crate::domain::services::path_service::StoragePath) -> Result<(), crate::application::services::storage_mediator::StorageMediatorError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFileReadPort;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::storage_ports::FileReadPort for DummyFileReadPort {
|
||||
async fn get_file(&self, _id: &str) -> Result<crate::domain::entities::file::File, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::file::File::default())
|
||||
}
|
||||
|
||||
async fn list_files(&self, _folder_id: Option<&str>) -> Result<Vec<crate::domain::entities::file::File>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_content(&self, _id: &str) -> Result<Vec<u8>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_stream(&self, _id: &str) -> Result<Box<dyn futures::Stream<Item = Result<bytes::Bytes, std::io::Error>> + Send>, crate::common::errors::DomainError> {
|
||||
let empty_stream = futures::stream::empty::<Result<bytes::Bytes, std::io::Error>>();
|
||||
Ok(Box::new(empty_stream))
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFileWritePort;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::storage_ports::FileWritePort for DummyFileWritePort {
|
||||
async fn save_file(
|
||||
&self,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_content: Vec<u8>,
|
||||
) -> Result<crate::domain::entities::file::File, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::file::File::default())
|
||||
}
|
||||
|
||||
async fn move_file(&self, _file_id: &str, _target_folder_id: Option<String>) -> Result<crate::domain::entities::file::File, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::file::File::default())
|
||||
}
|
||||
|
||||
async fn delete_file(&self, _id: &str) -> Result<(), crate::common::errors::DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFileStoragePort;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::outbound::FileStoragePort for DummyFileStoragePort {
|
||||
async fn save_file(
|
||||
&self,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_content: Vec<u8>,
|
||||
) -> Result<crate::domain::entities::file::File, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::file::File::default())
|
||||
}
|
||||
|
||||
async fn get_file(&self, _id: &str) -> Result<crate::domain::entities::file::File, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::file::File::default())
|
||||
}
|
||||
|
||||
async fn list_files(&self, _folder_id: Option<&str>) -> Result<Vec<crate::domain::entities::file::File>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn delete_file(&self, _id: &str) -> Result<(), crate::common::errors::DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_file_content(&self, _id: &str) -> Result<Vec<u8>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_stream(&self, _id: &str) -> Result<Box<dyn futures::Stream<Item = Result<bytes::Bytes, std::io::Error>> + Send>, crate::common::errors::DomainError> {
|
||||
let empty_stream = futures::stream::empty::<Result<bytes::Bytes, std::io::Error>>();
|
||||
Ok(Box::new(empty_stream))
|
||||
}
|
||||
|
||||
async fn move_file(&self, _file_id: &str, _target_folder_id: Option<String>) -> Result<crate::domain::entities::file::File, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::file::File::default())
|
||||
}
|
||||
|
||||
async fn get_file_path(&self, _id: &str) -> Result<crate::domain::services::path_service::StoragePath, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::services::path_service::StoragePath::from_string("/"))
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFolderStoragePort;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::outbound::FolderStoragePort for DummyFolderStoragePort {
|
||||
async fn create_folder(&self, _name: String, _parent_id: Option<String>) -> Result<crate::domain::entities::folder::Folder, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::folder::Folder::default())
|
||||
}
|
||||
|
||||
async fn get_folder(&self, _id: &str) -> Result<crate::domain::entities::folder::Folder, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::folder::Folder::default())
|
||||
}
|
||||
|
||||
async fn get_folder_by_path(&self, _storage_path: &crate::domain::services::path_service::StoragePath) -> Result<crate::domain::entities::folder::Folder, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::folder::Folder::default())
|
||||
}
|
||||
|
||||
async fn list_folders(&self, _parent_id: Option<&str>) -> Result<Vec<crate::domain::entities::folder::Folder>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn list_folders_paginated(
|
||||
&self,
|
||||
_parent_id: Option<&str>,
|
||||
_offset: usize,
|
||||
_limit: usize,
|
||||
_include_total: bool
|
||||
) -> Result<(Vec<crate::domain::entities::folder::Folder>, Option<usize>), crate::common::errors::DomainError> {
|
||||
Ok((Vec::new(), Some(0)))
|
||||
}
|
||||
|
||||
async fn rename_folder(&self, _id: &str, _new_name: String) -> Result<crate::domain::entities::folder::Folder, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::folder::Folder::default())
|
||||
}
|
||||
|
||||
async fn move_folder(&self, _id: &str, _new_parent_id: Option<&str>) -> Result<crate::domain::entities::folder::Folder, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::entities::folder::Folder::default())
|
||||
}
|
||||
|
||||
async fn delete_folder(&self, _id: &str) -> Result<(), crate::common::errors::DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn folder_exists(&self, _storage_path: &crate::domain::services::path_service::StoragePath) -> Result<bool, crate::common::errors::DomainError> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn get_folder_path(&self, _id: &str) -> Result<crate::domain::services::path_service::StoragePath, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::services::path_service::StoragePath::from_string("/"))
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFilePathResolutionPort;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::storage_ports::FilePathResolutionPort for DummyFilePathResolutionPort {
|
||||
async fn get_file_path(&self, _id: &str) -> Result<crate::domain::services::path_service::StoragePath, crate::common::errors::DomainError> {
|
||||
Ok(crate::domain::services::path_service::StoragePath::from_string("/"))
|
||||
}
|
||||
|
||||
fn resolve_path(&self, _storage_path: &crate::domain::services::path_service::StoragePath) -> std::path::PathBuf {
|
||||
std::path::PathBuf::from("/")
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyI18nService;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::domain::services::i18n_service::I18nService for DummyI18nService {
|
||||
async fn translate(&self, _key: &str, _locale: crate::domain::services::i18n_service::Locale) -> crate::domain::services::i18n_service::I18nResult<String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
async fn load_translations(&self, _locale: crate::domain::services::i18n_service::Locale) -> crate::domain::services::i18n_service::I18nResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn available_locales(&self) -> Vec<crate::domain::services::i18n_service::Locale> {
|
||||
vec![crate::domain::services::i18n_service::Locale::default()]
|
||||
}
|
||||
|
||||
async fn is_supported(&self, _locale: crate::domain::services::i18n_service::Locale) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFolderUseCase;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::inbound::FolderUseCase for DummyFolderUseCase {
|
||||
async fn create_folder(&self, _dto: crate::application::dtos::folder_dto::CreateFolderDto) -> Result<crate::application::dtos::folder_dto::FolderDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::folder_dto::FolderDto::default())
|
||||
}
|
||||
|
||||
async fn get_folder(&self, _id: &str) -> Result<crate::application::dtos::folder_dto::FolderDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::folder_dto::FolderDto::default())
|
||||
}
|
||||
|
||||
async fn get_folder_by_path(&self, _path: &str) -> Result<crate::application::dtos::folder_dto::FolderDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::folder_dto::FolderDto::default())
|
||||
}
|
||||
|
||||
async fn list_folders(&self, _parent_id: Option<&str>) -> Result<Vec<crate::application::dtos::folder_dto::FolderDto>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn list_folders_paginated(
|
||||
&self,
|
||||
_parent_id: Option<&str>,
|
||||
_pagination: &crate::application::dtos::pagination::PaginationRequestDto
|
||||
) -> Result<crate::application::dtos::pagination::PaginatedResponseDto<crate::application::dtos::folder_dto::FolderDto>, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::pagination::PaginatedResponseDto::new(
|
||||
Vec::new(),
|
||||
0,
|
||||
10,
|
||||
0
|
||||
))
|
||||
}
|
||||
|
||||
async fn rename_folder(&self, _id: &str, _dto: crate::application::dtos::folder_dto::RenameFolderDto) -> Result<crate::application::dtos::folder_dto::FolderDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::folder_dto::FolderDto::default())
|
||||
}
|
||||
|
||||
async fn move_folder(&self, _id: &str, _dto: crate::application::dtos::folder_dto::MoveFolderDto) -> Result<crate::application::dtos::folder_dto::FolderDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::folder_dto::FolderDto::default())
|
||||
}
|
||||
|
||||
async fn delete_folder(&self, _id: &str) -> Result<(), crate::common::errors::DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFileUseCase;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::inbound::FileUseCase for DummyFileUseCase {
|
||||
async fn upload_file(
|
||||
&self,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_content: Vec<u8>,
|
||||
) -> Result<crate::application::dtos::file_dto::FileDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::file_dto::FileDto::default())
|
||||
}
|
||||
|
||||
async fn get_file(&self, _id: &str) -> Result<crate::application::dtos::file_dto::FileDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::file_dto::FileDto::default())
|
||||
}
|
||||
|
||||
async fn list_files(&self, _folder_id: Option<&str>) -> Result<Vec<crate::application::dtos::file_dto::FileDto>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn delete_file(&self, _id: &str) -> Result<(), crate::common::errors::DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_file_content(&self, _id: &str) -> Result<Vec<u8>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_stream(&self, _id: &str) -> Result<Box<dyn futures::Stream<Item = Result<bytes::Bytes, std::io::Error>> + Send>, crate::common::errors::DomainError> {
|
||||
// Create an empty stream
|
||||
let empty_stream = futures::stream::empty::<Result<bytes::Bytes, std::io::Error>>();
|
||||
Ok(Box::new(empty_stream))
|
||||
}
|
||||
|
||||
async fn move_file(&self, _file_id: &str, _folder_id: Option<String>) -> Result<crate::application::dtos::file_dto::FileDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::file_dto::FileDto::default())
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFileUploadUseCase;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::file_ports::FileUploadUseCase for DummyFileUploadUseCase {
|
||||
async fn upload_file(
|
||||
&self,
|
||||
_name: String,
|
||||
_folder_id: Option<String>,
|
||||
_content_type: String,
|
||||
_content: Vec<u8>,
|
||||
) -> Result<crate::application::dtos::file_dto::FileDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::file_dto::FileDto::default())
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFileRetrievalUseCase;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::file_ports::FileRetrievalUseCase for DummyFileRetrievalUseCase {
|
||||
async fn get_file(&self, _id: &str) -> Result<crate::application::dtos::file_dto::FileDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::file_dto::FileDto::default())
|
||||
}
|
||||
|
||||
async fn list_files(&self, _folder_id: Option<&str>) -> Result<Vec<crate::application::dtos::file_dto::FileDto>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_content(&self, _id: &str) -> Result<Vec<u8>, crate::common::errors::DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn get_file_stream(&self, _id: &str) -> Result<Box<dyn futures::Stream<Item = Result<bytes::Bytes, std::io::Error>> + Send>, crate::common::errors::DomainError> {
|
||||
// Create an empty stream
|
||||
let empty_stream = futures::stream::empty::<Result<bytes::Bytes, std::io::Error>>();
|
||||
Ok(Box::new(empty_stream))
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFileManagementUseCase;
|
||||
#[async_trait::async_trait]
|
||||
impl crate::application::ports::file_ports::FileManagementUseCase for DummyFileManagementUseCase {
|
||||
async fn move_file(&self, _file_id: &str, _folder_id: Option<String>) -> Result<crate::application::dtos::file_dto::FileDto, crate::common::errors::DomainError> {
|
||||
Ok(crate::application::dtos::file_dto::FileDto::default())
|
||||
}
|
||||
|
||||
async fn delete_file(&self, _id: &str) -> Result<(), crate::common::errors::DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFileUseCaseFactory;
|
||||
impl crate::application::ports::file_ports::FileUseCaseFactory for DummyFileUseCaseFactory {
|
||||
fn create_file_upload_use_case(&self) -> std::sync::Arc<dyn crate::application::ports::file_ports::FileUploadUseCase> {
|
||||
std::sync::Arc::new(DummyFileUploadUseCase)
|
||||
}
|
||||
|
||||
fn create_file_retrieval_use_case(&self) -> std::sync::Arc<dyn crate::application::ports::file_ports::FileRetrievalUseCase> {
|
||||
std::sync::Arc::new(DummyFileRetrievalUseCase)
|
||||
}
|
||||
|
||||
fn create_file_management_use_case(&self) -> std::sync::Arc<dyn crate::application::ports::file_ports::FileManagementUseCase> {
|
||||
std::sync::Arc::new(DummyFileManagementUseCase)
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyI18nApplicationService {};
|
||||
|
||||
// Need to implement the actual service to match the type signature in DI container
|
||||
impl DummyI18nApplicationService {
|
||||
fn dummy() -> crate::application::services::i18n_application_service::I18nApplicationService {
|
||||
// We need to create an actual I18nApplicationService
|
||||
crate::application::services::i18n_application_service::I18nApplicationService::new(
|
||||
Arc::new(DummyI18nService) as Arc<dyn crate::domain::services::i18n_service::I18nService>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Create service instances
|
||||
let id_mapping_service = Arc::new(DummyIdMappingService) as Arc<dyn crate::application::ports::outbound::IdMappingPort>;
|
||||
let storage_mediator = Arc::new(DummyStorageMediator) as Arc<dyn crate::application::services::storage_mediator::StorageMediator>;
|
||||
let i18n_repository = Arc::new(DummyI18nService) as Arc<dyn crate::domain::services::i18n_service::I18nService>;
|
||||
let folder_service = Arc::new(DummyFolderUseCase) as Arc<dyn crate::application::ports::inbound::FolderUseCase>;
|
||||
let file_service = Arc::new(DummyFileUseCase) as Arc<dyn crate::application::ports::inbound::FileUseCase>;
|
||||
let file_upload_service = Arc::new(DummyFileUploadUseCase) as Arc<dyn crate::application::ports::file_ports::FileUploadUseCase>;
|
||||
let file_retrieval_service = Arc::new(DummyFileRetrievalUseCase) as Arc<dyn crate::application::ports::file_ports::FileRetrievalUseCase>;
|
||||
let file_management_service = Arc::new(DummyFileManagementUseCase) as Arc<dyn crate::application::ports::file_ports::FileManagementUseCase>;
|
||||
let file_use_case_factory = Arc::new(DummyFileUseCaseFactory) as Arc<dyn crate::application::ports::file_ports::FileUseCaseFactory>;
|
||||
|
||||
// This creates the core services needed for basic functionality
|
||||
let core_services = CoreServices {
|
||||
path_service: path_service.clone(),
|
||||
cache_manager: Arc::new(crate::infrastructure::services::cache_manager::StorageCacheManager::default()),
|
||||
id_mapping_service: id_mapping_service.clone(),
|
||||
config: config.clone(),
|
||||
};
|
||||
|
||||
// Create empty repository implementations
|
||||
let repository_services = RepositoryServices {
|
||||
folder_repository: Arc::new(DummyFolderStoragePort) as Arc<dyn crate::application::ports::outbound::FolderStoragePort>,
|
||||
file_repository: Arc::new(DummyFileStoragePort) as Arc<dyn crate::application::ports::outbound::FileStoragePort>,
|
||||
file_read_repository: Arc::new(DummyFileReadPort) as Arc<dyn crate::application::ports::storage_ports::FileReadPort>,
|
||||
file_write_repository: Arc::new(DummyFileWritePort) as Arc<dyn crate::application::ports::storage_ports::FileWritePort>,
|
||||
i18n_repository,
|
||||
storage_mediator: storage_mediator.clone(),
|
||||
metadata_manager: Arc::new(crate::infrastructure::repositories::FileMetadataManager::default()),
|
||||
path_resolver: Arc::new(crate::infrastructure::repositories::file_path_resolver::FilePathResolver::new(
|
||||
path_service.clone(),
|
||||
storage_mediator.clone(),
|
||||
id_mapping_service.clone()
|
||||
)),
|
||||
};
|
||||
|
||||
// Create application services
|
||||
let application_services = ApplicationServices {
|
||||
folder_service,
|
||||
file_service,
|
||||
file_upload_service,
|
||||
file_retrieval_service,
|
||||
file_management_service,
|
||||
file_use_case_factory,
|
||||
i18n_service: Arc::new(DummyI18nApplicationService::dummy()),
|
||||
};
|
||||
|
||||
// Return a minimal app state
|
||||
Self {
|
||||
core: core_services,
|
||||
repositories: repository_services,
|
||||
applications: application_services,
|
||||
db_pool: None,
|
||||
auth_service: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(
|
||||
core: CoreServices,
|
||||
repositories: RepositoryServices,
|
||||
applications: ApplicationServices,
|
||||
) -> Self {
|
||||
Self {
|
||||
core,
|
||||
repositories,
|
||||
applications,
|
||||
db_pool: None,
|
||||
auth_service: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_database(mut self, db_pool: Arc<PgPool>) -> Self {
|
||||
self.db_pool = Some(db_pool);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_auth_services(mut self, auth_services: AuthServices) -> Self {
|
||||
self.auth_service = Some(auth_services);
|
||||
self
|
||||
}
|
||||
}
|
||||
+81
-1
@@ -222,4 +222,84 @@ macro_rules! impl_from_error {
|
||||
|
||||
// Implementación para errores estándar comunes
|
||||
impl_from_error!(std::io::Error, "IO");
|
||||
impl_from_error!(serde_json::Error, "Serialization");
|
||||
impl_from_error!(serde_json::Error, "Serialization");
|
||||
|
||||
// Error para capas HTTP/API
|
||||
#[derive(Debug)]
|
||||
pub struct AppError {
|
||||
pub status_code: axum::http::StatusCode,
|
||||
pub message: String,
|
||||
pub error_type: String,
|
||||
}
|
||||
|
||||
// Estructura de respuesta de error
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct ErrorResponse {
|
||||
pub status: String,
|
||||
pub message: String,
|
||||
pub error_type: String,
|
||||
}
|
||||
|
||||
impl AppError {
|
||||
pub fn new(status_code: axum::http::StatusCode, message: impl Into<String>, error_type: impl Into<String>) -> Self {
|
||||
Self {
|
||||
status_code,
|
||||
message: message.into(),
|
||||
error_type: error_type.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bad_request(message: impl Into<String>) -> Self {
|
||||
Self::new(axum::http::StatusCode::BAD_REQUEST, message, "BadRequest")
|
||||
}
|
||||
|
||||
pub fn unauthorized(message: impl Into<String>) -> Self {
|
||||
Self::new(axum::http::StatusCode::UNAUTHORIZED, message, "Unauthorized")
|
||||
}
|
||||
|
||||
pub fn forbidden(message: impl Into<String>) -> Self {
|
||||
Self::new(axum::http::StatusCode::FORBIDDEN, message, "Forbidden")
|
||||
}
|
||||
|
||||
pub fn not_found(message: impl Into<String>) -> Self {
|
||||
Self::new(axum::http::StatusCode::NOT_FOUND, message, "NotFound")
|
||||
}
|
||||
|
||||
pub fn internal_error(message: impl Into<String>) -> Self {
|
||||
Self::new(axum::http::StatusCode::INTERNAL_SERVER_ERROR, message, "InternalError")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DomainError> for AppError {
|
||||
fn from(err: DomainError) -> Self {
|
||||
let status_code = match err.kind {
|
||||
ErrorKind::NotFound => axum::http::StatusCode::NOT_FOUND,
|
||||
ErrorKind::AlreadyExists => axum::http::StatusCode::CONFLICT,
|
||||
ErrorKind::InvalidInput => axum::http::StatusCode::BAD_REQUEST,
|
||||
ErrorKind::AccessDenied => axum::http::StatusCode::FORBIDDEN,
|
||||
ErrorKind::Timeout => axum::http::StatusCode::REQUEST_TIMEOUT,
|
||||
ErrorKind::InternalError => axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ErrorKind::NotImplemented => axum::http::StatusCode::NOT_IMPLEMENTED,
|
||||
};
|
||||
|
||||
Self {
|
||||
status_code,
|
||||
message: err.message,
|
||||
error_type: err.kind.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl axum::response::IntoResponse for AppError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
let status = self.status_code;
|
||||
let error_response = ErrorResponse {
|
||||
status: status.to_string(),
|
||||
message: self.message,
|
||||
error_type: self.error_type,
|
||||
};
|
||||
|
||||
let body = axum::Json(error_response);
|
||||
(status, body).into_response()
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
pub mod errors;
|
||||
pub mod config;
|
||||
pub mod cache;
|
||||
pub mod di;
|
||||
pub mod di;
|
||||
pub mod db;
|
||||
pub mod auth_factory;
|
||||
Reference in New Issue
Block a user