fix trash and additional bugs

This commit is contained in:
DioCrafts
2025-03-26 18:33:22 +01:00
parent af70581f91
commit e22c0ac855
43 changed files with 2487 additions and 436 deletions
+1 -2
View File
@@ -2,10 +2,9 @@ use std::sync::Arc;
use axum::{
Router,
routing::{post, get, put},
extract::{State, Json, Path, Extension},
extract::{State, Json, Extension},
http::{StatusCode, HeaderMap, header},
response::IntoResponse,
middleware,
};
use crate::common::di::AppState;
+51 -35
View File
@@ -11,16 +11,15 @@ use futures::Stream;
use futures::StreamExt;
use std::task::{Context, Poll};
use std::pin::Pin;
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use std::path::PathBuf;
use crate::application::services::file_service::{FileService, FileServiceError};
use crate::infrastructure::services::compression_service::{
CompressionService, GzipCompressionService, CompressionLevel
};
use crate::common::di::AppState;
type AppState = Arc<FileService>;
type FileServiceState = Arc<FileService>;
type GlobalState = AppState;
/// Handler for file-related API endpoints
pub struct FileHandler;
@@ -57,7 +56,7 @@ impl<T> BoxedStream<T> {
impl FileHandler {
/// Uploads a file
pub async fn upload_file(
State(service): State<AppState>,
State(service): State<FileServiceState>,
mut multipart: Multipart,
) -> impl IntoResponse {
// Extract file from multipart request
@@ -131,7 +130,7 @@ impl FileHandler {
/// Downloads a file with optional compression
pub async fn download_file(
State(service): State<AppState>,
State(service): State<FileServiceState>,
Path(id): Path<String>,
Query(params): Query<HashMap<String, String>>,
) -> impl IntoResponse {
@@ -375,7 +374,7 @@ impl FileHandler {
/// Lists files, optionally filtered by folder ID
pub async fn list_files(
State(service): State<AppState>,
State(service): State<FileServiceState>,
folder_id: Option<&str>,
) -> impl IntoResponse {
tracing::info!("Listing files with folder_id: {:?}", folder_id);
@@ -399,10 +398,7 @@ impl FileHandler {
Err(err) => {
tracing::error!("Error listing files through service: {}", err);
let status = match &err {
FileServiceError::NotFound(_) => StatusCode::NOT_FOUND,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
let status = StatusCode::INTERNAL_SERVER_ERROR;
// Return a JSON error response
(status, Json(serde_json::json!({
@@ -412,22 +408,54 @@ impl FileHandler {
}
}
/// Deletes a file
/// Deletes a file (with trash support)
pub async fn delete_file(
State(service): State<AppState>,
State(state): State<GlobalState>,
Path(id): Path<String>,
) -> impl IntoResponse {
// Use the file service to delete the file
match service.delete_file(&id).await {
// Check if trash service is available
if let Some(trash_service) = &state.trash_service {
tracing::info!("Moving file to trash: {}", id);
// Debug logs to track trash components
tracing::debug!("Trash service type: {}", std::any::type_name_of_val(&*trash_service));
let default_user_id = "00000000-0000-0000-0000-000000000000".to_string();
tracing::info!("Using default user ID: {}", default_user_id);
// Try to move to trash first - add more detailed logging
tracing::info!("About to call trash_service.move_to_trash with id={}, type=file", id);
match trash_service.move_to_trash(&id, "file", &default_user_id).await {
Ok(_) => {
tracing::info!("File successfully moved to trash: {}", id);
// Note: Use 204 No Content for consistency with DELETE operations
return StatusCode::NO_CONTENT.into_response();
},
Err(err) => {
tracing::error!("Could not move file to trash: {:?}", err);
tracing::error!("Error kind: {:?}, Error details: {}", err.kind, err);
tracing::warn!("Could not move file to trash, falling back to permanent delete: {}", err);
// Fall through to regular delete if trash fails
}
}
} else {
tracing::warn!("Trash service not available, using permanent delete");
}
// Fallback to permanent delete if trash is unavailable or failed
tracing::warn!("Falling back to permanent delete for file: {}", id);
let file_service = &state.applications.file_service;
match file_service.delete_file(&id).await {
Ok(_) => {
tracing::info!("File successfully deleted: {}", id);
tracing::info!("File permanently deleted: {}", id);
// CRITICAL FIX: Return status code that matches the API expectations (204 No Content)
// This ensures the client knows the operation was successful
StatusCode::NO_CONTENT.into_response()
},
Err(err) => {
tracing::error!("Error deleting file: {}", err);
let status = match &err {
FileServiceError::NotFound(_) => StatusCode::NOT_FOUND,
let status = match err.kind {
crate::common::errors::ErrorKind::NotFound => StatusCode::NOT_FOUND,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
@@ -440,7 +468,7 @@ impl FileHandler {
/// Moves a file to a different folder
pub async fn move_file(
State(service): State<AppState>,
State(service): State<FileServiceState>,
Path(id): Path<String>,
Json(payload): Json<MoveFilePayload>,
) -> impl IntoResponse {
@@ -463,24 +491,12 @@ impl FileHandler {
(StatusCode::OK, Json(file)).into_response()
},
Err(err) => {
let status = match &err {
FileServiceError::NotFound(_) => {
tracing::error!("Error moving file - not found: {}", err);
StatusCode::NOT_FOUND
},
FileServiceError::Conflict(_) => {
tracing::error!("Error moving file - already exists: {}", err);
StatusCode::CONFLICT
},
_ => {
tracing::error!("Error moving file: {}", err);
StatusCode::INTERNAL_SERVER_ERROR
}
};
// Simplify error handling
let status = StatusCode::INTERNAL_SERVER_ERROR;
tracing::error!("Error moving file: {}", err);
(status, Json(serde_json::json!({
"error": format!("Error moving file: {}", err.to_string()),
"code": status.as_u16()
"error": format!("Error moving file: {}", err)
}))).into_response()
}
}
+49 -1
View File
@@ -11,6 +11,8 @@ use crate::application::dtos::folder_dto::{CreateFolderDto, RenameFolderDto, Mov
use crate::application::dtos::pagination::PaginationRequestDto;
use crate::common::errors::ErrorKind;
use crate::application::ports::inbound::FolderUseCase;
use crate::common::di::AppState as GlobalAppState;
use crate::interfaces::middleware::auth::AuthUser;
type AppState = Arc<FolderService>;
@@ -148,11 +150,12 @@ impl FolderHandler {
}
}
/// Deletes a folder
/// Deletes a folder (with trash support)
pub async fn delete_folder(
State(service): State<AppState>,
Path(id): Path<String>,
) -> impl IntoResponse {
// For folder deletion without trash functionality
match service.delete_folder(&id).await {
Ok(_) => StatusCode::NO_CONTENT.into_response(),
Err(err) => {
@@ -165,4 +168,49 @@ impl FolderHandler {
}
}
}
/// Deletes a folder with trash functionality
pub async fn delete_folder_with_trash(
State(state): State<GlobalAppState>,
auth_user: AuthUser,
Path(id): Path<String>,
) -> impl IntoResponse {
// Check if trash service is available
if let Some(trash_service) = &state.trash_service {
tracing::info!("Moving folder to trash: {}", id);
// Try to move to trash first
match trash_service.move_to_trash(&id, "folder", &"00000000-0000-0000-0000-000000000000".to_string()).await {
Ok(_) => {
tracing::info!("Folder successfully moved to trash: {}", id);
return StatusCode::NO_CONTENT.into_response();
},
Err(err) => {
tracing::warn!("Could not move folder to trash, falling back to permanent delete: {}", err);
// Fall through to regular delete if trash fails
}
}
}
// Fallback to permanent delete if trash is unavailable or failed
let folder_service = &state.applications.folder_service;
match folder_service.delete_folder(&id).await {
Ok(_) => {
tracing::info!("Folder permanently deleted: {}", id);
StatusCode::NO_CONTENT.into_response()
},
Err(err) => {
tracing::error!("Error deleting folder: {}", err);
let status = match err.kind {
ErrorKind::NotFound => StatusCode::NOT_FOUND,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
(status, Json(serde_json::json!({
"error": format!("Error deleting folder: {}", err)
}))).into_response()
}
}
}
}
@@ -1,6 +1,5 @@
use axum::extract::{Path, State};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Json;
use serde_json::json;
use tracing::{debug, error, instrument};