Repository: web3infra-foundation/mega (language: rust)

Issue:
Import openEuler docs repo encounter protocol error
When I import `https://gitee.com/openeuler/docs.git` to Mega service, encountered an error like this,

```bash
docs 已存在，正在更新...
Already up to date.
Enumerating objects: 29297, done.
Counting objects: 100% (29297/29297), done.
Delta compression using up to 96 threads
Compressing objects: 100% (16164/16164), done.
Writing objects: 100% (29297/29297), 516.64 MiB | 268.27 MiB/s, done.
Total 29297 (delta 13770), reused 27491 (delta 12593), pack-reused 0
error: unable to rewind rpc post data - try increasing http.postBuffer
error: RPC failed; curl 65 seek callback returned error 1
send-pack: unexpected disconnect while reading sideband packet
fatal: the remote end hung up unexpectedly
Everything up-to-date
Enumerating objects: 13794, done.
Counting objects: 100% (13794/13794), done.
Delta compression using up to 96 threads
Compressing objects: 100% (7350/7350), done.
Writing objects: 100% (13794/13794), 156.02 MiB | 292.07 MiB/s, done.
Total 13794 (delta 6398), reused 13200 (delta 6159), pack-reused 0
error: unable to rewind rpc post data - try increasing http.postBuffer
error: RPC failed; curl 65 seek callback returned error 1
send-pack: unexpected disconnect while reading sideband packet
fatal: the remote end hung up unexpectedly
Everything up-to-date
```


Discussion / hints:
`git config --global http.postBuffer 5242880000`

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 34c16196..26427851 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,8 +15,8 @@ members = [
     "taurus",
     "lunar/src-tauri",
     "atlas",
-    "orion",
-    "panel",
+    # "orion",
+    # "panel",
 ]
 default-members = ["mega", "mono", "libra", "aries"]
 resolver = "1"
@@ -43,7 +43,7 @@ serde_json = "1.0.132"
 tracing = "0.1.40"
 tracing-subscriber = "0.3.19"
 tracing-appender = "0.2"
-thiserror = "2.0.6"
+thiserror = "2.0.9"
 rand = "0.8.5"
 smallvec = "1.13.2"
 tokio = "1.42"
@@ -61,21 +61,21 @@ futures-util = "0.3.30"
 go-defer = "0.1.0"
 russh = "0.49.0"
 russh-keys = "0.49.1"
-axum = "0.7.7"
-axum-extra = "0.9.4"
+axum = "0.8.1"
+axum-extra = "0.10.0"
 axum-server = "0.7.1"
 tower-http = "0.6.1"
-tower = "0.5.1"
+tower = "0.5.2"
 hex = "0.4.3"
-sea-orm = "1.1.1"
+sea-orm = "1.1.3"
 flate2 = "1.0.35"
 bstr = "1.11.0"
 colored = "2.1.0"
 idgenerator = "2.0.0"
 num_cpus = "1.16.0"
-config = "0.15.0"
+config = "0.15.4"
 shadow-rs = "0.36.0"
-reqwest = "0.12.9"
+reqwest = "0.12.12"
 lazy_static = "1.5.0"
 uuid = "1.11.0"
 regex = "1.11.1"
@@ -89,6 +89,7 @@ cedar-policy = "4.2.2"
 secp256k1 = "0.30.0"
 oauth2 = "4.4.2"
 base64 = "0.22.1"
+encoding_rs = "0.8.31"
 
 [profile.release]
 debug = true
\ No newline at end of file
diff --git a/ceres/src/api_service/import_api_service.rs b/ceres/src/api_service/import_api_service.rs
index ae203722..231ac9a6 100644
--- a/ceres/src/api_service/import_api_service.rs
+++ b/ceres/src/api_service/import_api_service.rs
@@ -4,7 +4,7 @@ use std::collections::VecDeque;
 use std::path::Path;
 use std::path::PathBuf;
 
-use axum::async_trait;
+use async_trait::async_trait;
 
 use jupiter::context::Context;
 use mercury::errors::GitError;
