Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ members = [
"orion-server",
"third-party",
"observatory",
"scorpio",
]
default-members = ["mega", "mono", "libra", "aries", "orion", "orion-server"]
default-members = ["mega", "mono", "libra", "aries", "orion", "orion-server","scorpio"]
resolver = "1"

[workspace.dependencies]
Expand Down
59 changes: 58 additions & 1 deletion ceres/src/api_service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ pub trait ApiHandler: Send + Sync {
let commit = if let Some(commit) = commit_map.get(commit_id) {
commit.to_owned()
} else {
tracing::warn!("failed fecth from commit map: {}", commit_id);
tracing::warn!("failed fetch from commit map: {}", commit_id);
if root_commit.is_none() {
root_commit = Some(self.get_root_commit().await);
}
Expand All @@ -216,6 +216,63 @@ pub trait ApiHandler: Send + Sync {
}
}

/// return the dir's hash only
async fn get_tree_dir_hash(

Copilot AI May 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] This method is quite long and handles multiple responsibilities (tree search, mapping, commit lookup). Consider splitting into helper functions to improve readability and testability.

Copilot uses AI. Check for mistakes.
&self,
path: PathBuf,
dir_name: &str,
) -> Result<Vec<TreeCommitItem>, GitError> {
let mut cache = GitObjectCache::default();
match self.search_tree_by_path(&path).await? {
Some(tree) => {
let mut item_to_commit = HashMap::new();

self.add_trees_to_map(
&mut item_to_commit,
tree.tree_items
.iter()
.filter(|x| x.mode == TreeItemMode::Tree && x.name == dir_name)
.map(|x| x.id.to_string())
.collect(),
)
.await;

let commit_ids: HashSet<String> = item_to_commit.values().cloned().collect();
let commits = self
.get_commits_by_hashes(commit_ids.into_iter().collect())
.await
.unwrap();
let commit_map: HashMap<String, Commit> =
commits.into_iter().map(|x| (x.id.to_string(), x)).collect();

let mut root_commit: Option<Commit> = None;
let mut item_to_commit_map: HashMap<TreeItem, Option<Commit>> = HashMap::new();
for item in tree.tree_items {
if let Some(commit_id) = item_to_commit.get(&item.id.to_string()) {
let commit = if let Some(commit) = commit_map.get(commit_id) {
commit.to_owned()
} else {
tracing::warn!("failed fetch from commit map: {}", commit_id);
if root_commit.is_none() {
root_commit = Some(self.get_root_commit().await);
}
let root_commit = root_commit.as_ref().unwrap().clone();
self.traverse_commit_history(&path, &root_commit, &item, &mut cache)
.await
};
item_to_commit_map.insert(item, Some(commit));
}
}
let items: Vec<TreeCommitItem> = item_to_commit_map
.into_iter()
.map(TreeCommitItem::from)
.collect();
Ok(items)
}
None => Ok(Vec::new()),
}
}

