fix auth errors and add primigenial paper trash
This commit is contained in:
+30
-1
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user