From 097521b35dfd72447ba265489ed80f66e895c385 Mon Sep 17 00:00:00 2001 From: Warren Date: Mon, 22 Jun 2026 01:22:16 +0800 Subject: [PATCH] P2: Fix S3 multipart route - use query param for action - Change route from /s3/multipart/:bucket/*key/init to /s3/multipart/:bucket/*key?action=init - Add multipart_handler to unify all multipart operations - Use Response type instead of impl IntoResponse for type compatibility --- markbase-core/src/s3.rs | 76 +++++++++++++++++++++++++++++++++++++ markbase-core/src/server.rs | 11 +++--- 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/markbase-core/src/s3.rs b/markbase-core/src/s3.rs index 9fbb1c2..b8516da 100644 --- a/markbase-core/src/s3.rs +++ b/markbase-core/src/s3.rs @@ -1027,3 +1027,79 @@ fn extract_user_from_auth(headers: &HeaderMap) -> Option { None } } + +/// Unified multipart handler using query param for action +#[derive(serde::Deserialize)] +pub struct MultipartActionQuery { + action: Option, // init, part, complete, abort + upload_id: Option, + part_number: Option, +} + +pub async fn multipart_handler( + method: axum::http::Method, + Path((bucket, key)): Path<(String, String)>, + State(state): State, + query: axum::extract::Query, + headers: HeaderMap, + body: Body, +) -> axum::response::Response { + let action = query.action.as_deref().unwrap_or(""); + + match action { + "init" => { + initiate_multipart_upload( + Path((bucket, key)), + State(state), + headers, + ).await.into_response() + } + "part" => { + let upload_query = axum::extract::Query(UploadPartQuery { + upload_id: query.upload_id.clone().unwrap_or_default(), + part_number: query.part_number.unwrap_or(1), + }); + upload_part( + Path((bucket, key)), + State(state), + upload_query, + headers, + body, + ).await.into_response() + } + "complete" => { + let complete_query = axum::extract::Query(CompleteMultipartQuery { + upload_id: query.upload_id.clone().unwrap_or_default(), + }); + complete_multipart_upload( + Path((bucket, key)), + State(state), + complete_query, + headers, + body, + ).await.into_response() + } + "abort" => { + let abort_query = axum::extract::Query(AbortMultipartQuery { + upload_id: query.upload_id.clone().unwrap_or_default(), + }); + abort_multipart_upload( + Path((bucket, key)), + State(state), + abort_query, + headers, + ).await.into_response() + } + _ => { + if method == axum::http::Method::POST { + initiate_multipart_upload( + Path((bucket, key)), + State(state), + headers, + ).await.into_response() + } else { + (StatusCode::BAD_REQUEST, "Missing action parameter").into_response() + } + } + } +} diff --git a/markbase-core/src/server.rs b/markbase-core/src/server.rs index 16e95f5..41f467f 100644 --- a/markbase-core/src/server.rs +++ b/markbase-core/src/server.rs @@ -245,11 +245,12 @@ pub async fn run(port: u16, file: Option) -> anyhow::Result<()> { .post(crate::s3::put_object) // POST for uploads (same handler handles multipart detection) .delete(crate::s3::delete_object) ) - // Multipart upload endpoints - .route("/s3/multipart/:bucket/*key/init", post(crate::s3::initiate_multipart_upload)) - .route("/s3/multipart/:bucket/*key/part", put(crate::s3::upload_part)) - .route("/s3/multipart/:bucket/*key/complete", post(crate::s3::complete_multipart_upload)) - .route("/s3/multipart/:bucket/*key/abort", delete(crate::s3::abort_multipart_upload)) + // Multipart upload endpoints (use query param for action) + .route("/s3/multipart/:bucket/*key", + post(crate::s3::multipart_handler) + .put(crate::s3::multipart_handler) + .delete(crate::s3::multipart_handler) + ) // Bucket policy endpoints .route("/s3/policy/:bucket", get(crate::s3::get_bucket_policy)) .route("/s3/policy/:bucket", put(crate::s3::put_bucket_policy))