diff --git a/ceres/src/api_service/mod.rs b/ceres/src/api_service/mod.rs
index d22fff1d..3416cc77 100644
--- a/ceres/src/api_service/mod.rs
+++ b/ceres/src/api_service/mod.rs
@@ -3,7 +3,7 @@ use std::{
     path::{Component, Path, PathBuf},
 };
 
-use axum::async_trait;
+use async_trait::async_trait;
 
 use callisto::raw_blob;
 use common::errors::MegaError;
diff --git a/ceres/src/api_service/mono_api_service.rs b/ceres/src/api_service/mono_api_service.rs
index 768b0bfa..0384b156 100644
--- a/ceres/src/api_service/mono_api_service.rs
+++ b/ceres/src/api_service/mono_api_service.rs
@@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
 use std::str::FromStr;
 use std::{env, fs};
 
-use axum::async_trait;
+use async_trait::async_trait;
 use tokio::process::Command;
 
 use callisto::db_enums::ConvType;
diff --git a/gateway/src/https_server.rs b/gateway/src/https_server.rs
index 6ebb7faa..d4eaf0b7 100644
--- a/gateway/src/https_server.rs
+++ b/gateway/src/https_server.rs
@@ -154,20 +154,20 @@ pub async fn app(
     // add TraceLayer for log record
     // add CorsLayer to add cors header
     Router::new()
-        .nest(
-            "/",
-            lfs_router::routers().with_state(mono_api_state.clone()),
-        )
-        .nest(
-            "/api/v1/mono",
-            mono::api::api_router::routers().with_state(mono_api_state.clone()),
-        )
-        .nest(
-            "/api/v1/mega",
-            mega_routers().with_state(mega_api_state.clone()),
+        .merge(lfs_router::routers().with_state(mono_api_state.clone()))
+        .merge(
+            Router::new()
+                .nest(
+                    "/api/v1/mono",
+                    mono::api::api_router::routers().with_state(mono_api_state.clone()),
+                )
+                .nest(
+                    "/api/v1/mega",
+                    mega_routers().with_state(mega_api_state.clone()),
+                ),
         )
         // Using Regular Expressions for Path Matching in Protocol
-        .route("/*path", get(get_method_router).post(post_method_router))
+        .route("/{*path}", get(get_method_router).post(post_method_router))
         .layer(
             ServiceBuilder::new().layer(CorsLayer::new().allow_origin(Any).allow_headers(vec![
                 http::header::AUTHORIZATION,
diff --git a/gemini/Cargo.toml b/gemini/Cargo.toml
index 9a81f3cf..bfba0919 100644
--- a/gemini/Cargo.toml
+++ b/gemini/Cargo.toml
@@ -25,6 +25,7 @@ reqwest = { workspace = true }
 tracing = { workspace = true }
 tokio = { workspace = true, features = ["net"] }
 chrono = { workspace = true }
-secp256k1 = { workspace = true , features = ["serde", "rand","hashes"] }
+secp256k1 = { workspace = true, features = ["serde", "rand", "hashes"] }
 ring = "0.17.8"
-hex = { workspace = true }
\ No newline at end of file
+hex = { workspace = true }
+async-trait = { workspace = true }
diff --git a/gemini/src/ztm/agent.rs b/gemini/src/ztm/agent.rs
index 0048b170..8641cd42 100644
--- a/gemini/src/ztm/agent.rs
+++ b/gemini/src/ztm/agent.rs
@@ -5,7 +5,7 @@ use std::{
     time::Duration,
 };
 
-use axum::async_trait;
+use async_trait::async_trait;
 use common::config::Config;
 use reqwest::{header::CONTENT_TYPE, Client};
 use serde::{Deserialize, Serialize};
diff --git a/gemini/src/ztm/hub.rs b/gemini/src/ztm/hub.rs
index 8d29630e..607a6cc6 100644
--- a/gemini/src/ztm/hub.rs
+++ b/gemini/src/ztm/hub.rs
@@ -1,5 +1,5 @@
 use ::serde::{Deserialize, Serialize};
-use axum::async_trait;
+use async_trait::async_trait;
 use reqwest::Client;
 use serde_json::Value;
 
diff --git a/mercury/Cargo.toml b/mercury/Cargo.toml
index dca858a3..e4952ec1 100644
--- a/mercury/Cargo.toml
+++ b/mercury/Cargo.toml
@@ -34,6 +34,7 @@ futures-util = { workspace = true }
 bytes = { workspace = true }
 axum = { workspace = true }
 memchr = { workspace = true }
+encoding_rs = { workspace = true }
 rayon = "1.10.0"
 
 [target.'cfg(windows)'.dependencies] # only on Windows
diff --git a/mercury/src/internal/object/tree.rs b/mercury/src/internal/object/tree.rs
index 3f947320..14ae5392 100644
--- a/mercury/src/internal/object/tree.rs
+++ b/mercury/src/internal/object/tree.rs
@@ -14,15 +14,15 @@
 //! have been added, modified, or deleted between two points in time. This allows Git to perform
 //! operations like merging and rebasing more quickly and accurately.
 //!
-use std::fmt::Display;
-use colored::Colorize;
-use serde::Deserialize;
-use serde::Serialize;
 use crate::errors::GitError;
 use crate::hash::SHA1;
 use crate::internal::object::ObjectTrait;
 use crate::internal::object::ObjectType;
-
+use colored::Colorize;
+use encoding_rs::GBK;
+use serde::Deserialize;
+use serde::Serialize;
+use std::fmt::Display;
 
 /// In Git, the mode field in a tree object's entry specifies the type of the object represented by
 /// that entry. The mode is a three-digit octal number that encodes both the permissions and the
@@ -180,13 +180,26 @@ impl TreeItem {
         let mode = parts.next().unwrap();
         let rest = parts.next().unwrap();
         let mut parts = rest.splitn(2, |b| *b == b'\0');
-        let name = parts.next().unwrap();
+        let raw_name = parts.next().unwrap();
         let id = parts.next().unwrap();
 
+        let name = if String::from_utf8(raw_name.to_vec()).is_ok() {
+            String::from_utf8(raw_name.to_vec()).unwrap()
+        } else {
+            let (decoded, _, had_errors) = GBK.decode(raw_name);
+            if had_errors {
+                return Err(GitError::InvalidTreeItem(format!(
+                    "Unsupported raw format: {:?}",
+                    raw_name
+                )));
+            } else {
+                decoded.to_string()
+            }
+        };
         Ok(TreeItem {
             mode: TreeItemMode::tree_item_type_from_bytes(mode)?,
             id: SHA1::from_bytes(id),
-            name: String::from_utf8(name.to_vec())?,
+            name,
         })
     }
 
@@ -262,7 +275,7 @@ impl Tree {
     }
 
     /// After the subdirectory is changed, the hash value of the tree is recalculated.
-    pub fn rehash(&mut self){
+    pub fn rehash(&mut self) {
         let mut data = Vec::new();
         for item in &self.tree_items {
             data.extend_from_slice(item.to_data().as_slice());
@@ -271,9 +284,9 @@ impl Tree {
     }
 }
 
-impl TryFrom<&[u8]> for Tree{
+impl TryFrom<&[u8]> for Tree {
     type Error = GitError;
-    fn try_from(data: &[u8]) -> Result<Self, Self::Error>  {
+    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
         let h = SHA1::from_type_and_data(ObjectType::Tree, data);
         Tree::from_bytes(data, h)
     }
diff --git a/mono/src/api/api_router.rs b/mono/src/api/api_router.rs
index 8352b6a1..a5ec6776 100644
--- a/mono/src/api/api_router.rs
+++ b/mono/src/api/api_router.rs
@@ -35,7 +35,7 @@ pub fn routers() -> Router<MonoApiServiceState> {
         .route("/tree/path-can-clone", get(path_can_be_cloned))
         .route("/tree", get(get_tree_info))
         .route("/blob", get(get_blob_string))
-        .route("/file/blob/:object_id", get(get_blob_file))
+        .route("/file/blob/{object_id}", get(get_blob_file))
         .route("/file/tree", get(get_tree_file));
     Router::new()
         .merge(router)
diff --git a/mono/src/api/issue/issue_router.rs b/mono/src/api/issue/issue_router.rs
index 6f1d3fd8..e69c0184 100644
--- a/mono/src/api/issue/issue_router.rs
+++ b/mono/src/api/issue/issue_router.rs
@@ -14,14 +14,17 @@ use crate::api::oauth::model::LoginUser;
 use crate::api::MonoApiServiceState;
 
 pub fn routers() -> Router<MonoApiServiceState> {
-    Router::new()
-        .route("/issue/list", post(fetch_issue_list))
-        .route("/issue/new", post(new_issue))
-        .route("/issue/:link/close", post(close_issue))
-        .route("/issue/:link/reopen", post(reopen_issue))
-        .route("/issue/:link/detail", get(issue_detail))
-        .route("/issue/:link/comment", post(save_comment))
-        .route("/issue/comment/:id/delete", post(delete_comment))
+    Router::new().nest(
+        "/issue",
+        Router::new()
+            .route("/list", post(fetch_issue_list))
+            .route("/new", post(new_issue))
+            .route("/{link}/close", post(close_issue))
+            .route("/{link}/reopen", post(reopen_issue))
+            .route("/{link}/detail", get(issue_detail))
+            .route("/{link}/comment", post(save_comment))
+            .route("/comment/{id}/delete", post(delete_comment)),
+    )
 }
 
 #[derive(Deserialize)]
diff --git a/mono/src/api/lfs/lfs_router.rs b/mono/src/api/lfs/lfs_router.rs
index 69d5cee6..0a252747 100644
--- a/mono/src/api/lfs/lfs_router.rs
+++ b/mono/src/api/lfs/lfs_router.rs
@@ -70,15 +70,15 @@ const LFS_CONTENT_TYPE: &str = "application/vnd.git-lfs+json";
 /// document describes the server LFS discovery protocol.
 pub fn routers() -> Router<MonoApiServiceState> {
     Router::new()
-        .route("/objects/:object_id", get(lfs_download_object))
-        .route("/objects/:object_id/:chunk_id", get(lfs_download_chunk))
-        .route("/objects/:object_id", put(lfs_upload_object))
+        .route("/objects/{object_id}", get(lfs_download_object))
+        .route("/objects/{object_id}/{chunk_id}", get(lfs_download_chunk))
+        .route("/objects/{object_id}", put(lfs_upload_object))
         .route("/locks", get(list_locks))
         .route("/locks", post(create_lock))
         .route("/locks/verify", post(list_locks_for_verification))
-        .route("/locks/:id/unlock", post(delete_lock))
+        .route("/locks/{id}/unlock", post(delete_lock))
         .route("/objects/batch", post(lfs_process_batch))
-        .route("/objects/:object_id/chunks", get(lfs_fetch_chunk_ids))
+        .route("/objects/{object_id}/chunks", get(lfs_fetch_chunk_ids))
 }
 
 pub async fn list_locks(
@@ -103,7 +103,8 @@ pub async fn list_locks_for_verification(
     state: State<MonoApiServiceState>,
     Json(json): Json<VerifiableLockRequest>,
 ) -> Result<Response<Body>, (StatusCode, String)> {
-    let result = handler::lfs_verify_lock(state.context.services.lfs_db_storage.clone(), json).await;
+    let result =
+        handler::lfs_verify_lock(state.context.services.lfs_db_storage.clone(), json).await;
     match result {
         Ok(lock_list) => {
             let body = serde_json::to_string(&lock_list).unwrap_or_default();
@@ -125,7 +126,8 @@ pub async fn create_lock(
     state: State<MonoApiServiceState>,
     Json(json): Json<LockRequest>,
 ) -> Result<Response<Body>, (StatusCode, String)> {
-    let result = handler::lfs_create_lock(state.context.services.lfs_db_storage.clone(), json).await;
+    let result =
+        handler::lfs_create_lock(state.context.services.lfs_db_storage.clone(), json).await;
     match result {
         Ok(lock) => {
             let lock_response = LockResponse {
diff --git a/mono/src/api/mr/mr_router.rs b/mono/src/api/mr/mr_router.rs
index bd8a8873..4dd51022 100644
--- a/mono/src/api/mr/mr_router.rs
+++ b/mono/src/api/mr/mr_router.rs
@@ -21,15 +21,18 @@ use crate::api::util;
 use crate::api::MonoApiServiceState;
 
 pub fn routers() -> Router<MonoApiServiceState> {
-    Router::new()
-        .route("/mr/list", post(fetch_mr_list))
-        .route("/mr/:link/detail", get(mr_detail))
-        .route("/mr/:link/merge", post(merge))
-        .route("/mr/:link/close", post(close_mr))
-        .route("/mr/:link/reopen", post(reopen_mr))
-        .route("/mr/:link/files-changed", get(get_mr_files_changed))
-        .route("/mr/:link/comment", post(save_comment))
-        .route("/mr/comment/:conv_id/delete", post(delete_comment))
+    Router::new().nest(
+        "/mr",
+        Router::new()
+            .route("/list", post(fetch_mr_list))
+            .route("/{link}/detail", get(mr_detail))
+            .route("/{link}/merge", post(merge))
+            .route("/{link}/close", post(close_mr))
+            .route("/{link}/reopen", post(reopen_mr))
+            .route("/{link}/files-changed", get(get_mr_files_changed))
+            .route("/{link}/comment", post(save_comment))
+            .route("/comment/{conv_id}/delete", post(delete_comment)),
+    )
 }
 
 async fn reopen_mr(
diff --git a/mono/src/api/oauth/mod.rs b/mono/src/api/oauth/mod.rs
index f2099c5e..768e1b0b 100644
--- a/mono/src/api/oauth/mod.rs
+++ b/mono/src/api/oauth/mod.rs
@@ -1,7 +1,6 @@
 use anyhow::Context;
 use async_session::{MemoryStore, Session, SessionStore};
 use axum::{
-    async_trait,
     extract::{FromRef, FromRequestParts, Query, State},
     http::{header::SET_COOKIE, HeaderMap},
     response::{IntoResponse, Redirect, Response},
@@ -79,7 +78,8 @@ async fn login_authorized(
     }
 
     let new_user: user::Model = github_user.into();
-    let user = state.user_stg()
+    let user = state
+        .user_stg()
         .find_user_by_email(&new_user.email)
         .await
         .unwrap();
@@ -190,7 +190,6 @@ impl IntoResponse for AuthRedirect {
     }
 }
 
-#[async_trait]
 impl<S> FromRequestParts<S> for LoginUser
 where
     MemoryStore: FromRef<S>,
diff --git a/mono/src/api/user/user_router.rs b/mono/src/api/user/user_router.rs
index 4a32bb63..b46b6e1c 100644
--- a/mono/src/api/user/user_router.rs
+++ b/mono/src/api/user/user_router.rs
@@ -16,15 +16,18 @@ use crate::api::MonoApiServiceState;
 use crate::api::{error::ApiError, oauth::model::LoginUser, util};
 
 pub fn routers() -> Router<MonoApiServiceState> {
-    Router::new()
-        .route("/user", get(user))
-        .route("/user/ssh", get(list_key))
-        .route("/user/ssh", post(add_key))
-        .route("/user/ssh/:key_id/delete", post(remove_key))
-        .route("/user/token/generate", post(generate_token))
-        .route("/user/token/list", get(list_token))
-        .route("/user/token/:key_id/delete", post(remove_token))
-        .route("/repo-permissions", get(repo_permissions))
+    Router::new().nest(
+        "/user",
+        Router::new()
+            .route("/", get(user))
+            .route("/ssh", get(list_key))
+            .route("/ssh", post(add_key))
+            .route("/ssh/{key_id}/delete", post(remove_key))
+            .route("/token/generate", post(generate_token))
+            .route("/token/list", get(list_token))
+            .route("/token/{key_id}/delete", post(remove_token))
+            .route("/repo-permissions", get(repo_permissions)),
+    )
 }
 
 async fn user(
@@ -54,7 +57,12 @@ async fn add_key(
 
     let res = state
         .user_stg()
-        .save_ssh_key(user.user_id, &title, &json.ssh_key, &key.fingerprint(HashAlg::Sha256).to_string())
+        .save_ssh_key(
+            user.user_id,
+            &title,
+            &json.ssh_key,
+            &key.fingerprint(HashAlg::Sha256).to_string(),
+        )
         .await;
     let res = match res {
         Ok(_) => CommonResult::success(None),
diff --git a/mono/src/git_protocol/http.rs b/mono/src/git_protocol/http.rs
index 7a3fdf2c..3045f9e1 100644
--- a/mono/src/git_protocol/http.rs
+++ b/mono/src/git_protocol/http.rs
@@ -194,19 +194,23 @@ pub async fn git_receive_pack(
     let mut data_stream = req.into_body().into_data_stream();
     let mut report_status = Bytes::new();
 
+    let mut chunk_buffer = BytesMut::new(); // Used to cache the data of chunks before the PACK subsequence is found.
     // Process the data stream to handle the Git receive-pack protocol.
     while let Some(chunk) = data_stream.next().await {
         let chunk = chunk.unwrap();
         // Process the data up to the "PACK" subsequence.
         if let Some(pos) = search_subsequence(&chunk, b"PACK") {
-            pack_protocol.git_receive_pack_protocol(Bytes::copy_from_slice(&chunk[0..pos]));
+            chunk_buffer.extend_from_slice(&chunk[0..pos]);
+            pack_protocol.git_receive_pack_protocol(Bytes::copy_from_slice(&chunk_buffer));
             // Create a new stream from the remaining bytes and the rest of the data stream.
-            let remaining_bytes = Bytes::copy_from_slice(&chunk[pos..]);
-            let remaining_stream = stream::once(async { Ok(remaining_bytes) }).chain(data_stream);
+            let left_chunk_bytes = Bytes::copy_from_slice(&chunk[pos..]);
+            let pack_stream = stream::once(async { Ok(left_chunk_bytes) }).chain(data_stream);
             report_status = pack_protocol
-                .git_receive_pack_stream(Box::pin(remaining_stream))
+                .git_receive_pack_stream(Box::pin(pack_stream))
                 .await?;
             break;
+        } else {
+            chunk_buffer.extend_from_slice(&chunk);
         }
     }
     tracing::info!("report status:{:?}", report_status);
diff --git a/mono/src/server/https_server.rs b/mono/src/server/https_server.rs
index 155f595a..cc2ac273 100644
--- a/mono/src/server/https_server.rs
+++ b/mono/src/server/https_server.rs
@@ -151,15 +151,14 @@ pub async fn app(context: Context, host: String, port: u16, common: CommonOption
     // add TraceLayer for log record
     // add CorsLayer to add cors header
     Router::new()
-        .nest("/", lfs_router::routers())
-        .with_state(api_state.clone())
-        .nest(
+        .merge(lfs_router::routers().with_state(api_state.clone()))
+        .merge(Router::new().nest(
             "/api/v1",
             api_router::routers().with_state(api_state.clone()),
-        )
-        .nest("/auth", oauth::routers().with_state(api_state.clone()))
+        ))
+        .merge(Router::new().nest("/auth", oauth::routers().with_state(api_state.clone())))
         // Using Regular Expressions for Path Matching in Protocol
-        .route("/*path", get(get_method_router).post(post_method_router))
+        .route("/{*path}", get(get_method_router).post(post_method_router))
         .layer(
             ServiceBuilder::new().layer(CorsLayer::new().allow_origin(Any).allow_headers(vec![
                 http::header::AUTHORIZATION,


Test patch: