change: change get from json cache file to multipart
Signed-off-by: Pakin <pakin.t@forth.co.th>
This commit is contained in:
parent
d19dab7561
commit
5bb2a6c192
3 changed files with 187 additions and 24 deletions
51
Cargo.lock
generated
51
Cargo.lock
generated
|
|
@ -247,9 +247,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
||||
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"bytes",
|
||||
|
|
@ -297,6 +297,29 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be44683b41ccb9ab2d23a5230015c9c3c55be97a25e4428366de8873103f7970"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"multer",
|
||||
"pin-project-lite",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.5.0"
|
||||
|
|
@ -1667,6 +1690,23 @@ dependencies = [
|
|||
"pxfm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multer"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-util",
|
||||
"http",
|
||||
"httparse",
|
||||
"memchr",
|
||||
"mime",
|
||||
"spin",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.10.1"
|
||||
|
|
@ -2614,6 +2654,12 @@ dependencies = [
|
|||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
|
|
@ -2695,6 +2741,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"async-compression",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"axum-macros",
|
||||
"bb8",
|
||||
"bb8-redis",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ edition = "2024"
|
|||
async-compression = { version = "0.4.39", features = ["tokio", "brotli"] }
|
||||
axum = "0.8.7"
|
||||
axum-macros = "0.5.0"
|
||||
axum-extra = { version = "0.12.6", features = ["multipart"] }
|
||||
bb8 = "0.9.1"
|
||||
bb8-redis = "0.26.0"
|
||||
brotli = "8.0.2"
|
||||
|
|
|
|||
159
src/app.rs
159
src/app.rs
|
|
@ -8,6 +8,7 @@ use axum::{
|
|||
response::{IntoResponse, Response},
|
||||
routing::{get, post},
|
||||
};
|
||||
use axum_extra::extract::multipart::Multipart;
|
||||
use axum_macros::debug_handler;
|
||||
use bb8::{Pool, PooledConnection};
|
||||
use bb8_redis::RedisConnectionManager;
|
||||
|
|
@ -378,37 +379,148 @@ struct Signature {
|
|||
#[debug_handler]
|
||||
async fn commit_handler(
|
||||
State(state): State<AppState>,
|
||||
// request body
|
||||
Json(payload): Json<CommitBody>,
|
||||
// request body as multipart/form-data
|
||||
mut payload: Multipart,
|
||||
) -> impl IntoResponse {
|
||||
let mut content = match fetch_content_from_redis(state.redis.clone(), &payload.patch_key).await
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
// Extract multipart fields
|
||||
let mut path: Option<String> = None;
|
||||
let mut signature_username: Option<String> = None;
|
||||
let mut signature_email: Option<String> = None;
|
||||
let mut message: Option<String> = None;
|
||||
let mut file_bytes: Option<Vec<u8>> = None;
|
||||
|
||||
// Process each field in the multipart payload
|
||||
while let Ok(Some(field)) = payload.next_field().await {
|
||||
let name = field.name().unwrap_or("").to_string();
|
||||
|
||||
match name.as_str() {
|
||||
"path" => {
|
||||
path = Some(match field.text().await {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({"error": format!("Failed to read path field: {}", e)})),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
"signature_username" => {
|
||||
signature_username = Some(match field.text().await {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
Json(
|
||||
json!({"error": format!("Failed to read signature_username field: {}", e)}),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
"signature_email" => {
|
||||
signature_email = Some(match field.text().await {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
Json(
|
||||
json!({"error": format!("Failed to read signature_email field: {}", e)}),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
"message" => {
|
||||
message = Some(match field.text().await {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({"error": format!("Failed to read message field: {}", e)})),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
"file" => {
|
||||
file_bytes = Some(match field.bytes().await {
|
||||
Ok(b) => b.to_vec(),
|
||||
Err(e) => {
|
||||
return (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({"error": format!("Failed to read file field: {}", e)})),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
// Ignore unknown fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
let path = match path {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
return (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({"error": e})),
|
||||
Json(json!({"error": "Missing required field: path"})),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let is_patch_file = content.starts_with("patch");
|
||||
// do apply patch first
|
||||
if is_patch_file {
|
||||
content = apply_patch_to_file(state.redis.clone(), &payload.path, &mut content).await;
|
||||
}
|
||||
let signature_username = match signature_username {
|
||||
Some(su) => su,
|
||||
None => {
|
||||
return (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({"error": "Missing required field: signature_username"})),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let signature_email = match signature_email {
|
||||
Some(se) => se,
|
||||
None => {
|
||||
return (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({"error": "Missing required field: signature_email"})),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let file_bytes = match file_bytes {
|
||||
Some(fb) => fb,
|
||||
None => {
|
||||
return (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({"error": "Missing required field: file"})),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Create signature
|
||||
let signature = Signature {
|
||||
username: signature_username,
|
||||
email: signature_email,
|
||||
};
|
||||
|
||||
// Get branch name from config
|
||||
let branch = state
|
||||
.clone()
|
||||
.get_config("GIT_REPO_BRANCH_NAME")
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or("master".to_string());
|
||||
|
||||
// Commit the file content directly from multipart upload
|
||||
let commit_oid = match commit_file_content(
|
||||
state.clone().repo,
|
||||
&payload.path,
|
||||
&content.as_bytes(),
|
||||
payload.signature,
|
||||
&payload.message.unwrap_or("update: from api".to_string()),
|
||||
state
|
||||
.clone()
|
||||
.get_config("GIT_REPO_BRANCH_NAME")
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or("master".to_string()),
|
||||
&path,
|
||||
&file_bytes,
|
||||
signature,
|
||||
&message.unwrap_or("update: from api".to_string()),
|
||||
branch,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
|
@ -425,7 +537,10 @@ async fn commit_handler(
|
|||
let redis_pre_lock = state.redis.clone();
|
||||
{
|
||||
if let Ok(mut rl) = redis_pre_lock.get().await {
|
||||
let _ = rl.rpush(format!("{}.history", payload.path), payload.patch_key);
|
||||
let _ = rl.rpush(
|
||||
format!("{}.history", path),
|
||||
format!("commit-{}", commit_oid),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue