fix auth errors and add primigenial paper trash

This commit is contained in:
DioCrafts
2025-03-24 16:47:42 +01:00
parent 838ae6e0d4
commit 38b0e9594b
58 changed files with 3701 additions and 135 deletions
+30 -1
View File
@@ -195,6 +195,30 @@ impl Default for ConcurrencyConfig {
}
}
/// Configuración de almacenamiento
#[derive(Debug, Clone)]
pub struct StorageConfig {
/// Directorio raíz para el almacenamiento
pub root_dir: String,
/// Tamaño de chunk para procesamiento de archivos
pub chunk_size: usize,
/// Umbral para procesamiento paralelo
pub parallel_threshold: usize,
/// Días de retención para archivos en la papelera
pub trash_retention_days: u32,
}
impl Default for StorageConfig {
fn default() -> Self {
Self {
root_dir: "storage".to_string(),
chunk_size: 1024 * 1024, // 1 MB
parallel_threshold: 100 * 1024 * 1024, // 100 MB
trash_retention_days: 30, // 30 días
}
}
}
/// Configuración de base de datos
#[derive(Debug, Clone)]
pub struct DatabaseConfig {
@@ -210,7 +234,7 @@ impl Default for DatabaseConfig {
fn default() -> Self {
Self {
// Updated connection string with default credentials that PostgreSQL often uses
connection_string: "postgres://postgres:postgres@localhost:5432/postgres".to_string(),
connection_string: "postgres://postgres:postgres@localhost:5432/oxicloud".to_string(),
max_connections: 20,
min_connections: 5,
connect_timeout_secs: 10,
@@ -248,6 +272,7 @@ pub struct FeaturesConfig {
pub enable_auth: bool,
pub enable_user_storage_quotas: bool,
pub enable_file_sharing: bool,
pub enable_trash: bool,
}
impl Default for FeaturesConfig {
@@ -256,6 +281,7 @@ impl Default for FeaturesConfig {
enable_auth: true, // Enable authentication by default
enable_user_storage_quotas: false,
enable_file_sharing: false,
enable_trash: false, // Disable trash feature temporarily
}
}
}
@@ -279,6 +305,8 @@ pub struct AppConfig {
pub resources: ResourceConfig,
/// Configuración de concurrencia
pub concurrency: ConcurrencyConfig,
/// Configuración de almacenamiento
pub storage: StorageConfig,
/// Configuración de base de datos
pub database: DatabaseConfig,
/// Configuración de autenticación
@@ -298,6 +326,7 @@ impl Default for AppConfig {
timeouts: TimeoutConfig::default(),
resources: ResourceConfig::default(),
concurrency: ConcurrencyConfig::default(),
storage: StorageConfig::default(),
database: DatabaseConfig::default(),
auth: AuthConfig::default(),
features: FeaturesConfig::default(),
+41 -17
View File
@@ -38,28 +38,52 @@ pub async fn create_database_pool(config: &AppConfig) -> Result<PgPool> {
// Simple schema creation - this handles fresh installations
let create_tables_result = sqlx::query(r#"
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL,
-- Create the auth schema if not exists
CREATE SCHEMA IF NOT EXISTS auth;
-- Create UserRole enum type if not exists
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'userrole') THEN
CREATE TYPE auth.userrole AS ENUM ('admin', 'user');
END IF;
END $$;
-- Create the auth.users table
CREATE TABLE IF NOT EXISTS auth.users (
id VARCHAR(36) PRIMARY KEY,
username VARCHAR(32) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
quota_bytes BIGINT NOT NULL DEFAULT 1073741824,
last_login TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
role auth.userrole NOT NULL,
storage_quota_bytes BIGINT NOT NULL,
storage_used_bytes BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
last_login_at TIMESTAMPTZ,
active BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id),
refresh_token TEXT UNIQUE NOT NULL,
ip_address TEXT,
-- Create an index on username and email for fast lookups
CREATE INDEX IF NOT EXISTS idx_users_username ON auth.users(username);
CREATE INDEX IF NOT EXISTS idx_users_email ON auth.users(email);
-- Create the sessions table
CREATE TABLE IF NOT EXISTS auth.sessions (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
refresh_token VARCHAR(255) NOT NULL UNIQUE,
expires_at TIMESTAMPTZ NOT NULL,
ip_address VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL,
is_revoked BOOLEAN NOT NULL DEFAULT FALSE
created_at TIMESTAMPTZ NOT NULL,
revoked BOOLEAN NOT NULL DEFAULT FALSE
);
-- Create indexes on user_id and refresh_token for fast lookups
CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON auth.sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_sessions_refresh_token ON auth.sessions(refresh_token);
CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON auth.sessions(expires_at);
"#).execute(&pool).await;
match create_tables_result {
+36
View File
@@ -9,13 +9,17 @@ use crate::application::services::auth_application_service::AuthApplicationServi
use crate::domain::services::path_service::PathService;
use crate::infrastructure::repositories::folder_fs_repository::FolderFsRepository;
use crate::infrastructure::repositories::file_fs_repository::FileFsRepository;
use crate::infrastructure::repositories::trash_fs_repository::TrashFsRepository;
use crate::infrastructure::services::file_system_i18n_service::FileSystemI18nService;
use crate::infrastructure::services::id_mapping_service::IdMappingService;
use crate::infrastructure::services::cache_manager::StorageCacheManager;
use crate::infrastructure::services::file_metadata_cache::FileMetadataCache;
use crate::infrastructure::services::trash_cleanup_service::TrashCleanupService;
use crate::application::services::folder_service::FolderService;
use crate::application::services::file_service::FileService;
use crate::application::services::i18n_application_service::I18nApplicationService;
use crate::application::services::trash_service::TrashService;
use crate::application::ports::trash_ports::TrashUseCase;
use crate::application::services::storage_mediator::{StorageMediator, FileSystemStorageMediator};
use crate::application::ports::inbound::{FileUseCase, FolderUseCase, UseCaseFactory};
use crate::application::ports::outbound::{FileStoragePort, FolderStoragePort};
@@ -164,6 +168,16 @@ impl AppServiceFactory {
self.locales_path.clone()
));
// Trash repository
let trash_repository = if core.config.features.enable_trash {
Some(Arc::new(TrashFsRepository::new(
self.storage_path.as_path(),
core.id_mapping_service.clone(),
)) as Arc<dyn crate::domain::repositories::trash_repository::TrashRepository>)
} else {
None
};
RepositoryServices {
folder_repository,
file_repository,
@@ -173,6 +187,7 @@ impl AppServiceFactory {
storage_mediator,
metadata_manager,
path_resolver,
trash_repository,
}
}
@@ -211,6 +226,9 @@ impl AppServiceFactory {
repos.i18n_repository.clone()
));
// Servicio de papelera (deshabilitado temporalmente)
let trash_service = None; // La función de papelera está deshabilitada por defecto
ApplicationServices {
folder_service,
file_service,
@@ -219,12 +237,14 @@ impl AppServiceFactory {
file_management_service,
file_use_case_factory,
i18n_service,
trash_service,
}
}
}
/// Contenedor para servicios base
#[allow(dead_code)]
#[derive(Clone)]
pub struct CoreServices {
pub path_service: Arc<PathService>,
pub cache_manager: Arc<StorageCacheManager>,
@@ -234,6 +254,7 @@ pub struct CoreServices {
/// Contenedor para servicios de repositorio
#[allow(dead_code)]
#[derive(Clone)]
pub struct RepositoryServices {
pub folder_repository: Arc<dyn FolderStoragePort>,
pub file_repository: Arc<dyn FileStoragePort>,
@@ -243,10 +264,12 @@ pub struct RepositoryServices {
pub storage_mediator: Arc<dyn StorageMediator>,
pub metadata_manager: Arc<FileMetadataManager>,
pub path_resolver: Arc<FilePathResolver>,
pub trash_repository: Option<Arc<dyn crate::domain::repositories::trash_repository::TrashRepository>>,
}
/// Contenedor para servicios de aplicación
#[allow(dead_code)]
#[derive(Clone)]
pub struct ApplicationServices {
pub folder_service: Arc<dyn FolderUseCase>,
pub file_service: Arc<dyn FileUseCase>,
@@ -255,22 +278,26 @@ pub struct ApplicationServices {
pub file_management_service: Arc<dyn FileManagementUseCase>,
pub file_use_case_factory: Arc<dyn FileUseCaseFactory>,
pub i18n_service: Arc<I18nApplicationService>,
pub trash_service: Option<Arc<dyn TrashUseCase>>,
}
/// Contenedor para servicios de autenticación
#[allow(dead_code)]
#[derive(Clone)]
pub struct AuthServices {
pub auth_service: Arc<AuthService>,
pub auth_application_service: Arc<AuthApplicationService>,
}
/// Estado global de la aplicación para dependency injection
#[derive(Clone)]
pub struct AppState {
pub core: CoreServices,
pub repositories: RepositoryServices,
pub applications: ApplicationServices,
pub db_pool: Option<Arc<PgPool>>,
pub auth_service: Option<AuthServices>,
pub trash_service: Option<Arc<dyn TrashUseCase>>,
}
impl Default for AppState {
@@ -719,6 +746,7 @@ impl Default for AppState {
storage_mediator.clone(),
id_mapping_service.clone()
)),
trash_repository: None, // No trash repository in minimal mode
};
// Create application services
@@ -730,6 +758,7 @@ impl Default for AppState {
file_management_service,
file_use_case_factory,
i18n_service: Arc::new(DummyI18nApplicationService::dummy()),
trash_service: None, // No trash service in minimal mode
};
// Return a minimal app state
@@ -739,6 +768,7 @@ impl Default for AppState {
applications: application_services,
db_pool: None,
auth_service: None,
trash_service: None,
}
}
}
@@ -755,6 +785,7 @@ impl AppState {
applications,
db_pool: None,
auth_service: None,
trash_service: None,
}
}
@@ -767,4 +798,9 @@ impl AppState {
self.auth_service = Some(auth_services);
self
}
pub fn with_trash_service(mut self, trash_service: Arc<dyn TrashUseCase>) -> Self {
self.trash_service = Some(trash_service);
self
}
}
+21 -5
View File
@@ -2,6 +2,9 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
use std::error::Error as StdError;
use thiserror::Error;
/// Tipo Result común para la aplicación con DomainError como error estándar
pub type Result<T> = std::result::Result<T, DomainError>;
/// Tipos de errores comunes en toda la aplicación
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind {
@@ -19,6 +22,8 @@ pub enum ErrorKind {
InternalError,
/// Funcionalidad no implementada
NotImplemented,
/// Operación no soportada
UnsupportedOperation,
}
impl Display for ErrorKind {
@@ -31,6 +36,7 @@ impl Display for ErrorKind {
ErrorKind::Timeout => write!(f, "Timeout"),
ErrorKind::InternalError => write!(f, "Internal Error"),
ErrorKind::NotImplemented => write!(f, "Not Implemented"),
ErrorKind::UnsupportedOperation => write!(f, "Unsupported Operation"),
}
}
}
@@ -92,6 +98,15 @@ impl DomainError {
}
}
/// Crea un error para operaciones no soportadas
pub fn operation_not_supported<S: Into<String>>(entity_type: &'static str, message: S) -> Self {
Self::new(
ErrorKind::UnsupportedOperation,
entity_type,
message,
)
}
/// Crea un error de tiempo agotado
pub fn timeout<S: Into<String>>(entity_type: &'static str, message: S) -> Self {
Self {
@@ -163,17 +178,17 @@ impl DomainError {
/// Trait para añadir contexto a los errores
pub trait ErrorContext<T, E> {
fn with_context<C, F>(self, context: F) -> Result<T, DomainError>
fn with_context<C, F>(self, context: F) -> std::result::Result<T, DomainError>
where
C: Into<String>,
F: FnOnce() -> C;
#[allow(dead_code)]
fn with_error_kind(self, kind: ErrorKind, entity_type: &'static str) -> Result<T, DomainError>;
fn with_error_kind(self, kind: ErrorKind, entity_type: &'static str) -> std::result::Result<T, DomainError>;
}
impl<T, E: StdError + Send + Sync + 'static> ErrorContext<T, E> for Result<T, E> {
fn with_context<C, F>(self, context: F) -> Result<T, DomainError>
impl<T, E: StdError + Send + Sync + 'static> ErrorContext<T, E> for std::result::Result<T, E> {
fn with_context<C, F>(self, context: F) -> std::result::Result<T, DomainError>
where
C: Into<String>,
F: FnOnce() -> C,
@@ -189,7 +204,7 @@ impl<T, E: StdError + Send + Sync + 'static> ErrorContext<T, E> for Result<T, E>
})
}
fn with_error_kind(self, kind: ErrorKind, entity_type: &'static str) -> Result<T, DomainError> {
fn with_error_kind(self, kind: ErrorKind, entity_type: &'static str) -> std::result::Result<T, DomainError> {
self.map_err(|e| {
DomainError {
kind,
@@ -280,6 +295,7 @@ impl From<DomainError> for AppError {
ErrorKind::Timeout => axum::http::StatusCode::REQUEST_TIMEOUT,
ErrorKind::InternalError => axum::http::StatusCode::INTERNAL_SERVER_ERROR,
ErrorKind::NotImplemented => axum::http::StatusCode::NOT_IMPLEMENTED,
ErrorKind::UnsupportedOperation => axum::http::StatusCode::METHOD_NOT_ALLOWED,
};
Self {