fn convert_commit_to_info(&self, commit: Commit) -> Result<LatestCommitInfo, GitError> {
let message = commit.format_message();
let committer = UserInfo {
Expand Down
34 changes: 34 additions & 0 deletions mono/src/api/api_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub fn routers() -> OpenApiRouter<MonoApiServiceState> {
.routes(routes!(create_file))
.routes(routes!(get_latest_commit))
.routes(routes!(get_tree_commit_info))
.routes(routes!(get_tree_dir_hash))
.routes(routes!(path_can_be_cloned))
.routes(routes!(get_tree_info))
.routes(routes!(get_blob_string))
Expand Down Expand Up @@ -172,6 +173,39 @@ async fn get_tree_commit_info(
Ok(Json(CommonResult::success(Some(data))))
}

/// return the dir's hash
#[utoipa::path(
get,
path = "/tree/dir-hash",
params(
CodePreviewQuery
),
responses(
(status = 200, body = CommonResult<Vec<TreeCommitItem>>, content_type = "application/json")
),
tag = GIT_TAG
)]
async fn get_tree_dir_hash(
Query(query): Query<CodePreviewQuery>,
state: State<MonoApiServiceState>,
) -> Result<Json<CommonResult<Vec<TreeCommitItem>>>, ApiError> {
let path = std::path::Path::new(&query.path);
let parent_path = path
.parent()
.and_then(|p| p.to_str())
.unwrap_or("")
.to_string();
let target_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");

let data = state
.api_handler(parent_path.clone().into())
.await?
.get_tree_dir_hash(parent_path.into(), target_name)
.await?;

Ok(Json(CommonResult::success(Some(data))))
}

pub async fn get_blob_file(
state: State<MonoApiServiceState>,
Path(oid): Path<String>,
Expand Down
14 changes: 12 additions & 2 deletions scorpio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,22 @@ fjall = "2.8.0"
thiserror = "2.0.12"
crossbeam = "0.8.4"
fs_extra = "1.2"
dashmap = "6.1.0"



[features]
async-io = []

[workspace]

[dev-dependencies]
tempfile = { workspace = true }
serial_test = { workspace = true }
lazy_static = { workspace = true }
assert_cmd = { workspace = true }
scopeguard = { workspace = true }
testcontainers = { workspace = true, features = ["http_wait","reusable-containers"] }
reqwest = { version = "0.12.12", features = ["blocking"] }
http = { workspace = true }

[package.metadata.docs.rs]
all-features = true
Expand Down
9 changes: 8 additions & 1 deletion scorpio/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ workspace = "/home/luxian/megadir/mount"
config_file = "config.toml"
git_author = "MEGA"
git_email = "admin@mega.org"
dicfuse_readable = "true"
load_dir_depth = "3"
```
### `scorpio.toml` Configuration Guide:

Expand All @@ -97,8 +99,13 @@ git_email = "admin@mega.org"
Extended configuration filename (default: `config.toml`).

- **`git_author`** / **`git_email`**
Default Git author metadata (for version tracking).
Default Git author metadata (for version tracking).

- **`dicfuse_readable`**
Allow reading file contents from a read-only directory.

- **`load_dir_depth`**
Specifies how deep the file system should load and preload directories during initialization.

### How to Contribute?

Expand Down
7 changes: 4 additions & 3 deletions scorpio/scorpio.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
lfs_url = "http://47.79.35.136:8000"
lfs_url = "http://localhost:8000"
store_path = "/home/luxian/megadir/store"
config_file = "config.toml"
git_author = "MEGA"
git_email = "admin@mega.org"
workspace = "/home/luxian/megadir/mount"
base_url = "http://47.79.35.136:8000"
dicfuse_readable = "true"
base_url = "http://localhost:8000"
dicfuse_readable = "true"
load_dir_depth = "3"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve the description of configuration options in the README of scorpio.

18 changes: 15 additions & 3 deletions scorpio/src/dicfuse/async_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use fuse3::raw::prelude::*;
use fuse3::raw::reply::DirectoryEntry;
use fuse3::{Errno, Inode, Result};

use super::Dicfuse;
use crate::dicfuse::store::load_dir;
use futures::stream::{iter, Iter};
use std::vec::IntoIter;

use super::Dicfuse;

impl Filesystem for Dicfuse {
/// dir entry stream given by [`readdir`][Filesystem::readdir].
type DirEntryStream<'a>
Expand Down Expand Up @@ -136,6 +136,18 @@ impl Filesystem for Dicfuse {
}
async fn access(&self, _req: Request, inode: Inode, _mask: u32) -> Result<()> {
self.store.get_inode(inode).await?;
let load_parent = "/".to_string()
+ &self
.store
.find_path(inode)
.await
.ok_or(libc::ENOENT)?
.to_string();
let max_depth = self.store.max_depth() + load_parent.matches('/').count();
let hash_change = load_dir(self.store.clone(), load_parent, max_depth).await;
if hash_change {
self.store.update_ancestors_hash(inode).await;
}
Ok(())
}

Expand Down Expand Up @@ -213,7 +225,7 @@ impl Filesystem for Dicfuse {
}));
}
}
println!("{:?}", d);
// println!("{:?}", d);
Ok(ReplyDirectoryPlus {
entries: iter(d.into_iter()),
})
Expand Down
2 changes: 1 addition & 1 deletion scorpio/src/dicfuse/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod abi;
mod async_io;
mod store;
pub mod store;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to expose this store module? I think only dicfuse should use it.

mod tree_store;
use crate::manager::fetch::fetch_tree;
use crate::util::config;
Expand Down
Loading