From 3db1aa558fd0b9562dce47bcccc589d0afd58e11 Mon Sep 17 00:00:00 2001 From: Edouard Vanbelle Date: Fri, 17 Jul 2026 01:25:57 +0200 Subject: [PATCH] chore(/api/uploads): maked as deprecated, use now /api/files/delta/ --- .../api/handlers/chunked_upload_handler.rs | 26 ++++++++++++++++++- src/interfaces/api/routes.rs | 12 +++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/interfaces/api/handlers/chunked_upload_handler.rs b/src/interfaces/api/handlers/chunked_upload_handler.rs index 630c2a57..c7c1eb3b 100644 --- a/src/interfaces/api/handlers/chunked_upload_handler.rs +++ b/src/interfaces/api/handlers/chunked_upload_handler.rs @@ -491,7 +491,12 @@ impl ChunkedUploadHandler { } Err(e) => { tracing::error!("Failed to create file from chunked upload: {:?}", e); - AppError::internal_error(format!("Failed to create file: {}", e)).into_response() + // AuthZ audit #2 (2026-07-12) — route DomainError through + // `AppError::from` so graduated denial from + // `upload_file_streaming_with_perms` keeps the 403/404 + // shape instead of collapsing into a 500. Sibling + // `cancel_upload_impl` at :514 already uses this pattern. + AppError::from(e).into_response() } } } @@ -532,9 +537,15 @@ impl ChunkedUploadHandler { // routes.rs calls these free functions directly. // TODO: collapse back into the impl block after a utoipa upgrade resolves the issue. +/// **Deprecated.** Prefer `/api/files/delta/*` — hash-first negotiation, +/// resumable, chunked. The `/api/uploads/*` family stays for backward +/// compatibility with existing clients but receives no new features. #[utoipa::path( post, path = "/api/uploads", + description = "**Deprecated.** Prefer the delta-upload surface at `/api/files/delta/*` \ +(hash-first negotiation, resumable, chunked). The `/api/uploads/*` family is kept for \ +backward compatibility with existing clients but is no longer receiving new features.", request_body(content = CreateUploadRequest, content_type = "application/json", description = "Upload session parameters"), responses( (status = 201, description = "Upload session created", body = crate::application::ports::chunked_upload_ports::CreateUploadResponseDto), @@ -544,6 +555,7 @@ impl ChunkedUploadHandler { tag = "uploads", security(("bearerAuth" = [])) )] +#[deprecated(note = "prefer /api/files/delta/*")] pub async fn create_upload( state: State>, auth_user: AuthUser, @@ -552,9 +564,11 @@ pub async fn create_upload( ChunkedUploadHandler::create_upload_impl(state, auth_user, request).await } +/// **Deprecated.** Prefer `/api/files/delta/*` — see `create_upload`. #[utoipa::path( patch, path = "/api/uploads/{upload_id}", + description = "**Deprecated.** See `POST /api/uploads` for the migration note.", params( ("upload_id" = String, Path, description = "Upload session ID"), ("chunk_index" = usize, Query, description = "Zero-based chunk index"), @@ -583,6 +597,7 @@ pub async fn create_upload( tag = "uploads", security(("bearerAuth" = [])) )] +#[deprecated(note = "prefer /api/files/delta/*")] pub async fn upload_chunk( State(state): State>, auth_user: AuthUser, @@ -696,9 +711,11 @@ pub async fn upload_chunk( .into_response() } +/// **Deprecated.** Prefer `/api/files/delta/*` — see `create_upload`. #[utoipa::path( head, path = "/api/uploads/{upload_id}", + description = "**Deprecated.** See `POST /api/uploads` for the migration note.", params( ("upload_id" = String, Path, description = "Upload session ID"), ), @@ -709,6 +726,7 @@ pub async fn upload_chunk( tag = "uploads", security(("bearerAuth" = [])) )] +#[deprecated(note = "prefer /api/files/delta/*")] pub async fn get_upload_status( state: State>, auth_user: AuthUser, @@ -717,9 +735,11 @@ pub async fn get_upload_status( ChunkedUploadHandler::get_upload_status_impl(state, auth_user, path).await } +/// **Deprecated.** Prefer `/api/files/delta/*` — see `create_upload`. #[utoipa::path( post, path = "/api/uploads/{upload_id}/complete", + description = "**Deprecated.** See `POST /api/uploads` for the migration note.", params( ("upload_id" = String, Path, description = "Upload session ID"), ), @@ -744,6 +764,7 @@ pub async fn get_upload_status( tag = "uploads", security(("bearerAuth" = [])) )] +#[deprecated(note = "prefer /api/files/delta/*")] pub async fn complete_upload( state: State>, auth_user: AuthUser, @@ -757,9 +778,11 @@ pub async fn complete_upload( ChunkedUploadHandler::complete_upload_impl(state, auth_user, path, req).await } +/// **Deprecated.** Prefer `/api/files/delta/*` — see `create_upload`. #[utoipa::path( delete, path = "/api/uploads/{upload_id}", + description = "**Deprecated.** See `POST /api/uploads` for the migration note.", params( ("upload_id" = String, Path, description = "Upload session ID"), ), @@ -770,6 +793,7 @@ pub async fn complete_upload( tag = "uploads", security(("bearerAuth" = [])) )] +#[deprecated(note = "prefer /api/files/delta/*")] pub async fn cancel_upload( state: State>, auth_user: AuthUser, diff --git a/src/interfaces/api/routes.rs b/src/interfaces/api/routes.rs index 03e01807..d82bb52b 100644 --- a/src/interfaces/api/routes.rs +++ b/src/interfaces/api/routes.rs @@ -52,6 +52,11 @@ async fn get_openapi_spec() -> AxumJson { use crate::interfaces::api::handlers::admin_handler; use crate::interfaces::api::handlers::batch_handler::{self, BatchHandlerState}; +// `chunked_upload_handler::*` are marked `#[deprecated]` (prefer +// `/api/files/delta/*`); the router still needs to reference them +// until clients migrate. See the `chunked_upload_router` block +// below for the local `#[allow(deprecated)]`. +#[allow(deprecated)] use crate::interfaces::api::handlers::chunked_upload_handler::{ cancel_upload, complete_upload, create_upload, get_upload_status, upload_chunk, }; @@ -368,6 +373,13 @@ pub fn create_api_routes(app_state: &Arc) -> Router> { // Create routes for chunked uploads (large files >10MB). // All five handlers are free functions — see chunked_upload_handler.rs for why // #[utoipa::path] cannot be applied to ChunkedUploadHandler impl methods directly. + // + // Each handler carries `#[deprecated]` so utoipa marks the OpenAPI paths + // deprecated (Swagger UI shows the strikethrough + banner) and existing + // callers get a compile-time nudge to migrate to `/api/files/delta/*`. + // The route registration itself has to keep referencing them until the + // clients migrate off, so we suppress the local `deprecated` lint here. + #[allow(deprecated)] let chunked_upload_router = Router::new() .route("/", post(create_upload)) .route("/{upload_id}", axum::routing::patch(upload_chunk))