adding recent feature + bug fixed
This commit is contained in:
@@ -7,4 +7,5 @@ pub mod trash_dto;
|
||||
pub mod search_dto;
|
||||
pub mod share_dto;
|
||||
pub mod favorites_dto;
|
||||
pub mod recent_dto;
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
/// DTO para elementos recientes
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RecentItemDto {
|
||||
/// Identificador único para el elemento reciente
|
||||
pub id: String,
|
||||
|
||||
/// ID del usuario propietario
|
||||
pub user_id: String,
|
||||
|
||||
/// ID del elemento (archivo o carpeta)
|
||||
pub item_id: String,
|
||||
|
||||
/// Tipo del elemento ('file' o 'folder')
|
||||
pub item_type: String,
|
||||
|
||||
/// Cuándo se accedió al elemento
|
||||
pub accessed_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -5,4 +5,5 @@ pub mod storage_ports;
|
||||
pub mod auth_ports;
|
||||
pub mod trash_ports;
|
||||
pub mod share_ports;
|
||||
pub mod favorites_ports;
|
||||
pub mod favorites_ports;
|
||||
pub mod recent_ports;
|
||||
@@ -0,0 +1,19 @@
|
||||
use async_trait::async_trait;
|
||||
use crate::common::errors::Result;
|
||||
use crate::application::dtos::recent_dto::RecentItemDto;
|
||||
|
||||
/// Define operaciones para gestionar elementos recientes del usuario
|
||||
#[async_trait]
|
||||
pub trait RecentItemsUseCase: Send + Sync {
|
||||
/// Obtener todos los elementos recientes de un usuario
|
||||
async fn get_recent_items(&self, user_id: &str, limit: Option<i32>) -> Result<Vec<RecentItemDto>>;
|
||||
|
||||
/// Registrar acceso a un elemento
|
||||
async fn record_item_access(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<()>;
|
||||
|
||||
/// Eliminar un elemento de recientes
|
||||
async fn remove_from_recent(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<bool>;
|
||||
|
||||
/// Limpiar toda la lista de elementos recientes
|
||||
async fn clear_recent_items(&self, user_id: &str) -> Result<()>;
|
||||
}
|
||||
@@ -14,6 +14,7 @@ pub mod trash_service;
|
||||
pub mod search_service;
|
||||
pub mod share_service;
|
||||
pub mod favorites_service;
|
||||
pub mod recent_service;
|
||||
|
||||
#[cfg(test)]
|
||||
mod trash_service_test;
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use sqlx::{PgPool, Row};
|
||||
use tracing::{info, error};
|
||||
use uuid::Uuid;
|
||||
use crate::common::errors::{Result, DomainError, ErrorKind};
|
||||
use crate::application::ports::recent_ports::RecentItemsUseCase;
|
||||
use crate::application::dtos::recent_dto::RecentItemDto;
|
||||
|
||||
/// Implementación del caso de uso para gestionar elementos recientes
|
||||
pub struct RecentService {
|
||||
db_pool: Arc<PgPool>,
|
||||
max_recent_items: i32, // Número máximo de elementos recientes a mantener por usuario
|
||||
}
|
||||
|
||||
impl RecentService {
|
||||
/// Crear un nuevo servicio de elementos recientes
|
||||
pub fn new(db_pool: Arc<PgPool>, max_recent_items: i32) -> Self {
|
||||
Self {
|
||||
db_pool,
|
||||
max_recent_items: max_recent_items.max(1).min(100), // Entre 1 y 100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl RecentItemsUseCase for RecentService {
|
||||
/// Obtener elementos recientes de un usuario
|
||||
async fn get_recent_items(&self, user_id: &str, limit: Option<i32>) -> Result<Vec<RecentItemDto>> {
|
||||
info!("Obteniendo elementos recientes para usuario: {}", user_id);
|
||||
|
||||
// Convertir user_id a UUID
|
||||
let user_uuid = Uuid::parse_str(user_id)?;
|
||||
|
||||
// Determinar límite (usar el especificado o el máximo del servicio)
|
||||
let limit_value = limit.unwrap_or(self.max_recent_items).min(self.max_recent_items);
|
||||
|
||||
// Ejecutar consulta SQL
|
||||
let rows = sqlx::query(
|
||||
r#"
|
||||
SELECT
|
||||
id::TEXT as "id",
|
||||
user_id::TEXT as "user_id",
|
||||
item_id as "item_id",
|
||||
item_type as "item_type",
|
||||
accessed_at as "accessed_at"
|
||||
FROM auth.user_recent_files
|
||||
WHERE user_id = $1
|
||||
ORDER BY accessed_at DESC
|
||||
LIMIT $2
|
||||
"#
|
||||
)
|
||||
.bind(user_uuid)
|
||||
.bind(limit_value)
|
||||
.fetch_all(&*self.db_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Error de base de datos al obtener elementos recientes: {}", e);
|
||||
DomainError::new(
|
||||
ErrorKind::InternalError,
|
||||
"RecentItems",
|
||||
format!("Fallo al obtener elementos recientes: {}", e)
|
||||
)
|
||||
})?;
|
||||
|
||||
// Convertir filas a DTOs
|
||||
let mut recent_items = Vec::with_capacity(rows.len());
|
||||
for row in rows {
|
||||
recent_items.push(RecentItemDto {
|
||||
id: row.get("id"),
|
||||
user_id: row.get("user_id"),
|
||||
item_id: row.get("item_id"),
|
||||
item_type: row.get("item_type"),
|
||||
accessed_at: row.get("accessed_at"),
|
||||
});
|
||||
}
|
||||
|
||||
info!("Recuperados {} elementos recientes para usuario {}", recent_items.len(), user_id);
|
||||
Ok(recent_items)
|
||||
}
|
||||
|
||||
/// Registrar acceso a un elemento
|
||||
async fn record_item_access(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<()> {
|
||||
info!("Registrando acceso a {} '{}' para usuario {}", item_type, item_id, user_id);
|
||||
|
||||
// Validar tipo de elemento
|
||||
if item_type != "file" && item_type != "folder" {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"RecentItems",
|
||||
"El tipo de elemento debe ser 'file' o 'folder'"
|
||||
));
|
||||
}
|
||||
|
||||
// Convertir user_id a UUID
|
||||
let user_uuid = Uuid::parse_str(user_id)?;
|
||||
|
||||
// Ejecutar consulta SQL con UPSERT para mantener un único registro por elemento
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO auth.user_recent_files (user_id, item_id, item_type, accessed_at)
|
||||
VALUES ($1, $2, $3, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (user_id, item_id, item_type)
|
||||
DO UPDATE SET accessed_at = CURRENT_TIMESTAMP
|
||||
"#
|
||||
)
|
||||
.bind(user_uuid)
|
||||
.bind(item_id)
|
||||
.bind(item_type)
|
||||
.execute(&*self.db_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Error de base de datos al registrar acceso a elemento: {}", e);
|
||||
DomainError::new(
|
||||
ErrorKind::InternalError,
|
||||
"RecentItems",
|
||||
format!("Fallo al registrar acceso a elemento: {}", e)
|
||||
)
|
||||
})?;
|
||||
|
||||
// Eliminar elementos antiguos que excedan el límite
|
||||
self.prune_old_items(user_id).await?;
|
||||
|
||||
info!("Registrado correctamente acceso a {} '{}' para usuario {}", item_type, item_id, user_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Eliminar un elemento de recientes
|
||||
async fn remove_from_recent(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<bool> {
|
||||
info!("Eliminando {} '{}' de recientes para usuario {}", item_type, item_id, user_id);
|
||||
|
||||
// Convertir user_id a UUID
|
||||
let user_uuid = Uuid::parse_str(user_id)?;
|
||||
|
||||
// Ejecutar consulta SQL
|
||||
let result = sqlx::query(
|
||||
r#"
|
||||
DELETE FROM auth.user_recent_files
|
||||
WHERE user_id = $1 AND item_id = $2 AND item_type = $3
|
||||
"#
|
||||
)
|
||||
.bind(user_uuid)
|
||||
.bind(item_id)
|
||||
.bind(item_type)
|
||||
.execute(&*self.db_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Error de base de datos al eliminar elemento de recientes: {}", e);
|
||||
DomainError::new(
|
||||
ErrorKind::InternalError,
|
||||
"RecentItems",
|
||||
format!("Fallo al eliminar de recientes: {}", e)
|
||||
)
|
||||
})?;
|
||||
|
||||
let removed = result.rows_affected() > 0;
|
||||
info!(
|
||||
"{} {} '{}' de recientes para usuario {}",
|
||||
if removed { "Eliminado correctamente" } else { "No se encontró" },
|
||||
item_type,
|
||||
item_id,
|
||||
user_id
|
||||
);
|
||||
|
||||
Ok(removed)
|
||||
}
|
||||
|
||||
/// Limpiar todos los elementos recientes
|
||||
async fn clear_recent_items(&self, user_id: &str) -> Result<()> {
|
||||
info!("Limpiando todos los elementos recientes para usuario {}", user_id);
|
||||
|
||||
// Convertir user_id a UUID
|
||||
let user_uuid = Uuid::parse_str(user_id)?;
|
||||
|
||||
// Ejecutar consulta SQL
|
||||
sqlx::query(
|
||||
r#"
|
||||
DELETE FROM auth.user_recent_files
|
||||
WHERE user_id = $1
|
||||
"#
|
||||
)
|
||||
.bind(user_uuid)
|
||||
.execute(&*self.db_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Error de base de datos al limpiar elementos recientes: {}", e);
|
||||
DomainError::new(
|
||||
ErrorKind::InternalError,
|
||||
"RecentItems",
|
||||
format!("Fallo al limpiar elementos recientes: {}", e)
|
||||
)
|
||||
})?;
|
||||
|
||||
info!("Limpiados todos los elementos recientes para usuario {}", user_id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl RecentService {
|
||||
/// Método auxiliar para eliminar elementos antiguos que excedan el límite
|
||||
async fn prune_old_items(&self, user_id: &str) -> Result<()> {
|
||||
// Convertir user_id a UUID
|
||||
let user_uuid = Uuid::parse_str(user_id)?;
|
||||
|
||||
// Eliminar elementos antiguos que excedan el límite
|
||||
sqlx::query(
|
||||
r#"
|
||||
DELETE FROM auth.user_recent_files
|
||||
WHERE id IN (
|
||||
SELECT id FROM auth.user_recent_files
|
||||
WHERE user_id = $1
|
||||
ORDER BY accessed_at DESC
|
||||
OFFSET $2
|
||||
)
|
||||
"#
|
||||
)
|
||||
.bind(user_uuid)
|
||||
.bind(self.max_recent_items)
|
||||
.execute(&*self.db_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Error al podar elementos recientes antiguos: {}", e);
|
||||
DomainError::new(
|
||||
ErrorKind::InternalError,
|
||||
"RecentItems",
|
||||
format!("Fallo al limpiar elementos recientes antiguos: {}", e)
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -22,6 +22,7 @@ use crate::application::services::storage_mediator::{StorageMediator, FileSystem
|
||||
use crate::application::ports::inbound::{FileUseCase, FolderUseCase, SearchUseCase};
|
||||
use crate::application::ports::outbound::{FileStoragePort, FolderStoragePort};
|
||||
use crate::application::ports::favorites_ports::FavoritesUseCase;
|
||||
use crate::application::ports::recent_ports::RecentItemsUseCase;
|
||||
use crate::application::ports::file_ports::{FileUploadUseCase, FileRetrievalUseCase, FileManagementUseCase, FileUseCaseFactory};
|
||||
use crate::application::ports::storage_ports::{FileReadPort, FileWritePort};
|
||||
use crate::infrastructure::repositories::{FileMetadataManager, FilePathResolver, FileFsReadRepository, FileFsWriteRepository};
|
||||
@@ -241,7 +242,8 @@ impl AppServiceFactory {
|
||||
trash_service,
|
||||
search_service,
|
||||
share_service: None, // No share service by default
|
||||
favorites_service: None // No favorites service by default
|
||||
favorites_service: None, // No favorites service by default
|
||||
recent_service: None // No recent service by default
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,6 +288,7 @@ pub struct ApplicationServices {
|
||||
pub search_service: Option<Arc<dyn SearchUseCase>>,
|
||||
pub share_service: Option<Arc<dyn crate::application::ports::share_ports::ShareUseCase>>,
|
||||
pub favorites_service: Option<Arc<dyn FavoritesUseCase>>,
|
||||
pub recent_service: Option<Arc<dyn RecentItemsUseCase>>,
|
||||
}
|
||||
|
||||
/// Contenedor para servicios de autenticación
|
||||
@@ -307,6 +310,7 @@ pub struct AppState {
|
||||
pub trash_service: Option<Arc<dyn TrashUseCase>>,
|
||||
pub share_service: Option<Arc<dyn crate::application::ports::share_ports::ShareUseCase>>,
|
||||
pub favorites_service: Option<Arc<dyn FavoritesUseCase>>,
|
||||
pub recent_service: Option<Arc<dyn RecentItemsUseCase>>,
|
||||
}
|
||||
|
||||
impl Default for AppState {
|
||||
@@ -777,6 +781,7 @@ impl Default for AppState {
|
||||
search_service: Some(Arc::new(DummySearchUseCase) as Arc<dyn crate::application::ports::inbound::SearchUseCase>),
|
||||
share_service: None, // No share service in minimal mode
|
||||
favorites_service: None, // No favorites service in minimal mode
|
||||
recent_service: None, // No recent service in minimal mode
|
||||
};
|
||||
|
||||
// Return a minimal app state
|
||||
@@ -789,6 +794,7 @@ impl Default for AppState {
|
||||
trash_service: None,
|
||||
share_service: None,
|
||||
favorites_service: None,
|
||||
recent_service: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -808,6 +814,7 @@ impl AppState {
|
||||
trash_service: None,
|
||||
share_service: None,
|
||||
favorites_service: None,
|
||||
recent_service: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,4 +842,9 @@ impl AppState {
|
||||
self.favorites_service = Some(favorites_service);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_recent_service(mut self, recent_service: Arc<dyn RecentItemsUseCase>) -> Self {
|
||||
self.recent_service = Some(recent_service);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ pub mod trash_handler;
|
||||
pub mod search_handler;
|
||||
pub mod share_handler;
|
||||
pub mod favorites_handler;
|
||||
pub mod recent_handler;
|
||||
|
||||
/// Tipo de resultado para controladores de API
|
||||
pub type ApiResult<T> = Result<T, (axum::http::StatusCode, String)>;
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
use std::sync::Arc;
|
||||
use axum::{
|
||||
extract::{Path, State, Query},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
Json,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::application::ports::recent_ports::RecentItemsUseCase;
|
||||
|
||||
/// Parámetros de consulta para obtener elementos recientes
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetRecentParams {
|
||||
#[serde(default)]
|
||||
limit: Option<i32>,
|
||||
}
|
||||
|
||||
/// Obtener elementos recientes del usuario
|
||||
pub async fn get_recent_items(
|
||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
||||
Query(params): Query<GetRecentParams>,
|
||||
) -> impl IntoResponse {
|
||||
// Para pruebas, usando ID de usuario fijo
|
||||
let user_id = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
match recent_service.get_recent_items(user_id, params.limit).await {
|
||||
Ok(items) => {
|
||||
info!("Recuperados {} elementos recientes para usuario", items.len());
|
||||
(StatusCode::OK, Json(items)).into_response()
|
||||
},
|
||||
Err(err) => {
|
||||
error!("Error al recuperar elementos recientes: {}", err);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Fallo al recuperar elementos recientes: {}", err)
|
||||
}))
|
||||
).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Registrar acceso a un elemento
|
||||
pub async fn record_item_access(
|
||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
||||
Path((item_type, item_id)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
// Para pruebas, usando ID de usuario fijo
|
||||
let user_id = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
// Validar tipo de elemento
|
||||
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'"
|
||||
}))
|
||||
).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);
|
||||
(
|
||||
StatusCode::OK,
|
||||
Json(serde_json::json!({
|
||||
"message": "Acceso registrado correctamente"
|
||||
}))
|
||||
).into_response()
|
||||
},
|
||||
Err(err) => {
|
||||
error!("Error al registrar acceso en recientes: {}", err);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Fallo al registrar acceso: {}", err)
|
||||
}))
|
||||
).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Eliminar un elemento de recientes
|
||||
pub async fn remove_from_recent(
|
||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
||||
Path((item_type, item_id)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
// Para pruebas, usando ID de usuario fijo
|
||||
let user_id = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
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);
|
||||
(
|
||||
StatusCode::OK,
|
||||
Json(serde_json::json!({
|
||||
"message": "Elemento eliminado de recientes"
|
||||
}))
|
||||
).into_response()
|
||||
} else {
|
||||
info!("Elemento {} '{}' no estaba en recientes", item_type, item_id);
|
||||
(
|
||||
StatusCode::NOT_FOUND,
|
||||
Json(serde_json::json!({
|
||||
"message": "Elemento no estaba en recientes"
|
||||
}))
|
||||
).into_response()
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
error!("Error al eliminar de recientes: {}", err);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Fallo al eliminar de recientes: {}", err)
|
||||
}))
|
||||
).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Limpiar todos los elementos recientes
|
||||
pub async fn clear_recent_items(
|
||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
||||
) -> impl IntoResponse {
|
||||
// Para pruebas, usando ID de usuario fijo
|
||||
let user_id = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
match recent_service.clear_recent_items(user_id).await {
|
||||
Ok(_) => {
|
||||
info!("Limpiados todos los elementos recientes para usuario");
|
||||
(
|
||||
StatusCode::OK,
|
||||
Json(serde_json::json!({
|
||||
"message": "Elementos recientes limpiados correctamente"
|
||||
}))
|
||||
).into_response()
|
||||
},
|
||||
Err(err) => {
|
||||
error!("Error al limpiar elementos recientes: {}", err);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Fallo al limpiar elementos recientes: {}", err)
|
||||
}))
|
||||
).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ use crate::application::ports::trash_ports::TrashUseCase;
|
||||
use crate::application::ports::inbound::SearchUseCase;
|
||||
use crate::application::ports::share_ports::ShareUseCase;
|
||||
use crate::application::ports::favorites_ports::FavoritesUseCase;
|
||||
use crate::application::ports::recent_ports::RecentItemsUseCase;
|
||||
|
||||
use crate::interfaces::api::handlers::folder_handler::FolderHandler;
|
||||
use crate::interfaces::api::handlers::file_handler::FileHandler;
|
||||
@@ -45,6 +46,7 @@ pub fn create_api_routes(
|
||||
search_service: Option<Arc<dyn SearchUseCase>>,
|
||||
share_service: Option<Arc<dyn ShareUseCase>>,
|
||||
favorites_service: Option<Arc<dyn FavoritesUseCase>>,
|
||||
recent_service: Option<Arc<dyn RecentItemsUseCase>>,
|
||||
) -> Router<crate::common::di::AppState> {
|
||||
// Create a simplified AppState for the trash view
|
||||
// Setup required components for repository construction
|
||||
@@ -109,12 +111,14 @@ pub fn create_api_routes(
|
||||
search_service: search_service.clone(), // Include the search service
|
||||
share_service: share_service.clone(), // Include the share service
|
||||
favorites_service: favorites_service.clone(), // Include the favorites service
|
||||
recent_service: recent_service.clone(), // Include the recent service
|
||||
},
|
||||
db_pool: None,
|
||||
auth_service: None,
|
||||
trash_service: trash_service.clone(), // This is the important part - include the trash service
|
||||
share_service: share_service.clone(), // Include the share service for routes
|
||||
favorites_service: favorites_service.clone() // Include the favorites service for routes
|
||||
favorites_service: favorites_service.clone(), // Include the favorites service for routes
|
||||
recent_service: recent_service.clone() // Include the recent service for routes
|
||||
};
|
||||
// Inicializar el servicio de operaciones por lotes
|
||||
let batch_service = Arc::new(BatchOperationService::default(
|
||||
@@ -340,6 +344,20 @@ pub fn create_api_routes(
|
||||
} else {
|
||||
Router::new()
|
||||
};
|
||||
|
||||
// Create routes for recent items if the service is available
|
||||
let recent_router = if let Some(recent_service) = recent_service.clone() {
|
||||
use crate::interfaces::api::handlers::recent_handler;
|
||||
|
||||
Router::new()
|
||||
.route("/", get(recent_handler::get_recent_items))
|
||||
.route("/{item_type}/{item_id}", post(recent_handler::record_item_access))
|
||||
.route("/{item_type}/{item_id}", delete(recent_handler::remove_from_recent))
|
||||
.route("/clear", delete(recent_handler::clear_recent_items))
|
||||
.with_state(recent_service.clone())
|
||||
} else {
|
||||
Router::new()
|
||||
};
|
||||
|
||||
let mut router = Router::new()
|
||||
.nest("/folders", folders_router)
|
||||
@@ -349,6 +367,7 @@ pub fn create_api_routes(
|
||||
.nest("/shares", share_router)
|
||||
.nest("/s", public_share_router)
|
||||
.nest("/favorites", favorites_router)
|
||||
.nest("/recent", recent_router)
|
||||
;
|
||||
|
||||
// Store the share service in app_state for future use
|
||||
|
||||
+23
-1
@@ -629,6 +629,22 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing::info!("Favorites service is disabled (requires database connection)");
|
||||
None
|
||||
};
|
||||
|
||||
// Initialize recent items service if database is available
|
||||
let recent_service: Option<Arc<dyn application::ports::recent_ports::RecentItemsUseCase>> =
|
||||
if let Some(ref pool) = db_pool {
|
||||
// Create a new service with the database pool
|
||||
let service = Arc::new(application::services::recent_service::RecentService::new(
|
||||
pool.clone(),
|
||||
50 // Maximum recent items per user
|
||||
));
|
||||
|
||||
tracing::info!("Recent items service initialized successfully");
|
||||
Some(service)
|
||||
} else {
|
||||
tracing::info!("Recent items service is disabled (requires database connection)");
|
||||
None
|
||||
};
|
||||
|
||||
let application_services = common::di::ApplicationServices {
|
||||
folder_service: folder_service.clone(),
|
||||
@@ -642,6 +658,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
search_service: search_service.clone(),
|
||||
share_service: share_service.clone(),
|
||||
favorites_service: favorites_service.clone(),
|
||||
recent_service: recent_service.clone()
|
||||
};
|
||||
|
||||
// Create the AppState without Arc first
|
||||
@@ -667,11 +684,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
app_state = app_state.with_favorites_service(service);
|
||||
}
|
||||
|
||||
// Add recent service if available
|
||||
if let Some(service) = recent_service.clone() {
|
||||
app_state = app_state.with_recent_service(service);
|
||||
}
|
||||
|
||||
// Wrap in Arc after all modifications
|
||||
let app_state = Arc::new(app_state);
|
||||
|
||||
// Build application router
|
||||
let api_routes = create_api_routes(folder_service, file_service, Some(i18n_service), trash_service, search_service, share_service, favorites_service);
|
||||
let api_routes = create_api_routes(folder_service, file_service, Some(i18n_service), trash_service, search_service, share_service, favorites_service, recent_service);
|
||||
let web_routes = create_web_routes();
|
||||
|
||||
// Build the app router
|
||||
|
||||
Reference in New Issue
Block a user