adding features

This commit is contained in:
DioCrafts
2025-03-26 19:08:07 +01:00
parent e22c0ac855
commit dacc3ecc4c
37 changed files with 456 additions and 60 deletions
+31 -11
View File
@@ -1,50 +1,70 @@
use serde::{Serialize, Deserialize};
use crate::domain::services::path_service::StoragePath;
/// Error en la creación o manipulación de entidades de archivo
/**
* Represents errors that can occur during file entity operations.
*
* This enum encapsulates various error conditions that may arise when creating,
* validating, or manipulating file entities in the domain model.
*/
#[derive(Debug, thiserror::Error)]
pub enum FileError {
/// Occurs when a file name contains invalid characters or is empty.
#[error("Nombre de archivo inválido: {0}")]
InvalidFileName(String),
/// Occurs when validation fails for any file entity attribute.
#[error("Error en la validación: {0}")]
#[allow(dead_code)]
ValidationError(String),
}
/// Tipo de resultado para operaciones con entidades de archivo
/**
* Type alias for results of file entity operations.
*
* Provides a convenient way to return either a successful value or a FileError.
*/
pub type FileResult<T> = Result<T, FileError>;
/// Represents a file entity in the domain
/**
* Represents a file in the system's domain model.
*
* The File entity is a core domain object that encapsulates all properties and behaviors
* of a file in the system. It implements an immutable design pattern where modification
* operations return new instances rather than modifying the existing one.
*
* This entity maintains both physical storage information and logical metadata about files,
* serving as the bridge between the storage system and the application.
*/
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct File {
/// Unique identifier for the file
/// Unique identifier for the file - used throughout the system for file operations
id: String,
/// Name of the file
/// Name of the file including extension
name: String,
/// Path to the file in the domain model
/// Path to the file in the domain model - not serialized as it contains internal representation
#[serde(skip_serializing, skip_deserializing)]
storage_path: StoragePath,
/// String representation of the path (for serialization compatibility)
/// String representation of the path for serialization and API compatibility
#[serde(rename = "path")]
path_string: String,
/// Size of the file in bytes
size: u64,
/// MIME type of the file
/// MIME type of the file (e.g., "text/plain", "image/jpeg")
mime_type: String,
/// Parent folder ID
/// Parent folder ID if the file is within a folder, None if in root
folder_id: Option<String>,
/// Creation timestamp
/// Creation timestamp (seconds since UNIX epoch)
created_at: u64,
/// Last modification timestamp
/// Last modification timestamp (seconds since UNIX epoch)
modified_at: u64,
}
+140 -18
View File
@@ -5,50 +5,89 @@ use crate::common::errors::DomainError;
use futures::Stream;
use bytes::Bytes;
/// Error types for file repository operations
/**
* Comprehensive error types for file repository operations.
*
* This enum represents all possible error conditions that can occur during file repository
* operations, providing detailed context for error handling across the application.
*/
#[derive(Debug, thiserror::Error)]
#[allow(dead_code)]
pub enum FileRepositoryError {
/// Returned when a requested file cannot be found by ID or path
#[error("File not found: {0}")]
NotFound(String),
/// Returned when attempting to create a file at a location where one already exists
#[error("File already exists: {0}")]
AlreadyExists(String),
/// Returned when a provided file path is invalid or malformed
#[error("Invalid file path: {0}")]
InvalidPath(String),
/// Returned when an operation is not supported by the current implementation
#[error("Operation not supported: {0}")]
OperationNotSupported(String),
/// Wraps standard I/O errors from the filesystem
#[error("IO Error: {0}")]
IoError(#[from] std::io::Error),
/// Indicates errors in the path-to-ID mapping system
#[error("Mapping error: {0}")]
MappingError(String),
/// Specific errors related to ID mapping operations
#[error("ID Mapping error: {0}")]
IdMappingError(String),
/// Returned when an operation exceeds its timeout threshold
#[error("Timeout error: {0}")]
Timeout(String),
/// Propagates domain model errors to the repository layer
#[error("Domain error: {0}")]
DomainError(#[from] DomainError),
/// Catch-all for other unspecified errors
#[error("Other error: {0}")]
Other(String),
}
/// Result type for file repository operations
/**
* Type alias for results of file repository operations.
*
* Provides a consistent return type for all repository methods, containing
* either a successful value or a FileRepositoryError.
*/
pub type FileRepositoryResult<T> = Result<T, FileRepositoryError>;
/// Repository interface for file operations (primary port)
/// Esta interfaz define las operaciones de negocio relacionadas con archivos
/// sin exponer detalles de implementación como rutas o sistemas de archivos
/**
* Repository interface defining all file storage operations.
*
* This trait represents the primary port for file operations in the domain model,
* following the hexagonal architecture pattern. It defines the contract that any
* file storage implementation must fulfill, abstracting away implementation details
* like filesystem specifics, cloud storage, or database operations.
*
* All implementations must be thread-safe (Send + Sync) and have a 'static lifetime
* to support the async operations in the system.
*/
#[async_trait]
pub trait FileRepository: Send + Sync + 'static {
/// Saves a file from bytes
/**
* Creates and saves a new file from binary content.
*
* This method handles new file creation with automatic ID generation,
* content storage, and metadata registration.
*
* @param name The filename with extension
* @param folder_id Optional ID of parent folder, None for root
* @param content_type MIME type of the file
* @param content Binary data of the file
* @return A File entity with generated metadata on success, error otherwise
*/
async fn save_file_from_bytes(
&self,
name: String,
@@ -57,7 +96,19 @@ pub trait FileRepository: Send + Sync + 'static {
content: Vec<u8>,
) -> FileRepositoryResult<File>;
/// Saves a file with a specific ID
/**
* Saves a file with a predetermined ID.
*
* Similar to save_file_from_bytes but allows specifying the ID,
* useful for restoring files or migrations.
*
* @param id Predefined unique ID for the file
* @param name The filename with extension
* @param folder_id Optional ID of parent folder, None for root
* @param content_type MIME type of the file
* @param content Binary data of the file
* @return The created File entity on success, error otherwise
*/
#[allow(dead_code)]
async fn save_file_with_id(
&self,
@@ -68,38 +119,109 @@ pub trait FileRepository: Send + Sync + 'static {
content: Vec<u8>,
) -> FileRepositoryResult<File>;
/// Gets a file by its ID
/**
* Retrieves a file entity by its unique ID.
*
* @param id The unique identifier of the file
* @return The File entity if found, NotFound error otherwise
*/
async fn get_file_by_id(&self, id: &str) -> FileRepositoryResult<File>;
/// Lists files in a folder
/**
* Lists all files within a specified folder.
*
* @param folder_id Optional folder ID to list files from, None for root
* @return Vector of File entities in the folder
*/
async fn list_files(&self, folder_id: Option<&str>) -> FileRepositoryResult<Vec<File>>;
/// Deletes a file
/**
* Deletes a file by ID.
*
* @param id The unique identifier of the file to delete
* @return Success or error
*/
async fn delete_file(&self, id: &str) -> FileRepositoryResult<()>;
/// Deletes a file and its entry from mapping systems
/**
* Deletes a file and removes its mapping entries.
*
* More thorough than delete_file as it also purges ID mappings,
* useful for permanent deletions.
*
* @param id The unique identifier of the file to delete
* @return Success or error
*/
#[allow(dead_code)]
async fn delete_file_entry(&self, id: &str) -> FileRepositoryResult<()>;
/// Gets file content as bytes - use only for small files
/**
* Retrieves the complete file content as a byte vector.
*
* This method loads the entire file into memory, so it should
* only be used for reasonably sized files.
*
* @param id The unique identifier of the file
* @return The file's binary content
*/
async fn get_file_content(&self, id: &str) -> FileRepositoryResult<Vec<u8>>;
/// Gets file content as a stream - better for large files
/**
* Retrieves file content as an asynchronous stream of bytes.
*
* Preferred for large files as it avoids loading everything into memory at once.
*
* @param id The unique identifier of the file
* @return A stream that yields chunks of file data
*/
#[allow(clippy::type_complexity)]
async fn get_file_stream(&self, id: &str) -> FileRepositoryResult<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>>;
/// Moves a file to a different folder
/**
* Moves a file to a different folder.
*
* @param id The unique identifier of the file to move
* @param target_folder_id The destination folder ID, None for root
* @return The updated File entity after the move
*/
async fn move_file(&self, id: &str, target_folder_id: Option<String>) -> FileRepositoryResult<File>;
/// Gets the storage path for a file
/**
* Retrieves the storage path for a file.
*
* @param id The unique identifier of the file
* @return The StoragePath object representing the file's location
*/
async fn get_file_path(&self, id: &str) -> FileRepositoryResult<StoragePath>;
/// Moves a file to trash
/**
* Moves a file to the trash system.
*
* Instead of permanent deletion, this marks the file as trashed
* and relocates it to the trash storage area.
*
* @param file_id The unique identifier of the file to trash
* @return Success or error
*/
async fn move_to_trash(&self, file_id: &str) -> FileRepositoryResult<()>;
/// Restores a file from trash
/**
* Restores a file from the trash to its original location.
*
* @param file_id The unique identifier of the file to restore
* @param original_path The original path where the file was located before trashing
* @return Success or error
*/
async fn restore_from_trash(&self, file_id: &str, original_path: &str) -> FileRepositoryResult<()>;
/// Permanently deletes a file (used for trash cleanup)
/**
* Permanently deletes a file from the trash system.
*
* This operation is not reversible and removes the file completely.
* Used primarily by the trash cleanup service.
*
* @param file_id The unique identifier of the file to permanently delete
* @return Success or error
*/
async fn delete_file_permanently(&self, file_id: &str) -> FileRepositoryResult<()>;
}
+61 -10
View File
@@ -6,39 +6,78 @@ use chrono::Utc;
use crate::domain::entities::user::User;
use crate::common::errors::{DomainError, ErrorKind};
// Reclamaciones JWT
/**
* JWT claims structure for authentication tokens.
*
* This structure represents the payload of JWT tokens used for authentication
* in the system. It contains all the necessary claims to identify users and
* manage token lifecycle.
*/
#[derive(Debug, Serialize, Deserialize)]
pub struct TokenClaims {
pub sub: String, // user ID
pub exp: i64, // expiration timestamp
pub iat: i64, // issued at timestamp
pub jti: String, // JWT ID
pub username: String, // username
pub email: String, // email
pub role: String, // role as string
/// Subject identifier - contains the user ID
pub sub: String,
/// Expiration timestamp (seconds since Unix epoch)
pub exp: i64,
/// Issued at timestamp (seconds since Unix epoch)
pub iat: i64,
/// JWT unique ID for token tracking and revocation
pub jti: String,
/// Username for display and identification purposes
pub username: String,
/// User email for communication and identification
pub email: String,
/// User role for authorization checks
pub role: String,
}
/**
* Authentication-specific error types.
*
* This enum encapsulates all error scenarios that can occur during
* authentication and authorization processes, providing clear error messages
* and categorization for proper handling.
*/
#[derive(Debug, thiserror::Error)]
pub enum AuthError {
/// Returned when username/password authentication fails
#[error("Credenciales inválidas")]
InvalidCredentials,
/// Returned when a JWT token has passed its expiration time
#[error("Token expirado")]
TokenExpired,
/// Returned when a JWT token is malformed or has invalid signature
#[error("Token inválido: {0}")]
InvalidToken(String),
/// Returned when a user attempts to access a resource they don't have permission for
#[error("Acceso denegado: {0}")]
AccessDenied(String),
/// Returned when a requested operation is not allowed for the user
#[error("Operación no permitida: {0}")]
OperationNotAllowed(String),
/// Returned for unexpected errors in the authentication system
#[error("Error interno: {0}")]
InternalError(String),
}
/**
* Conversion from AuthError to DomainError.
*
* This implementation allows authentication errors to be seamlessly
* transformed into domain errors, making error handling more consistent
* throughout the application.
*/
impl From<AuthError> for DomainError {
fn from(err: AuthError) -> Self {
match err {
@@ -64,10 +103,22 @@ impl From<AuthError> for DomainError {
}
}
/**
* Authentication service for managing user sessions and authorization.
*
* This service provides the core authentication functionality for the system,
* including JWT token generation and validation, refresh token management,
* and session duration control.
*/
pub struct AuthService {
/// Secret key used for signing JWT tokens
jwt_secret: String,
access_token_expiry: i64, // segundos
refresh_token_expiry: i64, // segundos
/// Expiration time for access tokens in seconds
access_token_expiry: i64,
/// Expiration time for refresh tokens in seconds
refresh_token_expiry: i64,
}
impl AuthService {