P2: Fix S3 multipart route - use query param for action
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled

- 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
This commit is contained in:
Warren
2026-06-22 01:22:16 +08:00
parent aae8669c9f
commit 097521b35d
2 changed files with 82 additions and 5 deletions

View File

@@ -1027,3 +1027,79 @@ fn extract_user_from_auth(headers: &HeaderMap) -> Option<String> {
None
}
}
/// Unified multipart handler using query param for action
#[derive(serde::Deserialize)]
pub struct MultipartActionQuery {
action: Option<String>, // init, part, complete, abort
upload_id: Option<String>,
part_number: Option<u32>,
}
pub async fn multipart_handler(
method: axum::http::Method,
Path((bucket, key)): Path<(String, String)>,
State(state): State<crate::server::AppState>,
query: axum::extract::Query<MultipartActionQuery>,
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()
}
}
}
}

View File

@@ -245,11 +245,12 @@ pub async fn run(port: u16, file: Option<String>) -> 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))