chore: translate all Spanish comments and log messages to English

This commit is contained in:
Dionisio
2026-02-12 09:41:25 +01:00
parent bc169de8f6
commit d31a413e57
89 changed files with 2480 additions and 2482 deletions
+29 -29
View File
@@ -15,7 +15,7 @@ use crate::application::dtos::user_dto::{
use crate::interfaces::errors::AppError;
pub fn auth_routes() -> Router<Arc<AppState>> {
// Rutas que NO requieren autenticación
// Routes that do NOT require authentication
let public_routes = Router::new()
.route("/register", post(register))
.route("/login", post(login))
@@ -27,14 +27,14 @@ pub fn auth_routes() -> Router<Arc<AppState>> {
.route("/oidc/callback", get(oidc_callback))
.route("/oidc/exchange", post(oidc_exchange));
// Rutas que SÍ requieren autenticación - usamos route_layer para aplicar middleware
// El middleware usará el state que se pase con .with_state() desde main.rs
// Routes that DO require authentication - we use route_layer to apply middleware
// The middleware will use the state passed with .with_state() from main.rs
let protected_routes = Router::new()
.route("/me", get(get_current_user))
.route("/change-password", put(change_password))
.route("/logout", post(logout));
// Combinar rutas públicas y protegidas
// Combine public and protected routes
public_routes.merge(protected_routes)
}
@@ -53,7 +53,7 @@ async fn register(
},
None => {
tracing::error!("Auth service not configured");
return Err(AppError::internal_error("Servicio de autenticación no configurado"));
return Err(AppError::internal_error("Authentication service not configured"));
}
};
@@ -153,7 +153,7 @@ async fn login(
},
None => {
tracing::error!("Auth service not configured");
return Err(AppError::internal_error("Servicio de autenticación no configurado"));
return Err(AppError::internal_error("Authentication service not configured"));
}
};
@@ -174,7 +174,7 @@ async fn login(
// Ensure the response has the expected fields
if auth_response.access_token.is_empty() || auth_response.refresh_token.is_empty() {
tracing::error!("Login response contains empty tokens for user: {}", dto.username);
return Err(AppError::internal_error("Error generando tokens de autenticación"));
return Err(AppError::internal_error("Error generating authentication tokens"));
}
Ok((StatusCode::OK, Json(auth_response)))
@@ -198,7 +198,7 @@ async fn refresh_token(
// Normal process for real tokens
let auth_service = state.auth_service.as_ref()
.ok_or_else(|| AppError::internal_error("Servicio de autenticación no configurado"))?;
.ok_or_else(|| AppError::internal_error("Authentication service not configured"))?;
let auth_response = auth_service.auth_application_service.refresh_token(dto).await?;
@@ -214,37 +214,37 @@ async fn get_current_user(
) -> Result<impl IntoResponse, AppError> {
// Normal process for all users
let auth_service = state.auth_service.as_ref()
.ok_or_else(|| AppError::internal_error("Servicio de autenticación no configurado"))?;
.ok_or_else(|| AppError::internal_error("Authentication service not configured"))?;
// Extraer y validar el token directamente
// Extract and validate the token directly
let token = headers
.get(header::AUTHORIZATION)
.and_then(|value| value.to_str().ok())
.and_then(|value| value.strip_prefix("Bearer "))
.ok_or_else(|| AppError::unauthorized("Token de autorización no encontrado"))?;
.ok_or_else(|| AppError::unauthorized("Authorization token not found"))?;
// Validar el token y obtener claims
// Validate the token and get claims
let claims = auth_service.token_service.validate_token(token)
.map_err(|e| AppError::unauthorized(&format!("Token inválido: {}", e)))?;
.map_err(|e| AppError::unauthorized(&format!("Invalid token: {}", e)))?;
let user_id = claims.sub;
// Primero, actualizar las estadísticas de uso de almacenamiento
// IMPORTANTE: Esperamos el cálculo para devolver datos actualizados
// First, update the storage usage statistics
// IMPORTANT: We await the calculation to return updated data
if let Some(storage_usage_service) = state.storage_usage_service.as_ref() {
// Calcular storage de forma síncrona (esperamos el resultado)
// Calculate storage synchronously (we await the result)
match storage_usage_service.update_user_storage_usage(&user_id).await {
Ok(usage) => {
tracing::info!("Updated storage usage for user {}: {} bytes", user_id, usage);
},
Err(e) => {
// Solo log de warning, no fallar la petición completa
// Only log a warning, don't fail the entire request
tracing::warn!("Failed to update storage usage for user {}: {}", user_id, e);
}
}
}
// Ahora obtener los datos del usuario CON el almacenamiento actualizado
// Now get the user data WITH the updated storage
let user = auth_service.auth_application_service.get_user_by_id(&user_id).await?;
Ok((StatusCode::OK, Json(user)))
@@ -256,18 +256,18 @@ async fn change_password(
Json(dto): Json<ChangePasswordDto>,
) -> Result<impl IntoResponse, AppError> {
let auth_service = state.auth_service.as_ref()
.ok_or_else(|| AppError::internal_error("Servicio de autenticación no configurado"))?;
.ok_or_else(|| AppError::internal_error("Authentication service not configured"))?;
// Extraer y validar el token directamente
// Extract and validate the token directly
let token = headers
.get(header::AUTHORIZATION)
.and_then(|value| value.to_str().ok())
.and_then(|value| value.strip_prefix("Bearer "))
.ok_or_else(|| AppError::unauthorized("Token de autorización no encontrado"))?;
.ok_or_else(|| AppError::unauthorized("Authorization token not found"))?;
// Validar el token y obtener claims
// Validate the token and get claims
let claims = auth_service.token_service.validate_token(token)
.map_err(|e| AppError::unauthorized(&format!("Token inválido: {}", e)))?;
.map_err(|e| AppError::unauthorized(&format!("Invalid token: {}", e)))?;
auth_service.auth_application_service.change_password(&claims.sub, dto).await?;
@@ -279,18 +279,18 @@ async fn logout(
headers: HeaderMap,
) -> Result<impl IntoResponse, AppError> {
let auth_service = state.auth_service.as_ref()
.ok_or_else(|| AppError::internal_error("Servicio de autenticación no configurado"))?;
.ok_or_else(|| AppError::internal_error("Authentication service not configured"))?;
// Extraer y validar el token directamente
// Extract and validate the token directly
let token = headers
.get(header::AUTHORIZATION)
.and_then(|value| value.to_str().ok())
.and_then(|value| value.strip_prefix("Bearer "))
.ok_or_else(|| AppError::unauthorized("Token de autorización no encontrado"))?;
.ok_or_else(|| AppError::unauthorized("Authorization token not found"))?;
// Validar el token y obtener claims
// Validate the token and get claims
let claims = auth_service.token_service.validate_token(token)
.map_err(|e| AppError::unauthorized(&format!("Token inválido: {}", e)))?;
.map_err(|e| AppError::unauthorized(&format!("Invalid token: {}", e)))?;
// Use access token for logout (we don't have refresh token in headers)
auth_service.auth_application_service.logout(&claims.sub, token).await?;
@@ -314,7 +314,7 @@ async fn get_system_status(
State(state): State<Arc<AppState>>,
) -> Result<impl IntoResponse, AppError> {
let auth_service = state.auth_service.as_ref()
.ok_or_else(|| AppError::internal_error("Servicio de autenticación no configurado"))?;
.ok_or_else(|| AppError::internal_error("Authentication service not configured"))?;
// Count admin users to determine if system is initialized
let admin_count = auth_service.auth_application_service.count_admin_users().await
+84 -84
View File
@@ -13,86 +13,86 @@ use crate::application::dtos::file_dto::FileDto;
use crate::application::dtos::folder_dto::FolderDto;
use crate::interfaces::api::handlers::ApiResult;
/// Estado compartido para el handler de batch
/// Shared state for the batch handler
#[derive(Clone)]
pub struct BatchHandlerState {
pub batch_service: Arc<BatchOperationService>,
}
/// DTO para las solicitudes de operaciones en lote de archivos
/// DTO for batch file operation requests
#[derive(Debug, Deserialize)]
pub struct BatchFileOperationRequest {
/// IDs de los archivos a procesar
/// IDs of the files to process
pub file_ids: Vec<String>,
/// ID de la carpeta destino (opcional)
/// Target folder ID (optional)
#[serde(skip_serializing_if = "Option::is_none")]
pub target_folder_id: Option<String>,
}
/// DTO para las solicitudes de operaciones en lote de carpetas
/// DTO for batch folder operation requests
#[derive(Debug, Deserialize)]
pub struct BatchFolderOperationRequest {
/// IDs de las carpetas a procesar
/// IDs of the folders to process
pub folder_ids: Vec<String>,
/// Si la operación debe ser recursiva
/// Whether the operation should be recursive
#[serde(default)]
pub recursive: bool,
/// ID de la carpeta destino (opcional)
/// Target folder ID (optional)
#[serde(skip_serializing_if = "Option::is_none")]
pub target_folder_id: Option<String>,
}
/// DTO para las solicitudes de creación en lote de carpetas
/// DTO for batch folder creation requests
#[derive(Debug, Deserialize)]
pub struct BatchCreateFoldersRequest {
/// Detalles de las carpetas a crear
/// Details of the folders to create
pub folders: Vec<CreateFolderDetail>,
}
/// Detalle para creación de una carpeta
/// Detail for folder creation
#[derive(Debug, Deserialize)]
pub struct CreateFolderDetail {
/// Nombre de la carpeta
/// Folder name
pub name: String,
/// ID de la carpeta padre (opcional)
/// Parent folder ID (optional)
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_id: Option<String>,
}
/// DTO para los resultados de operaciones en lote
/// DTO for batch operation results
#[derive(Debug, Serialize)]
pub struct BatchOperationResponse<T> {
/// Entidades procesadas exitosamente
/// Successfully processed entities
pub successful: Vec<T>,
/// Operaciones fallidas con sus mensajes de error
/// Failed operations with their error messages
pub failed: Vec<FailedOperation>,
/// Estadísticas de la operación
/// Operation statistics
pub stats: BatchOperationStats,
}
/// Operación fallida en un lote
/// Failed operation in a batch
#[derive(Debug, Serialize)]
pub struct FailedOperation {
/// Identificador de la entidad que falló
/// Identifier of the entity that failed
pub id: String,
/// Mensaje de error
/// Error message
pub error: String,
}
/// Estadísticas de una operación por lotes
/// Statistics for a batch operation
#[derive(Debug, Serialize)]
pub struct BatchOperationStats {
/// Número total de operaciones
/// Total number of operations
pub total: usize,
/// Número de operaciones exitosas
/// Number of successful operations
pub successful: usize,
/// Número de operaciones fallidas
/// Number of failed operations
pub failed: usize,
/// Tiempo total de ejecución en milisegundos
/// Total execution time in milliseconds
pub execution_time_ms: u128,
}
/// Convierte BatchStats del dominio a DTO
/// Converts domain BatchStats to DTO
impl From<BatchStats> for BatchOperationStats {
fn from(stats: BatchStats) -> Self {
Self {
@@ -104,7 +104,7 @@ impl From<BatchStats> for BatchOperationStats {
}
}
/// Convierte BatchResult<T> del dominio a DTO
/// Converts domain BatchResult<T> to DTO
impl<T, U> From<BatchResult<T>> for BatchOperationResponse<U>
where
U: From<T>,
@@ -124,12 +124,12 @@ where
}
}
/// Handler para mover múltiples archivos en lote
/// Handler for moving multiple files in batch
pub async fn move_files_batch(
State(state): State<BatchHandlerState>,
Json(request): Json<BatchFileOperationRequest>,
) -> ApiResult<impl IntoResponse> {
// Verificar que hay archivos para procesar
// Verify there are files to process
if request.file_ids.is_empty() {
return Ok((
StatusCode::BAD_REQUEST,
@@ -139,35 +139,35 @@ pub async fn move_files_batch(
).into_response());
}
// Ejecutar operación de lote
// Execute batch operation
let result = state.batch_service
.move_files(request.file_ids, request.target_folder_id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Convertir resultado a DTO
// Convert result to DTO
let response: BatchOperationResponse<FileDto> = result.into();
// Determinar código de estado basado en los resultados
// Determine status code based on results
let status_code = if response.stats.failed > 0 {
if response.stats.successful > 0 {
StatusCode::PARTIAL_CONTENT // Algunas operaciones exitosas, otras fallidas
StatusCode::PARTIAL_CONTENT // Some operations successful, others failed
} else {
StatusCode::BAD_REQUEST // Todas fallaron
StatusCode::BAD_REQUEST // All failed
}
} else {
StatusCode::OK // Todas exitosas
StatusCode::OK // All successful
};
Ok((status_code, Json(response)).into_response())
}
/// Handler para copiar múltiples archivos en lote
/// Handler for copying multiple files in batch
pub async fn copy_files_batch(
State(state): State<BatchHandlerState>,
Json(request): Json<BatchFileOperationRequest>,
) -> ApiResult<impl IntoResponse> {
// Verificar que hay archivos para procesar
// Verify there are files to process
if request.file_ids.is_empty() {
return Ok((
StatusCode::BAD_REQUEST,
@@ -177,35 +177,35 @@ pub async fn copy_files_batch(
).into_response());
}
// Ejecutar operación de lote
// Execute batch operation
let result = state.batch_service
.copy_files(request.file_ids, request.target_folder_id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Convertir resultado a DTO
// Convert result to DTO
let response: BatchOperationResponse<FileDto> = result.into();
// Determinar código de estado basado en los resultados
// Determine status code based on results
let status_code = if response.stats.failed > 0 {
if response.stats.successful > 0 {
StatusCode::PARTIAL_CONTENT // Algunas operaciones exitosas, otras fallidas
StatusCode::PARTIAL_CONTENT // Some operations successful, others failed
} else {
StatusCode::BAD_REQUEST // Todas fallaron
StatusCode::BAD_REQUEST // All failed
}
} else {
StatusCode::OK // Todas exitosas
StatusCode::OK // All successful
};
Ok((status_code, Json(response)).into_response())
}
/// Handler para eliminar múltiples archivos en lote
/// Handler for deleting multiple files in batch
pub async fn delete_files_batch(
State(state): State<BatchHandlerState>,
Json(request): Json<BatchFileOperationRequest>,
) -> ApiResult<impl IntoResponse> {
// Verificar que hay archivos para procesar
// Verify there are files to process
if request.file_ids.is_empty() {
return Ok((
StatusCode::BAD_REQUEST,
@@ -215,13 +215,13 @@ pub async fn delete_files_batch(
).into_response());
}
// Ejecutar operación de lote
// Execute batch operation
let result = state.batch_service
.delete_files(request.file_ids)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Crear respuesta personalizada para IDs de string
// Create custom response for string IDs
let response = BatchOperationResponse {
successful: result.successful,
failed: result.failed.into_iter()
@@ -230,26 +230,26 @@ pub async fn delete_files_batch(
stats: result.stats.into(),
};
// Determinar código de estado basado en los resultados
// Determine status code based on results
let status_code = if response.stats.failed > 0 {
if response.stats.successful > 0 {
StatusCode::PARTIAL_CONTENT // Algunas operaciones exitosas, otras fallidas
StatusCode::PARTIAL_CONTENT // Some operations successful, others failed
} else {
StatusCode::BAD_REQUEST // Todas fallaron
StatusCode::BAD_REQUEST // All failed
}
} else {
StatusCode::OK // Todas exitosas
StatusCode::OK // All successful
};
Ok((status_code, Json(response)).into_response())
}
/// Handler para eliminar múltiples carpetas en lote
/// Handler for deleting multiple folders in batch
pub async fn delete_folders_batch(
State(state): State<BatchHandlerState>,
Json(request): Json<BatchFolderOperationRequest>,
) -> ApiResult<impl IntoResponse> {
// Verificar que hay carpetas para procesar
// Verify there are folders to process
if request.folder_ids.is_empty() {
return Ok((
StatusCode::BAD_REQUEST,
@@ -259,13 +259,13 @@ pub async fn delete_folders_batch(
).into_response());
}
// Ejecutar operación de lote
// Execute batch operation
let result = state.batch_service
.delete_folders(request.folder_ids, request.recursive)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Crear respuesta personalizada para IDs de string
// Create custom response for string IDs
let response = BatchOperationResponse {
successful: result.successful,
failed: result.failed.into_iter()
@@ -274,26 +274,26 @@ pub async fn delete_folders_batch(
stats: result.stats.into(),
};
// Determinar código de estado basado en los resultados
// Determine status code based on results
let status_code = if response.stats.failed > 0 {
if response.stats.successful > 0 {
StatusCode::PARTIAL_CONTENT // Algunas operaciones exitosas, otras fallidas
StatusCode::PARTIAL_CONTENT // Some operations successful, others failed
} else {
StatusCode::BAD_REQUEST // Todas fallaron
StatusCode::BAD_REQUEST // All failed
}
} else {
StatusCode::OK // Todas exitosas
StatusCode::OK // All successful
};
Ok((status_code, Json(response)).into_response())
}
/// Handler para crear múltiples carpetas en lote
/// Handler for creating multiple folders in batch
pub async fn create_folders_batch(
State(state): State<BatchHandlerState>,
Json(request): Json<BatchCreateFoldersRequest>,
) -> ApiResult<impl IntoResponse> {
// Verificar que hay carpetas para procesar
// Verify there are folders to process
if request.folders.is_empty() {
return Ok((
StatusCode::BAD_REQUEST,
@@ -303,41 +303,41 @@ pub async fn create_folders_batch(
).into_response());
}
// Transformar el formato para el servicio
// Transform the format for the service
let folders = request.folders
.into_iter()
.map(|detail| (detail.name, detail.parent_id))
.collect();
// Ejecutar operación de lote
// Execute batch operation
let result = state.batch_service
.create_folders(folders)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Convertir resultado a DTO
// Convert result to DTO
let response: BatchOperationResponse<FolderDto> = result.into();
// Determinar código de estado basado en los resultados
// Determine status code based on results
let status_code = if response.stats.failed > 0 {
if response.stats.successful > 0 {
StatusCode::PARTIAL_CONTENT // Algunas operaciones exitosas, otras fallidas
StatusCode::PARTIAL_CONTENT // Some operations successful, others failed
} else {
StatusCode::BAD_REQUEST // Todas fallaron
StatusCode::BAD_REQUEST // All failed
}
} else {
StatusCode::CREATED // Todas exitosas
StatusCode::CREATED // All successful
};
Ok((status_code, Json(response)).into_response())
}
/// Handler para obtener múltiples archivos en lote
/// Handler for getting multiple files in batch
pub async fn get_files_batch(
State(state): State<BatchHandlerState>,
Json(request): Json<BatchFileOperationRequest>,
) -> ApiResult<impl IntoResponse> {
// Verificar que hay archivos para procesar
// Verify there are files to process
if request.file_ids.is_empty() {
return Ok((
StatusCode::BAD_REQUEST,
@@ -347,35 +347,35 @@ pub async fn get_files_batch(
).into_response());
}
// Ejecutar operación de lote
// Execute batch operation
let result = state.batch_service
.get_multiple_files(request.file_ids)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Convertir resultado a DTO
// Convert result to DTO
let response: BatchOperationResponse<FileDto> = result.into();
// Determinar código de estado basado en los resultados
// Determine status code based on results
let status_code = if response.stats.failed > 0 {
if response.stats.successful > 0 {
StatusCode::PARTIAL_CONTENT // Algunas operaciones exitosas, otras fallidas
StatusCode::PARTIAL_CONTENT // Some operations successful, others failed
} else {
StatusCode::BAD_REQUEST // Todas fallaron
StatusCode::BAD_REQUEST // All failed
}
} else {
StatusCode::OK // Todas exitosas
StatusCode::OK // All successful
};
Ok((status_code, Json(response)).into_response())
}
/// Handler para obtener múltiples carpetas en lote
/// Handler for getting multiple folders in batch
pub async fn get_folders_batch(
State(state): State<BatchHandlerState>,
Json(request): Json<BatchFolderOperationRequest>,
) -> ApiResult<impl IntoResponse> {
// Verificar que hay carpetas para procesar
// Verify there are folders to process
if request.folder_ids.is_empty() {
return Ok((
StatusCode::BAD_REQUEST,
@@ -385,24 +385,24 @@ pub async fn get_folders_batch(
).into_response());
}
// Ejecutar operación de lote
// Execute batch operation
let result = state.batch_service
.get_multiple_folders(request.folder_ids)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Convertir resultado a DTO
// Convert result to DTO
let response: BatchOperationResponse<FolderDto> = result.into();
// Determinar código de estado basado en los resultados
// Determine status code based on results
let status_code = if response.stats.failed > 0 {
if response.stats.successful > 0 {
StatusCode::PARTIAL_CONTENT // Algunas operaciones exitosas, otras fallidas
StatusCode::PARTIAL_CONTENT // Some operations successful, others failed
} else {
StatusCode::BAD_REQUEST // Todas fallaron
StatusCode::BAD_REQUEST // All failed
}
} else {
StatusCode::OK // Todas exitosas
StatusCode::OK // All successful
};
Ok((status_code, Json(response)).into_response())
+24 -24
View File
@@ -11,14 +11,14 @@ use tracing::{error, info};
use crate::application::ports::recent_ports::RecentItemsUseCase;
use crate::interfaces::middleware::auth::AuthUser;
/// Parámetros de consulta para obtener elementos recientes
/// Query parameters for getting recent items
#[derive(Deserialize)]
pub struct GetRecentParams {
#[serde(default)]
limit: Option<i32>,
}
/// Obtener elementos recientes del usuario
/// Get user's recent items
pub async fn get_recent_items(
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
auth_user: AuthUser,
@@ -28,22 +28,22 @@ pub async fn get_recent_items(
match recent_service.get_recent_items(user_id, params.limit).await {
Ok(items) => {
info!("Recuperados {} elementos recientes para usuario", items.len());
info!("Retrieved {} recent items for user", items.len());
(StatusCode::OK, Json(items)).into_response()
},
Err(err) => {
error!("Error al recuperar elementos recientes: {}", err);
error!("Error retrieving recent items: {}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({
"error": format!("Fallo al recuperar elementos recientes: {}", err)
"error": format!("Failed to retrieve recent items: {}", err)
}))
).into_response()
}
}
}
/// Registrar acceso a un elemento
/// Record access to an item
pub async fn record_item_access(
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
auth_user: AuthUser,
@@ -51,39 +51,39 @@ pub async fn record_item_access(
) -> impl IntoResponse {
let user_id = &auth_user.id;
// Validar tipo de elemento
// Validate item type
if item_type != "file" && item_type != "folder" {
return (
StatusCode::BAD_REQUEST,
Json(serde_json::json!({
"error": "El tipo de elemento debe ser 'file' o 'folder'"
"error": "Item type must be 'file' or 'folder'"
}))
).into_response();
}
match recent_service.record_item_access(user_id, &item_id, &item_type).await {
Ok(_) => {
info!("Registrado acceso a {} '{}' en recientes", item_type, item_id);
info!("Recorded access to {} '{}' in recents", item_type, item_id);
(
StatusCode::OK,
Json(serde_json::json!({
"message": "Acceso registrado correctamente"
"message": "Access recorded successfully"
}))
).into_response()
},
Err(err) => {
error!("Error al registrar acceso en recientes: {}", err);
error!("Error recording access in recents: {}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({
"error": format!("Fallo al registrar acceso: {}", err)
"error": format!("Failed to record access: {}", err)
}))
).into_response()
}
}
}
/// Eliminar un elemento de recientes
/// Remove an item from recents
pub async fn remove_from_recent(
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
auth_user: AuthUser,
@@ -94,36 +94,36 @@ pub async fn remove_from_recent(
match recent_service.remove_from_recent(user_id, &item_id, &item_type).await {
Ok(removed) => {
if removed {
info!("Eliminado {} '{}' de recientes", item_type, item_id);
info!("Removed {} '{}' from recents", item_type, item_id);
(
StatusCode::OK,
Json(serde_json::json!({
"message": "Elemento eliminado de recientes"
"message": "Item removed from recents"
}))
).into_response()
} else {
info!("Elemento {} '{}' no estaba en recientes", item_type, item_id);
info!("Item {} '{}' was not in recents", item_type, item_id);
(
StatusCode::NOT_FOUND,
Json(serde_json::json!({
"message": "Elemento no estaba en recientes"
"message": "Item was not in recents"
}))
).into_response()
}
},
Err(err) => {
error!("Error al eliminar de recientes: {}", err);
error!("Error removing from recents: {}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({
"error": format!("Fallo al eliminar de recientes: {}", err)
"error": format!("Failed to remove from recents: {}", err)
}))
).into_response()
}
}
}
/// Limpiar todos los elementos recientes
/// Clear all recent items
pub async fn clear_recent_items(
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
auth_user: AuthUser,
@@ -132,20 +132,20 @@ pub async fn clear_recent_items(
match recent_service.clear_recent_items(user_id).await {
Ok(_) => {
info!("Limpiados todos los elementos recientes para usuario");
info!("Cleared all recent items for user");
(
StatusCode::OK,
Json(serde_json::json!({
"message": "Elementos recientes limpiados correctamente"
"message": "Recent items cleared successfully"
}))
).into_response()
},
Err(err) => {
error!("Error al limpiar elementos recientes: {}", err);
error!("Error clearing recent items: {}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({
"error": format!("Fallo al limpiar elementos recientes: {}", err)
"error": format!("Failed to clear recent items: {}", err)
}))
).into_response()
}
+51 -51
View File
@@ -10,34 +10,34 @@ use crate::application::dtos::search_dto::SearchCriteriaDto;
use crate::common::di::AppState;
/**
* Manejador para las operaciones de búsqueda a través de la API.
* Handler for search operations through the API.
*
* Este manejador expone endpoints relacionados con la funcionalidad de búsqueda,
* permitiendo a los usuarios buscar archivos y carpetas usando diversos criterios.
* This handler exposes endpoints related to search functionality,
* allowing users to search for files and folders using various criteria.
*/
pub struct SearchHandler;
impl SearchHandler {
/**
* Realiza una búsqueda basada en los criterios proporcionados como parámetros de consulta.
* Performs a search based on the criteria provided as query parameters.
*
* Este endpoint permite búsquedas simples directamente con parámetros URL.
* This endpoint allows simple searches directly with URL parameters.
*
* @param state Estado de la aplicación con servicios
* @param query_params Parámetros de búsqueda como query string
* @return Respuesta HTTP con los resultados de la búsqueda
* @param state Application state with services
* @param query_params Search parameters as query string
* @return HTTP response with the search results
*/
pub async fn search_files_get(
State(state): State<AppState>,
Query(params): Query<SearchParams>,
) -> impl IntoResponse {
info!("API: Búsqueda de archivos con parámetros: {:?}", params);
info!("API: File search with parameters: {:?}", params);
// Extraer el servicio de búsqueda o devolver error si no está disponible
// Extract the search service or return error if not available
let search_service = match &state.applications.search_service {
Some(service) => service,
None => {
error!("Servicio de búsqueda no disponible");
error!("Search service not available");
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({
@@ -47,7 +47,7 @@ impl SearchHandler {
}
};
// Convertir parámetros de búsqueda a DTO
// Convert search parameters to DTO
let search_criteria = SearchCriteriaDto {
name_contains: params.query,
file_types: params.type_filter.map(|t| t.split(',').map(|s| s.trim().to_string()).collect()),
@@ -63,15 +63,15 @@ impl SearchHandler {
offset: params.offset.unwrap_or(0),
};
// Realizar la búsqueda
// Perform the search
match search_service.search(search_criteria).await {
Ok(results) => {
info!("Búsqueda completada, {} archivos y {} carpetas encontrados",
info!("Search completed, {} files and {} folders found",
results.files.len(), results.folders.len());
(StatusCode::OK, Json(results)).into_response()
},
Err(err) => {
error!("Error en búsqueda: {}", err);
error!("Search error: {}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
@@ -83,26 +83,26 @@ impl SearchHandler {
}
/**
* Realiza una búsqueda avanzada basada en un objeto de criterios JSON completo.
* Performs an advanced search based on a complete JSON criteria object.
*
* Este endpoint permite búsquedas más complejas con todos los criterios posibles
* proporcionados en el cuerpo de la solicitud.
* This endpoint allows more complex searches with all possible criteria
* provided in the request body.
*
* @param state Estado de la aplicación con servicios
* @param criteria Criterios de búsqueda completos
* @return Respuesta HTTP con los resultados de la búsqueda
* @param state Application state with services
* @param criteria Complete search criteria
* @return HTTP response with the search results
*/
pub async fn search_files_post(
State(state): State<AppState>,
Json(criteria): Json<SearchCriteriaDto>,
) -> impl IntoResponse {
info!("API: Búsqueda avanzada de archivos");
info!("API: Advanced file search");
// Extraer el servicio de búsqueda o devolver error si no está disponible
// Extract the search service or return error if not available
let search_service = match &state.applications.search_service {
Some(service) => service,
None => {
error!("Servicio de búsqueda no disponible");
error!("Search service not available");
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({
@@ -112,15 +112,15 @@ impl SearchHandler {
}
};
// Realizar la búsqueda
// Perform the search
match search_service.search(criteria).await {
Ok(results) => {
info!("Búsqueda completada, {} archivos y {} carpetas encontrados",
info!("Search completed, {} files and {} folders found",
results.files.len(), results.folders.len());
(StatusCode::OK, Json(results)).into_response()
},
Err(err) => {
error!("Error en búsqueda: {}", err);
error!("Search error: {}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
@@ -132,24 +132,24 @@ impl SearchHandler {
}
/**
* Limpia la caché de resultados de búsqueda.
* Clears the search results cache.
*
* Este endpoint es útil para forzar búsquedas frescas después de cambios
* significativos en el sistema de archivos.
* This endpoint is useful for forcing fresh searches after significant
* changes in the file system.
*
* @param state Estado de la aplicación con servicios
* @return Respuesta HTTP indicando éxito o error
* @param state Application state with services
* @return HTTP response indicating success or error
*/
pub async fn clear_search_cache(
State(state): State<AppState>,
) -> impl IntoResponse {
info!("API: Limpiando caché de búsqueda");
info!("API: Clearing search cache");
// Extraer el servicio de búsqueda o devolver error si no está disponible
// Extract the search service or return error if not available
let search_service = match &state.applications.search_service {
Some(service) => service,
None => {
error!("Servicio de búsqueda no disponible");
error!("Search service not available");
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({
@@ -159,10 +159,10 @@ impl SearchHandler {
}
};
// Limpiar la caché
// Clear the cache
match search_service.clear_search_cache().await {
Ok(_) => {
info!("Caché de búsqueda limpiada correctamente");
info!("Search cache cleared successfully");
(
StatusCode::OK,
Json(json!({
@@ -171,7 +171,7 @@ impl SearchHandler {
).into_response()
},
Err(err) => {
error!("Error al limpiar caché de búsqueda: {}", err);
error!("Error clearing search cache: {}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
@@ -183,43 +183,43 @@ impl SearchHandler {
}
}
/// Parámetros de búsqueda para el endpoint GET
/// Search parameters for the GET endpoint
#[derive(Debug, serde::Deserialize)]
pub struct SearchParams {
/// Texto a buscar en nombres de archivos y carpetas
/// Text to search for in file and folder names
pub query: Option<String>,
/// Filtro por tipos de archivo (extensiones separadas por comas)
/// Filter by file types (comma-separated extensions)
#[serde(rename = "type")]
pub type_filter: Option<String>,
/// Filtrar elementos creados después de esta fecha (timestamp)
/// Filter items created after this date (timestamp)
pub created_after: Option<u64>,
/// Filtrar elementos creados antes de esta fecha (timestamp)
/// Filter items created before this date (timestamp)
pub created_before: Option<u64>,
/// Filtrar elementos modificados después de esta fecha (timestamp)
/// Filter items modified after this date (timestamp)
pub modified_after: Option<u64>,
/// Filtrar elementos modificados antes de esta fecha (timestamp)
/// Filter items modified before this date (timestamp)
pub modified_before: Option<u64>,
/// Tamaño mínimo en bytes
/// Minimum size in bytes
pub min_size: Option<u64>,
/// Tamaño máximo en bytes
/// Maximum size in bytes
pub max_size: Option<u64>,
/// ID de carpeta para limitar la búsqueda
/// Folder ID to limit the search scope
pub folder_id: Option<String>,
/// Búsqueda recursiva en subcarpetas
/// Recursive search in subfolders
pub recursive: Option<bool>,
/// Límite de resultados para paginación
/// Result limit for pagination
pub limit: Option<usize>,
/// Desplazamiento para paginación
/// Offset for pagination
pub offset: Option<usize>,
}
+30 -30
View File
@@ -8,7 +8,7 @@ use tracing::{debug, error, warn, instrument};
use crate::common::di::AppState;
use crate::interfaces::middleware::auth::AuthUser;
/// Obtiene todos los elementos en la papelera para el usuario actual
/// Gets all items in the trash for the current user
#[instrument(skip_all)]
pub async fn get_trash_items(
State(state): State<AppState>,
@@ -19,7 +19,7 @@ pub async fn get_trash_items(
// privilege escalation attacks.
let effective_user = auth_user.id.clone();
debug!("Solicitud para listar elementos en papelera para usuario {}", effective_user);
debug!("Request to list trash items for user {}", effective_user);
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
@@ -34,11 +34,11 @@ pub async fn get_trash_items(
match result {
Ok(items) => {
debug!("Encontrados {} elementos en la papelera", items.len());
debug!("Found {} items in trash", items.len());
(StatusCode::OK, Json(json!(items)))
},
Err(e) => {
error!("Error al obtener elementos de la papelera: {:?}", e);
error!("Error retrieving trash items: {:?}", e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({
"error": format!("Error retrieving trash items: {}", e)
})))
@@ -46,14 +46,14 @@ pub async fn get_trash_items(
}
}
/// Mueve un elemento (archivo o carpeta) a la papelera (función genérica, no usada directamente en rutas)
/// Moves an item (file or folder) to the trash (generic function, not used directly in routes)
#[instrument(skip_all)]
pub async fn move_to_trash(
State(state): State<AppState>,
auth_user: AuthUser,
Path((item_type, item_id)): Path<(String, String)>,
) -> (StatusCode, Json<serde_json::Value>) {
debug!("Solicitud para mover a papelera: tipo={}, id={}, usuario={}",
debug!("Request to move to trash: type={}, id={}, user={}",
item_type, item_id, auth_user.id);
let trash_service = match state.trash_service.as_ref() {
@@ -68,14 +68,14 @@ pub async fn move_to_trash(
match result {
Ok(_) => {
debug!("Elemento movido a papelera con éxito");
debug!("Item moved to trash successfully");
(StatusCode::OK, Json(json!({
"success": true,
"message": "Item moved to trash successfully"
})))
},
Err(e) => {
error!("Error al mover elemento a papelera: {:?}", e);
error!("Error moving item to trash: {:?}", e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({
"error": format!("Error moving item to trash: {}", e)
})))
@@ -83,14 +83,14 @@ pub async fn move_to_trash(
}
}
/// Mueve un archivo a la papelera
/// Moves a file to the trash
#[instrument(skip_all)]
pub async fn move_file_to_trash(
State(state): State<AppState>,
auth_user: AuthUser,
Path(item_id): Path<String>,
) -> (StatusCode, Json<serde_json::Value>) {
debug!("Solicitud para mover archivo a papelera: id={}, usuario={}",
debug!("Request to move file to trash: id={}, user={}",
item_id, auth_user.id);
let trash_service = match state.trash_service.as_ref() {
@@ -102,19 +102,19 @@ pub async fn move_file_to_trash(
}
};
// Especificar que es un archivo
// Specify that it is a file
let result = trash_service.move_to_trash(&item_id, "file", &auth_user.id).await;
match result {
Ok(_) => {
debug!("Archivo movido a papelera con éxito");
debug!("File moved to trash successfully");
(StatusCode::OK, Json(json!({
"success": true,
"message": "File moved to trash successfully"
})))
},
Err(e) => {
error!("Error al mover archivo a papelera: {:?}", e);
error!("Error moving file to trash: {:?}", e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({
"error": format!("Error moving file to trash: {}", e)
})))
@@ -122,14 +122,14 @@ pub async fn move_file_to_trash(
}
}
/// Mueve una carpeta a la papelera
/// Moves a folder to the trash
#[instrument(skip_all)]
pub async fn move_folder_to_trash(
State(state): State<AppState>,
auth_user: AuthUser,
Path(item_id): Path<String>,
) -> (StatusCode, Json<serde_json::Value>) {
debug!("Solicitud para mover carpeta a papelera: id={}, usuario={}",
debug!("Request to move folder to trash: id={}, user={}",
item_id, auth_user.id);
let trash_service = match state.trash_service.as_ref() {
@@ -141,19 +141,19 @@ pub async fn move_folder_to_trash(
}
};
// Especificar que es una carpeta
// Specify that it is a folder
let result = trash_service.move_to_trash(&item_id, "folder", &auth_user.id).await;
match result {
Ok(_) => {
debug!("Carpeta movida a papelera con éxito");
debug!("Folder moved to trash successfully");
(StatusCode::OK, Json(json!({
"success": true,
"message": "Folder moved to trash successfully"
})))
},
Err(e) => {
error!("Error al mover carpeta a papelera: {:?}", e);
error!("Error moving folder to trash: {:?}", e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({
"error": format!("Error moving folder to trash: {}", e)
})))
@@ -161,14 +161,14 @@ pub async fn move_folder_to_trash(
}
}
/// Restaura un elemento desde la papelera a su ubicación original
/// Restores an item from the trash to its original location
#[instrument(skip_all)]
pub async fn restore_from_trash(
State(state): State<AppState>,
auth_user: AuthUser,
Path(trash_id): Path<String>,
) -> (StatusCode, Json<serde_json::Value>) {
debug!("Solicitud para restaurar elemento {} de papelera", trash_id);
debug!("Request to restore item {} from trash", trash_id);
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
@@ -182,7 +182,7 @@ pub async fn restore_from_trash(
match result {
Ok(_) => {
debug!("Elemento restaurado con éxito");
debug!("Item restored successfully");
(StatusCode::OK, Json(json!({
"success": true,
"message": "Item restored successfully"
@@ -199,7 +199,7 @@ pub async fn restore_from_trash(
})));
}
error!("Error al restaurar elemento de papelera: {:?}", e);
error!("Error restoring item from trash: {:?}", e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({
"error": format!("Error restoring item from trash: {}", e)
})))
@@ -207,14 +207,14 @@ pub async fn restore_from_trash(
}
}
/// Elimina permanentemente un elemento de la papelera
/// Permanently deletes an item from the trash
#[instrument(skip_all)]
pub async fn delete_permanently(
State(state): State<AppState>,
auth_user: AuthUser,
Path(trash_id): Path<String>,
) -> (StatusCode, Json<serde_json::Value>) {
debug!("Solicitud para eliminar permanentemente elemento {}", trash_id);
debug!("Request to permanently delete item {}", trash_id);
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
@@ -228,7 +228,7 @@ pub async fn delete_permanently(
match result {
Ok(_) => {
debug!("Elemento eliminado permanentemente");
debug!("Item permanently deleted");
(StatusCode::OK, Json(json!({
"success": true,
"message": "Item deleted permanently"
@@ -245,7 +245,7 @@ pub async fn delete_permanently(
})));
}
error!("Error al eliminar permanentemente elemento: {:?}", e);
error!("Error permanently deleting item: {:?}", e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({
"error": format!("Error deleting item permanently: {}", e)
})))
@@ -253,13 +253,13 @@ pub async fn delete_permanently(
}
}
/// Vacía la papelera completamente para el usuario actual
/// Empties the trash completely for the current user
#[instrument(skip_all)]
pub async fn empty_trash(
State(state): State<AppState>,
auth_user: AuthUser,
) -> (StatusCode, Json<serde_json::Value>) {
debug!("Solicitud para vaciar papelera del usuario {}", auth_user.id);
debug!("Request to empty trash for user {}", auth_user.id);
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
@@ -273,14 +273,14 @@ pub async fn empty_trash(
match result {
Ok(_) => {
debug!("Papelera vaciada con éxito");
debug!("Trash emptied successfully");
(StatusCode::OK, Json(json!({
"success": true,
"message": "Trash emptied successfully"
})))
},
Err(e) => {
error!("Error al vaciar papelera: {:?}", e);
error!("Error emptying trash: {:?}", e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({
"error": format!("Error emptying trash: {}", e)
})))
+6 -6
View File
@@ -90,14 +90,14 @@ pub fn create_api_routes(app_state: &AppState) -> Router<AppState> {
let favorites_service = app_state.favorites_service.clone();
let recent_service = app_state.recent_service.clone();
// Inicializar el servicio de operaciones por lotes
// Initialize the batch operations service
let batch_service = Arc::new(BatchOperationService::default(
file_retrieval_service.clone(),
file_management_service.clone(),
folder_service.clone()
));
// Crear estado para el manejador de operaciones por lotes
// Create state for the batch operations handler
let batch_handler_state = BatchHandlerState {
batch_service: batch_service.clone(),
};
@@ -154,14 +154,14 @@ pub fn create_api_routes(app_state: &AppState) -> Router<AppState> {
// Merge the routers
let files_router = basic_file_router.merge(file_operations_router);
// Crear rutas para operaciones por lotes
// Create routes for batch operations
let batch_router = Router::new()
// Operaciones de archivos
// File operations
.route("/files/move", post(batch_handler::move_files_batch))
.route("/files/copy", post(batch_handler::copy_files_batch))
.route("/files/delete", post(batch_handler::delete_files_batch))
.route("/files/get", post(batch_handler::get_files_batch))
// Operaciones de carpetas
// Folder operations
.route("/folders/delete", post(batch_handler::delete_folders_batch))
.route("/folders/create", post(batch_handler::create_folders_batch))
.route("/folders/get", post(batch_handler::get_folders_batch))
@@ -183,7 +183,7 @@ pub fn create_api_routes(app_state: &AppState) -> Router<AppState> {
Router::new()
};
// Implementaciones directas de handlers para compartir, sin depender de ShareHandler
// Direct handler implementations for sharing, without depending on ShareHandler
// Create routes for shared resources management (requires auth)
let share_router = if let Some(share_service) = share_service.clone() {