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
25 changes: 13 additions & 12 deletions ceres/src/api_service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,22 @@ pub trait ApiHandler: Send + Sync {
Ok(commit.into())
}

async fn get_tree_info(&self, path: PathBuf) -> Result<Vec<TreeBriefItem>, GitError> {
match self.search_tree_by_path(&path).await? {
async fn get_tree_info(&self, path: &Path) -> Result<Vec<TreeBriefItem>, GitError> {
match self.search_tree_by_path(path).await? {
Some(tree) => {
let mut items = Vec::new();
for item in tree.tree_items {
let mut info: TreeBriefItem = item.clone().into();
path.join(item.name)
.to_str()
.unwrap()
.clone_into(&mut info.path);
items.push(info);
}
let items = tree
.tree_items
.into_iter()
.map(|item| {
let full_path = path.join(&item.name);
let mut info: TreeBriefItem = item.into();
info.path = full_path.to_str().unwrap().to_owned();
Comment thread
benjamin-747 marked this conversation as resolved.
info
})
.collect();
Ok(items)
}
None => Ok(Vec::new()),
None => Ok(vec![]),
}
}

Expand Down
14 changes: 14 additions & 0 deletions ceres/src/model/git.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use utoipa::{IntoParams, ToSchema};

Expand Down Expand Up @@ -164,3 +166,15 @@ impl From<TreeItem> for TreeBriefItem {
fn default_path() -> String {
"/".to_string()
}

#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct TreeResponse {
pub file_tree: HashMap<String, FileTreeItem>,
pub tree_items: Vec<TreeBriefItem>,
}

#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct FileTreeItem {
pub tree_items: Vec<TreeBriefItem>,
pub total_count: usize,
}
67 changes: 51 additions & 16 deletions mono/src/api/api_router.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::{collections::HashMap, path::PathBuf};

use axum::{
body::Body,
Expand All @@ -12,8 +12,8 @@ use http::StatusCode;
use ceres::{
api_service::ApiHandler,
model::git::{
BlobContentQuery, CodePreviewQuery, CreateFileInfo, LatestCommitInfo, TreeBriefItem,
TreeCommitItem, TreeHashItem, TreeQuery,
BlobContentQuery, CodePreviewQuery, CreateFileInfo, FileTreeItem, LatestCommitInfo,
TreeCommitItem, TreeHashItem, TreeQuery, TreeResponse,
},
};
use common::model::CommonResult;
Expand Down Expand Up @@ -60,7 +60,7 @@ async fn get_blob_string(
state: State<MonoApiServiceState>,
) -> Result<Json<CommonResult<String>>, ApiError> {
let data = state
.api_handler(query.path.clone().into())
.api_handler(query.path.as_ref())
.await?
.get_blob_as_string(query.path.into())
.await?;
Expand Down Expand Up @@ -95,7 +95,7 @@ async fn create_file(
Json(json): Json<CreateFileInfo>,
) -> Result<Json<CommonResult<String>>, ApiError> {
state
.api_handler(json.path.clone().into())
.api_handler(json.path.as_ref())
.await?
.create_monorepo_file(json.clone())
.await?;
Expand All @@ -119,7 +119,7 @@ async fn get_latest_commit(
state: State<MonoApiServiceState>,
) -> Result<Json<LatestCommitInfo>, ApiError> {
let res = state
.api_handler(query.path.clone().into())
.api_handler(query.path.as_ref())
.await?
.get_latest_commit(query.path.into())
.await?;
Expand All @@ -134,20 +134,55 @@ async fn get_latest_commit(
CodePreviewQuery
),
responses(
(status = 200, body = CommonResult<Vec<TreeBriefItem>>, content_type = "application/json")
(status = 200, body = CommonResult<TreeResponse>, content_type = "application/json")
),
tag = GIT_TAG
)]
async fn get_tree_info(
Query(query): Query<CodePreviewQuery>,
state: State<MonoApiServiceState>,
) -> Result<Json<CommonResult<Vec<TreeBriefItem>>>, ApiError> {
let data = state
.api_handler(query.path.clone().into())
) -> Result<Json<CommonResult<TreeResponse>>, ApiError> {
let mut parts = Vec::new();
let mut current = String::new();
let mut segments = query.path.split('/').peekable();

while let Some(segment) = segments.next() {
if segment.is_empty() {
current = "/".to_string();
parts.push(current.clone());
} else if segments.peek().is_some() {
if current != "/" {
current.push('/');
}
current.push_str(segment);
parts.push(current.clone());
}
Comment on lines +146 to +159

Copilot AI Jun 27, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Consider normalizing the path segments to handle edge cases such as multiple consecutive slashes, ensuring that the generated 'parts' accurately represent the intended file path hierarchy.

Suggested change
let mut current = String::new();
let mut segments = query.path.split('/').peekable();
while let Some(segment) = segments.next() {
if segment.is_empty() {
current = "/".to_string();
parts.push(current.clone());
} else if segments.peek().is_some() {
if current != "/" {
current.push('/');
}
current.push_str(segment);
parts.push(current.clone());
}
let normalized_path = PathBuf::from(query.path);
for component in normalized_path.components() {
let part = component.as_os_str().to_string_lossy().to_string();
parts.push(part);

Copilot uses AI. Check for mistakes.
}

let mut file_tree = HashMap::new();

for part in parts {
let path = part.as_ref();
let handler = state.api_handler(path).await?;
let tree_items = handler.get_tree_info(path).await?;
file_tree.insert(
part,
FileTreeItem {
total_count: tree_items.len(),
tree_items,
},
);
}

let tree_items = state
.api_handler(query.path.as_ref())
.await?
.get_tree_info(query.path.into())
.get_tree_info(query.path.as_ref())
.await?;
Ok(Json(CommonResult::success(Some(data))))
Ok(Json(CommonResult::success(Some(TreeResponse {
file_tree,
tree_items,
}))))
}

/// List matching trees with commit msg by query
Expand All @@ -167,7 +202,7 @@ async fn get_tree_commit_info(
state: State<MonoApiServiceState>,
) -> Result<Json<CommonResult<Vec<TreeCommitItem>>>, ApiError> {
let data = state
.api_handler(query.path.clone().into())
.api_handler(query.path.as_ref())
.await?
.get_tree_commit_info(query.path.into())
.await?;
Expand All @@ -191,7 +226,7 @@ async fn get_tree_content_hash(
state: State<MonoApiServiceState>,
) -> Result<Json<CommonResult<Vec<TreeHashItem>>>, ApiError> {
let data = state
.api_handler(query.path.clone().into())
.api_handler(query.path.as_ref())
.await?
.get_tree_content_hash(query.path.into())
.await?;
Expand Down Expand Up @@ -223,7 +258,7 @@ async fn get_tree_dir_hash(
let target_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");

let data = state
.api_handler(parent_path.clone().into())
.api_handler(parent_path.as_ref())
.await?
.get_tree_dir_hash(parent_path.into(), target_name)
.await?;
Expand Down Expand Up @@ -259,7 +294,7 @@ pub async fn get_tree_file(
Query(query): Query<TreeQuery>,
) -> Result<Response, ApiError> {
let data = state
.api_handler(query.path.clone().into())
.api_handler(query.path.as_ref())
.await?
.get_binary_tree_by_path(std::path::Path::new(&query.path), query.oid)
.await?;
Expand Down
4 changes: 2 additions & 2 deletions mono/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use oauth2::{
},
Client, EndpointNotSet, EndpointSet, StandardRevocableToken,
};
use std::path::PathBuf;
use std::path::Path;

use ceres::{
api_service::{
Expand Down Expand Up @@ -102,7 +102,7 @@ impl MonoApiServiceState {
self.storage.user_storage()
}

async fn api_handler(&self, path: PathBuf) -> Result<Box<dyn ApiHandler>, ProtocolError> {
async fn api_handler(&self, path: &Path) -> Result<Box<dyn ApiHandler>, ProtocolError> {
let import_dir = self.storage.config().monorepo.import_dir.clone();
if path.starts_with(&import_dir) && path != import_dir {
if let Some(model) = self
Expand Down
22 changes: 16 additions & 6 deletions moon/packages/types/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3133,12 +3133,11 @@ export type CommonResultString = {
req_result: boolean
}

export type CommonResultVecTreeBriefItem = {
export type CommonResultTreeResponse = {
data?: {
content_type: string
name: string
path: string
}[]
file_tree: Record<string, FileTreeItem>
tree_items: TreeBriefItem[]
}
err_message: string
req_result: boolean
}
Expand Down Expand Up @@ -3195,6 +3194,12 @@ export type CreateFileInfo = {
path: string
}

export type FileTreeItem = {
/** @min 0 */
total_count: number
tree_items: TreeBriefItem[]
}

export type FilesChangedList = {
content: string
mui_trees: MuiTreeNode[]
Expand Down Expand Up @@ -3363,6 +3368,11 @@ export type TreeHashItem = {
oid: string
}

export type TreeResponse = {
file_tree: Record<string, FileTreeItem>
tree_items: TreeBriefItem[]
}

export type UserInfo = {
avatar_url: string
display_name: string
Expand Down Expand Up @@ -4561,7 +4571,7 @@ export type GetApiTreeParams = {
path?: string
}

export type GetApiTreeData = CommonResultVecTreeBriefItem
export type GetApiTreeData = CommonResultTreeResponse

export type GetApiTreeCommitInfoParams = {
refs?: string
Expand Down
Loading