Files
Oxicloud/src/interfaces/api/handlers/trash_handler.rs
T

327 lines
9.4 KiB
Rust
Raw Normal View History

2026-02-14 01:29:34 +01:00
use axum::Json;
use axum::extract::{Path, State};
use axum::http::StatusCode;
use serde_json::json;
2026-02-14 01:29:34 +01:00
use tracing::{debug, error, instrument, warn};
use crate::application::ports::trash_ports::TrashUseCase;
use crate::common::di::AppState;
use crate::interfaces::middleware::auth::AuthUser;
use std::sync::Arc;
/// Gets all items in the trash for the current user
2025-03-24 17:49:53 +01:00
#[instrument(skip_all)]
pub async fn get_trash_items(
State(state): State<Arc<AppState>>,
auth_user: AuthUser,
2025-03-24 17:49:53 +01:00
) -> (StatusCode, Json<serde_json::Value>) {
// SECURITY: Always use the authenticated user's ID from the JWT token.
// Never allow user ID override via query parameters to prevent
// privilege escalation attacks.
let effective_user = auth_user.id.clone();
debug!("Request to list trash items for user {}", effective_user);
2026-02-14 01:29:34 +01:00
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
None => {
2026-02-14 01:29:34 +01:00
return (
StatusCode::NOT_IMPLEMENTED,
Json(json!({
"error": "Trash feature is not enabled"
})),
);
}
};
2026-02-14 01:29:34 +01:00
let result = trash_service.get_trash_items(&effective_user).await;
2026-02-14 01:29:34 +01:00
match result {
Ok(items) => {
debug!("Found {} items in trash", items.len());
2025-03-24 17:49:53 +01:00
(StatusCode::OK, Json(json!(items)))
2026-02-14 01:29:34 +01:00
}
Err(e) => {
error!("Error retrieving trash items: {:?}", e);
2026-02-14 01:29:34 +01:00
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
"error": "Error retrieving trash items"
2026-02-14 01:29:34 +01:00
})),
)
2025-03-24 17:49:53 +01:00
}
}
}
/// Moves a file to the trash
2025-03-24 17:49:53 +01:00
#[instrument(skip_all)]
pub async fn move_file_to_trash(
State(state): State<Arc<AppState>>,
auth_user: AuthUser,
2025-03-24 17:49:53 +01:00
Path(item_id): Path<String>,
) -> (StatusCode, Json<serde_json::Value>) {
let user_id = &auth_user.id;
2026-02-14 01:29:34 +01:00
debug!(
"Request to move file to trash: id={}, user={}",
item_id, user_id
);
2025-03-24 17:49:53 +01:00
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
None => {
2026-02-14 01:29:34 +01:00
return (
StatusCode::NOT_IMPLEMENTED,
Json(json!({
"error": "Trash feature is not enabled"
})),
);
2025-03-24 17:49:53 +01:00
}
};
2026-02-14 01:29:34 +01:00
// Specify that it is a file
let result = trash_service.move_to_trash(&item_id, "file", user_id).await;
2026-02-14 01:29:34 +01:00
2025-03-24 17:49:53 +01:00
match result {
Ok(_) => {
debug!("File moved to trash successfully");
2026-02-14 01:29:34 +01:00
(
StatusCode::OK,
Json(json!({
"success": true,
"message": "File moved to trash successfully"
})),
)
}
2025-03-24 17:49:53 +01:00
Err(e) => {
error!("Error moving file to trash: {:?}", e);
2026-02-14 01:29:34 +01:00
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
"error": "Error moving file to trash"
2026-02-14 01:29:34 +01:00
})),
)
2025-03-24 17:49:53 +01:00
}
}
}
/// Moves a folder to the trash
2025-03-24 17:49:53 +01:00
#[instrument(skip_all)]
pub async fn move_folder_to_trash(
State(state): State<Arc<AppState>>,
auth_user: AuthUser,
2025-03-24 17:49:53 +01:00
Path(item_id): Path<String>,
) -> (StatusCode, Json<serde_json::Value>) {
let user_id = &auth_user.id;
2026-02-14 01:29:34 +01:00
debug!(
"Request to move folder to trash: id={}, user={}",
item_id, user_id
);
2025-03-24 17:49:53 +01:00
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
None => {
2026-02-14 01:29:34 +01:00
return (
StatusCode::NOT_IMPLEMENTED,
Json(json!({
"error": "Trash feature is not enabled"
})),
);
2025-03-24 17:49:53 +01:00
}
};
2026-02-14 01:29:34 +01:00
// Specify that it is a folder
2026-02-14 01:29:34 +01:00
let result = trash_service
.move_to_trash(&item_id, "folder", user_id)
.await;
2025-03-24 17:49:53 +01:00
match result {
Ok(_) => {
debug!("Folder moved to trash successfully");
2026-02-14 01:29:34 +01:00
(
StatusCode::OK,
Json(json!({
"success": true,
"message": "Folder moved to trash successfully"
})),
)
}
2025-03-24 17:49:53 +01:00
Err(e) => {
error!("Error moving folder to trash: {:?}", e);
2026-02-14 01:29:34 +01:00
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
"error": "Error moving folder to trash"
2026-02-14 01:29:34 +01:00
})),
)
}
}
}
/// Restores an item from the trash to its original location
2025-03-24 17:49:53 +01:00
#[instrument(skip_all)]
pub async fn restore_from_trash(
State(state): State<Arc<AppState>>,
auth_user: AuthUser,
Path(trash_id): Path<String>,
2025-03-24 17:49:53 +01:00
) -> (StatusCode, Json<serde_json::Value>) {
debug!("Request to restore item {} from trash", trash_id);
2026-02-14 01:29:34 +01:00
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
None => {
2026-02-14 01:29:34 +01:00
return (
StatusCode::NOT_IMPLEMENTED,
Json(json!({
"error": "Trash feature is not enabled"
})),
);
}
};
let result = trash_service.restore_item(&trash_id, &auth_user.id).await;
2026-02-14 01:29:34 +01:00
match result {
Ok(_) => {
debug!("Item restored successfully");
2026-02-14 01:29:34 +01:00
(
StatusCode::OK,
Json(json!({
"success": true,
"message": "Item restored successfully"
})),
)
}
Err(e) => {
let err_str = format!("{}", e);
// If item not found, report success (it was already restored or removed)
if err_str.contains("not found") || err_str.contains("NotFound") {
2026-02-14 01:29:34 +01:00
warn!(
"Item not found in trash, but reporting success: {}",
trash_id
);
return (
StatusCode::OK,
Json(json!({
"success": true,
"message": "Item restored (or was already removed from trash)"
})),
);
}
error!("Error restoring item from trash: {:?}", e);
2026-02-14 01:29:34 +01:00
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
"error": "Error restoring item from trash"
2026-02-14 01:29:34 +01:00
})),
)
}
}
}
/// Permanently deletes an item from the trash
2025-03-24 17:49:53 +01:00
#[instrument(skip_all)]
pub async fn delete_permanently(
State(state): State<Arc<AppState>>,
auth_user: AuthUser,
Path(trash_id): Path<String>,
2025-03-24 17:49:53 +01:00
) -> (StatusCode, Json<serde_json::Value>) {
debug!("Request to permanently delete item {}", trash_id);
2026-02-14 01:29:34 +01:00
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
None => {
2026-02-14 01:29:34 +01:00
return (
StatusCode::NOT_IMPLEMENTED,
Json(json!({
"error": "Trash feature is not enabled"
})),
);
}
};
2026-02-14 01:29:34 +01:00
let result = trash_service
.delete_permanently(&trash_id, &auth_user.id)
.await;
match result {
Ok(_) => {
debug!("Item permanently deleted");
2026-02-14 01:29:34 +01:00
(
StatusCode::OK,
Json(json!({
"success": true,
"message": "Item deleted permanently"
})),
)
}
Err(e) => {
let err_str = format!("{}", e);
// If item not found, report success (it was already deleted)
if err_str.contains("not found") || err_str.contains("NotFound") {
2026-02-14 01:29:34 +01:00
warn!(
"Item not found in trash, but reporting success: {}",
trash_id
);
return (
StatusCode::OK,
Json(json!({
"success": true,
"message": "Item deleted (or was already removed from trash)"
})),
);
}
error!("Error permanently deleting item: {:?}", e);
2026-02-14 01:29:34 +01:00
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
"error": "Error deleting item permanently"
2026-02-14 01:29:34 +01:00
})),
)
}
}
}
/// Empties the trash completely for the current user
2025-03-24 17:49:53 +01:00
#[instrument(skip_all)]
pub async fn empty_trash(
State(state): State<Arc<AppState>>,
auth_user: AuthUser,
2025-03-24 17:49:53 +01:00
) -> (StatusCode, Json<serde_json::Value>) {
debug!("Request to empty trash for user {}", auth_user.id);
2026-02-14 01:29:34 +01:00
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
None => {
2026-02-14 01:29:34 +01:00
return (
StatusCode::NOT_IMPLEMENTED,
Json(json!({
"error": "Trash feature is not enabled"
})),
);
}
};
let result = trash_service.empty_trash(&auth_user.id).await;
2026-02-14 01:29:34 +01:00
match result {
Ok(_) => {
debug!("Trash emptied successfully");
2026-02-14 01:29:34 +01:00
(
StatusCode::OK,
Json(json!({
"success": true,
"message": "Trash emptied successfully"
})),
)
}
Err(e) => {
error!("Error emptying trash: {:?}", e);
2026-02-14 01:29:34 +01:00
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
"error": "Error emptying trash"
2026-02-14 01:29:34 +01:00
})),
)
}
}
2026-02-14 01:29:34 +01:00
}