feat(drive): permanent deletion per drive

This commit is contained in:
Edouard Vanbelle
2026-06-24 21:31:59 +02:00
parent 7d24015fc4
commit b73f176024
10 changed files with 596 additions and 23 deletions
@@ -405,3 +405,61 @@ pub async fn empty_trash(
}
}
}
/// `DELETE /api/trash/drive/{drive_id}` — per-drive empty trash.
///
/// Same destructive shape as the all-drives `DELETE /api/trash`, but
/// scoped to a single drive the caller can Delete in. Used by the
/// `/trash` page's Drive group-by, which exposes a per-row "Empty"
/// affordance so multi-drive owners don't have to wipe everything at
/// once.
///
/// Refused with `404` (anti-enum) when the caller has no Delete-bearing
/// role on the named drive — the user-facing drive listing would emit
/// the same shape for an unknown id.
#[utoipa::path(
delete,
path = "/api/trash/drive/{drive_id}",
params(("drive_id" = Uuid, Path, description = "Drive UUID")),
responses(
(status = 200, description = "Drive trash emptied successfully"),
(status = 404, description = "Caller lacks Delete on this drive"),
(status = 501, description = "Trash feature not enabled"),
),
security(("bearerAuth" = [])),
tag = "trash"
)]
#[instrument(skip_all)]
pub async fn empty_trash_for_drive(
State(state): State<Arc<AppState>>,
auth_user: AuthUser,
Path(drive_id): Path<uuid::Uuid>,
) -> impl IntoResponse {
debug!(
"Request to empty trash for drive {} by user {}",
drive_id, auth_user.id
);
let trash_service = match state.trash_service.as_ref() {
Some(service) => service,
None => {
return (
StatusCode::NOT_IMPLEMENTED,
Json(json!({ "error": "Trash feature is not enabled" })),
)
.into_response();
}
};
match trash_service
.empty_trash_for_drive(auth_user.id, drive_id)
.await
{
Ok(_) => (
StatusCode::OK,
Json(json!({ "success": true, "drive_id": drive_id })),
)
.into_response(),
Err(e) => AppError::from(e).into_response(),
}
}
+7
View File
@@ -469,6 +469,13 @@ pub fn create_api_routes(app_state: &Arc<AppState>) -> Router<Arc<AppState>> {
// when a wildcard like /{id} could otherwise capture them.
.route("/resources", get(trash_handler::get_trash_resources))
.route("/empty", delete(trash_handler::empty_trash))
// Per-drive empty (D2b stage 4 / per-drive UX). Scoped
// empty of one drive's trash; refused 404 when the caller
// lacks Delete on the named drive.
.route(
"/drive/{drive_id}",
delete(trash_handler::empty_trash_for_drive),
)
.route("/files/{id}", delete(trash_handler::move_file_to_trash))
.route("/folders/{id}", delete(trash_handler::move_folder_to_trash))
.route("/{id}/restore", post(trash_handler::restore_from_trash))