diff --git a/ceres/src/api_service/import_api_service.rs b/ceres/src/api_service/import_api_service.rs index 67907a35f..bff4228df 100644 --- a/ceres/src/api_service/import_api_service.rs +++ b/ceres/src/api_service/import_api_service.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; +use std::path::Component; use std::path::Path; use std::path::PathBuf; @@ -8,6 +9,7 @@ use async_trait::async_trait; use jupiter::context::Context; use mercury::errors::GitError; +use mercury::hash::SHA1; use mercury::internal::object::commit::Commit; use mercury::internal::object::tree::Tree; use mercury::internal::object::tree::TreeItem; @@ -91,6 +93,15 @@ impl ApiHandler for ImportApiService { .into() } + async fn get_commit_by_hash(&self, hash: &str) -> Option { + let storage = self.context.services.git_db_storage.clone(); + let commit = storage + .get_commit_by_hash(self.repo.repo_id, hash) + .await + .unwrap(); + commit.map(|x| x.into()) + } + async fn get_tree_relate_commit(&self, t_hash: &str) -> Commit { let storage = self.context.services.git_db_storage.clone(); let tree_info = storage @@ -106,36 +117,6 @@ impl ApiHandler for ImportApiService { .into() } - async fn add_trees_to_map( - &self, - item_to_commit: &mut HashMap, - hashes: Vec, - ) { - let storage = self.context.services.git_db_storage.clone(); - let trees = storage - .get_trees_by_hashes(self.repo.repo_id, hashes) - .await - .unwrap(); - for tree in trees { - item_to_commit.insert(tree.tree_id, tree.commit_id); - } - } - - async fn add_blobs_to_map( - &self, - item_to_commit: &mut HashMap, - hashes: Vec, - ) { - let storage = self.context.services.git_db_storage.clone(); - let blobs = storage - .get_blobs_by_hashes(self.repo.repo_id, hashes) - .await - .unwrap(); - for blob in blobs { - item_to_commit.insert(blob.blob_id, blob.commit_id); - } - } - async fn get_commits_by_hashes(&self, c_hashes: Vec) -> Result, GitError> { let storage = self.context.services.git_db_storage.clone(); let commits = storage @@ -145,6 +126,29 @@ impl ApiHandler for ImportApiService { Ok(commits.into_iter().map(|x| x.into()).collect()) } + async fn item_to_commit_map( + &self, + path: PathBuf, + ) -> Result>, GitError> { + let mut cache = GitObjectCache::default(); + let root_commit = self.get_root_commit().await; + match self.search_tree_by_path(&path).await? { + Some(tree) => { + let mut result: HashMap> = HashMap::new(); + for item in tree.tree_items { + let commit = self + .traverse_commit_history(&path, &root_commit, &item, &mut cache) + .await; + result.insert(item, Some(commit)); + } + Ok(result) + } + None => Ok(HashMap::new()), + } + } +} + +impl ImportApiService { async fn traverse_commit_history( &self, path: &Path, @@ -166,20 +170,76 @@ impl ApiHandler for ImportApiService { .await .unwrap(); if reachable { - let mut p_ids = vec![]; for p_id in commit.parent_commit_ids.clone() { if !visited.contains(&p_id) { - p_ids.push(p_id.to_string()); + let p_commit = self.get_commit_from_cache(p_id, cache).await.unwrap(); + p_stack.push_back(p_commit); visited.insert(p_id); } } if target_commit.committer.timestamp > commit.committer.timestamp { target_commit = commit.clone(); } - let parent_commits = self.get_commits_by_hashes(p_ids).await.unwrap(); - p_stack.extend(parent_commits); } } target_commit } + + async fn get_tree_from_cache(&self, oid: SHA1, cache: &mut GitObjectCache) -> Tree { + if let Some(tree) = cache.trees.get(&oid) { + return tree.clone(); + } + let tree = self.get_tree_by_hash(&oid.to_string()).await; + cache.trees.insert(oid, tree.clone()); + tree + } + + async fn get_commit_from_cache( + &self, + oid: SHA1, + cache: &mut GitObjectCache, + ) -> Result { + if let Some(commit) = cache.commits.get(&oid) { + return Ok(commit.clone()); + } + match self.get_commit_by_hash(&oid.to_string()).await { + Some(c) => { + cache.commits.insert(oid, c.clone()); + Ok(c) + } + None => Err(GitError::InvalidCommitObject), + } + } + + async fn reachable_in_tree( + &self, + root_tree: &Tree, + path: &Path, + target: &TreeItem, + cache: &mut GitObjectCache, + ) -> Result { + let relative_path = self.strip_relative(path).unwrap(); + let mut search_tree = root_tree.clone(); + // first find search tree by path + for component in relative_path.components() { + // root tree already found + if component != Component::RootDir { + let target_name = component.as_os_str().to_str().unwrap(); + let search_res = search_tree + .tree_items + .iter() + .find(|x| x.name == target_name); + if let Some(search_res) = search_res { + search_tree = self.get_tree_from_cache(search_res.id, cache).await; + } else { + return Ok(false); + } + } + } + // check item exist under search tree + if search_tree.tree_items.iter().any(|x| x == target) { + return Ok(true); + } + Ok(false) + } } diff --git a/ceres/src/api_service/mod.rs b/ceres/src/api_service/mod.rs index 19babcdf1..dd59bc61d 100644 --- a/ceres/src/api_service/mod.rs +++ b/ceres/src/api_service/mod.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashMap, HashSet, VecDeque}, + collections::{HashMap, VecDeque}, path::{Component, Path, PathBuf}, }; @@ -19,7 +19,7 @@ use mercury::{ }; use crate::model::git::{ - CreateFileInfo, LatestCommitInfo, TreeBriefItem, TreeCommitItem, UserInfo, + CreateFileInfo, LatestCommitInfo, TreeBriefItem, TreeCommitItem, TreeHashItem, }; pub mod import_api_service; @@ -28,6 +28,7 @@ pub mod mono_api_service; #[derive(Debug, Default, Clone)] pub struct GitObjectCache { trees: HashMap, + commits: HashMap, } #[async_trait] @@ -69,39 +70,12 @@ pub trait ApiHandler: Send + Sync { async fn get_tree_by_hash(&self, hash: &str) -> Tree; - async fn get_tree_from_cache(&self, oid: SHA1, cache: &mut GitObjectCache) -> Tree { - if let Some(tree) = cache.trees.get(&oid) { - return tree.clone(); - } - let tree = self.get_tree_by_hash(&oid.to_string()).await; - cache.trees.insert(oid, tree.clone()); - tree - } + async fn get_commit_by_hash(&self, hash: &str) -> Option; async fn get_tree_relate_commit(&self, t_hash: &str) -> Commit; - async fn add_trees_to_map( - &self, - item_to_commit: &mut HashMap, - hashes: Vec, - ); - - async fn add_blobs_to_map( - &self, - item_to_commit: &mut HashMap, - hashes: Vec, - ); - async fn get_commits_by_hashes(&self, c_hashes: Vec) -> Result, GitError>; - async fn traverse_commit_history( - &self, - path: &Path, - commit: &Commit, - target: &TreeItem, - cache: &mut GitObjectCache, - ) -> Commit; - async fn get_blob_as_string(&self, file_path: PathBuf) -> Result, GitError> { let filename = file_path.file_name().unwrap().to_str().unwrap(); let parent = file_path.parent().unwrap(); @@ -127,7 +101,7 @@ pub trait ApiHandler: Send + Sync { )); }; let commit = self.get_tree_relate_commit(&tree.id.to_string()).await; - self.convert_commit_to_info(commit) + Ok(commit.into()) } async fn get_tree_info(&self, path: PathBuf) -> Result, GitError> { @@ -149,153 +123,44 @@ pub trait ApiHandler: Send + Sync { } async fn get_tree_commit_info(&self, path: PathBuf) -> Result, 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) - .map(|x| x.id.to_string()) - .collect(), - ) - .await; - - self.add_blobs_to_map( - &mut item_to_commit, - tree.tree_items - .iter() - .filter(|x| x.mode == TreeItemMode::Blob) - .map(|x| x.id.to_string()) - .collect(), - ) - .await; - - let commit_ids: HashSet = item_to_commit.values().cloned().collect(); - let commits = self - .get_commits_by_hashes(commit_ids.into_iter().collect()) - .await - .unwrap(); - let commit_map: HashMap = - commits.into_iter().map(|x| (x.id.to_string(), x)).collect(); - - let mut root_commit: Option = None; - let mut item_to_commit_map: HashMap> = 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 mut items: Vec = item_to_commit_map - .into_iter() - .map(TreeCommitItem::from) - .collect(); - // sort with type and date - items.sort_by(|a, b| { - a.content_type - .cmp(&b.content_type) - .then(a.name.cmp(&b.name)) - }); - Ok(items) - } - None => Ok(Vec::new()), - } + let item_to_commit_map = self.item_to_commit_map(path).await?; + + let mut items: Vec = item_to_commit_map + .into_iter() + .map(TreeCommitItem::from) + .collect(); + // sort with type and name + items.sort_by(|a, b| { + a.content_type + .cmp(&b.content_type) + .then(a.name.cmp(&b.name)) + }); + Ok(items) } + async fn item_to_commit_map( + &self, + path: PathBuf, + ) -> Result>, GitError>; + /// the dir's hash as same as old,file's hash is the content hash /// may think about change dir'hash as the content /// for now,only change the file's hash - async fn get_tree_content_hash(&self, path: PathBuf) -> Result, GitError> { - let mut cache = GitObjectCache::default(); + async fn get_tree_content_hash(&self, path: PathBuf) -> Result, GitError> { 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) - .map(|x| x.id.to_string()) - .collect(), - ) - .await; - - // self.add_blobs_to_map( - // &mut item_to_commit, - // tree.tree_items - // .iter() - // .filter(|x| x.mode == TreeItemMode::Blob) - // .map(|x| x.id.to_string()) - // .collect(), - // ) - // .await; - let content_items: Vec = tree + let mut items: Vec = tree .tree_items - .iter() - .filter(|x| x.mode == TreeItemMode::Blob) - .map(|x| TreeCommitItem { - oid: x.id.to_string(), - name: x.name.clone(), - content_type: "file".to_owned(), - message: String::new(), - date: String::new(), - }) - .collect(); - let commit_ids: HashSet = item_to_commit.values().cloned().collect(); - let commits = self - .get_commits_by_hashes(commit_ids.into_iter().collect()) - .await - .unwrap(); - let commit_map: HashMap = - commits.into_iter().map(|x| (x.id.to_string(), x)).collect(); - - let mut root_commit: Option = None; - let mut item_to_commit_map: HashMap> = HashMap::new(); - for item in tree.tree_items { - if item.mode == TreeItemMode::Blob { - continue; - } - 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 mut items: Vec = item_to_commit_map .into_iter() - .map(TreeCommitItem::from) + .map(TreeHashItem::from) .collect(); - // sort with type and date + + // sort with type and name items.sort_by(|a, b| { a.content_type .cmp(&b.content_type) .then(a.name.cmp(&b.name)) }); - items.extend(content_items); Ok(items) } None => Ok(Vec::new()), @@ -307,51 +172,14 @@ pub trait ApiHandler: Send + Sync { &self, path: PathBuf, dir_name: &str, - ) -> Result, GitError> { - let mut cache = GitObjectCache::default(); + ) -> Result, GitError> { 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 = item_to_commit.values().cloned().collect(); - let commits = self - .get_commits_by_hashes(commit_ids.into_iter().collect()) - .await - .unwrap(); - let commit_map: HashMap = - commits.into_iter().map(|x| (x.id.to_string(), x)).collect(); - - let mut root_commit: Option = None; - let mut item_to_commit_map: HashMap> = 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 = item_to_commit_map + let items: Vec = tree + .tree_items .into_iter() - .map(TreeCommitItem::from) + .filter(|x| x.mode == TreeItemMode::Tree && x.name == dir_name) + .map(TreeHashItem::from) .collect(); Ok(items) } @@ -359,28 +187,6 @@ pub trait ApiHandler: Send + Sync { } } - fn convert_commit_to_info(&self, commit: Commit) -> Result { - let message = commit.format_message(); - let committer = UserInfo { - display_name: commit.committer.name, - ..Default::default() - }; - let author = UserInfo { - display_name: commit.author.name, - ..Default::default() - }; - - let res = LatestCommitInfo { - oid: commit.id.to_string(), - date: commit.committer.timestamp.to_string(), - short_message: message, - author, - committer, - status: "success".to_string(), - }; - Ok(res) - } - /// Searches for a tree in the Git repository by its path and returns the trees involved in the update and the target tree. /// /// # Arguments @@ -555,36 +361,4 @@ pub trait ApiHandler: Send + Sync { Ok(saving_trees) } - - async fn reachable_in_tree( - &self, - root_tree: &Tree, - path: &Path, - target: &TreeItem, - cache: &mut GitObjectCache, - ) -> Result { - let relative_path = self.strip_relative(path).unwrap(); - let mut search_tree = root_tree.clone(); - // first find search tree by path - for component in relative_path.components() { - // root tree already found - if component != Component::RootDir { - let target_name = component.as_os_str().to_str().unwrap(); - let search_res = search_tree - .tree_items - .iter() - .find(|x| x.name == target_name); - if let Some(search_res) = search_res { - search_tree = self.get_tree_from_cache(search_res.id, cache).await; - } else { - return Ok(false); - } - } - } - // check item exist under search tree - if search_tree.tree_items.iter().any(|x| x == target) { - return Ok(true); - } - Ok(false) - } } diff --git a/ceres/src/api_service/mono_api_service.rs b/ceres/src/api_service/mono_api_service.rs index 012b8741a..18ad703f0 100644 --- a/ceres/src/api_service/mono_api_service.rs +++ b/ceres/src/api_service/mono_api_service.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{env, fs}; @@ -18,7 +18,7 @@ use mercury::internal::object::blob::Blob; use mercury::internal::object::commit::Commit; use mercury::internal::object::tree::{Tree, TreeItem, TreeItemMode}; -use crate::api_service::{ApiHandler, GitObjectCache}; +use crate::api_service::ApiHandler; use crate::model::git::CreateFileInfo; use crate::protocol::mr::MergeRequest; @@ -149,6 +149,14 @@ impl ApiHandler for MonoApiService { .into() } + async fn get_commit_by_hash(&self, hash: &str) -> Option { + let storage = self.context.services.mono_storage.clone(); + match storage.get_commit_by_hash(hash).await { + Ok(Some(commit)) => Some(commit.into()), + _ => None, + } + } + async fn get_tree_relate_commit(&self, t_hash: &str) -> Commit { let storage = self.context.services.mono_storage.clone(); let tree_info = storage.get_tree_by_hash(t_hash).await.unwrap().unwrap(); @@ -160,44 +168,67 @@ impl ApiHandler for MonoApiService { .into() } - async fn add_trees_to_map( - &self, - item_to_commit: &mut HashMap, - hashes: Vec, - ) { - let storage = self.context.services.mono_storage.clone(); - let trees = storage.get_trees_by_hashes(hashes).await.unwrap(); - for tree in trees { - item_to_commit.insert(tree.tree_id, tree.commit_id); - } - } - - async fn add_blobs_to_map( - &self, - item_to_commit: &mut HashMap, - hashes: Vec, - ) { - let storage = self.context.services.mono_storage.clone(); - let blobs = storage.get_mega_blobs_by_hashes(hashes).await.unwrap(); - for blob in blobs { - item_to_commit.insert(blob.blob_id, blob.commit_id); - } - } - async fn get_commits_by_hashes(&self, c_hashes: Vec) -> Result, GitError> { let storage = self.context.services.mono_storage.clone(); let commits = storage.get_commits_by_hashes(&c_hashes).await.unwrap(); Ok(commits.into_iter().map(|x| x.into()).collect()) } - async fn traverse_commit_history( + async fn item_to_commit_map( &self, - _: &Path, - _: &Commit, - _: &TreeItem, - _: &mut GitObjectCache, - ) -> Commit { - unreachable!() + path: PathBuf, + ) -> Result>, GitError> { + match self.search_tree_by_path(&path).await? { + Some(tree) => { + let mut item_to_commit = HashMap::new(); + + let storage = self.context.services.mono_storage.clone(); + let tree_hashes = tree + .tree_items + .iter() + .filter(|x| x.mode == TreeItemMode::Tree) + .map(|x| x.id.to_string()) + .collect(); + let trees = storage.get_trees_by_hashes(tree_hashes).await.unwrap(); + for tree in trees { + item_to_commit.insert(tree.tree_id, tree.commit_id); + } + + let blob_hashes = tree + .tree_items + .iter() + .filter(|x| x.mode == TreeItemMode::Blob) + .map(|x| x.id.to_string()) + .collect(); + let blobs = storage.get_mega_blobs_by_hashes(blob_hashes).await.unwrap(); + for blob in blobs { + item_to_commit.insert(blob.blob_id, blob.commit_id); + } + + let commit_ids: HashSet = item_to_commit.values().cloned().collect(); + let commits = self + .get_commits_by_hashes(commit_ids.into_iter().collect()) + .await + .unwrap(); + let commit_map: HashMap = + commits.into_iter().map(|x| (x.id.to_string(), x)).collect(); + + let mut result: HashMap> = 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) { + Some(commit.to_owned()) + } else { + tracing::warn!("failed fetch from commit map: {}", commit_id); + None + }; + result.insert(item, commit); + } + } + Ok(result) + } + None => Ok(HashMap::new()), + } } } diff --git a/ceres/src/model/git.rs b/ceres/src/model/git.rs index 9ec007ff8..b8680ffc7 100644 --- a/ceres/src/model/git.rs +++ b/ceres/src/model/git.rs @@ -48,6 +48,28 @@ pub struct LatestCommitInfo { pub status: String, } +impl From for LatestCommitInfo { + fn from(commit: Commit) -> Self { + let message = commit.format_message(); + let committer = UserInfo { + display_name: commit.committer.name, + ..Default::default() + }; + let author = UserInfo { + display_name: commit.author.name, + ..Default::default() + }; + Self { + oid: commit.id.to_string(), + date: commit.committer.timestamp.to_string(), + short_message: message, + author, + committer, + status: "success".to_string(), + } + } +} + #[derive(Serialize, Deserialize, ToSchema)] pub struct UserInfo { pub display_name: String, @@ -65,10 +87,10 @@ impl Default for UserInfo { #[derive(Serialize, Deserialize, ToSchema)] pub struct TreeCommitItem { - pub oid: String, + pub commit_id: String, pub name: String, pub content_type: String, - pub message: String, + pub commit_message: String, pub date: String, } @@ -81,11 +103,11 @@ impl From<(TreeItem, Option)> for TreeCommitItem { } else { "file".to_owned() }, - oid: commit + commit_id: commit .as_ref() .map(|x| x.id.to_string()) .unwrap_or_default(), - message: commit + commit_message: commit .as_ref() .map(|x| x.format_message()) .unwrap_or_default(), @@ -97,6 +119,27 @@ impl From<(TreeItem, Option)> for TreeCommitItem { } } +#[derive(Serialize, Deserialize, ToSchema)] +pub struct TreeHashItem { + pub name: String, + pub content_type: String, + pub oid: String, +} + +impl From for TreeHashItem { + fn from(value: TreeItem) -> Self { + Self { + oid: value.id.to_string(), + name: value.name, + content_type: if value.mode == TreeItemMode::Tree { + "directory".to_owned() + } else { + "file".to_owned() + }, + } + } +} + #[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct TreeBriefItem { pub name: String, diff --git a/config/config.toml b/config/config.toml index cd9ef245c..3ca9641fa 100644 --- a/config/config.toml +++ b/config/config.toml @@ -109,8 +109,8 @@ s3_secret_access_key = "" github_client_id = "" github_client_secret = "" -# Used for redirect to ui after login, for example: https://console.gitmono.com -ui_domain = "http://localhost:3000" +# Used for redirect to ui after login, for example: http://app.gitmega.com +ui_domain = "http://local.gitmega.com" # Set your own domain here, for example: .gitmono.com cookie_domain = "localhost" diff --git a/jupiter/src/storage/mod.rs b/jupiter/src/storage/mod.rs index dc5d4a84d..d94cb9ce1 100644 --- a/jupiter/src/storage/mod.rs +++ b/jupiter/src/storage/mod.rs @@ -9,7 +9,7 @@ pub mod raw_db_storage; pub mod relay_storage; pub mod user_storage; -use sea_orm::{sea_query::OnConflict, ActiveModelTrait, ConnectionTrait, EntityTrait}; +use sea_orm::{sea_query::OnConflict, ActiveModelTrait, ConnectionTrait, DbErr, EntityTrait}; use common::errors::MegaError; @@ -50,7 +50,28 @@ where .await } -async fn batch_save_model_with_conflict( +/// Performs batch saving of models in the database with conflict resolution. +/// +/// This function allows saving models in batches while specifying conflict resolution behavior using the `OnConflict` parameter. +/// It is intended for advanced use cases where fine-grained control over conflict handling is required. +/// +/// # Arguments +/// +/// * `connection` - A reference to the database connection. +/// * `save_models` - A vector of models to be saved. +/// * `onconflict` - Specifies the conflict resolution strategy to be used during insertion. +/// +/// # Generic Constraints +/// +/// * `E` - The entity type that implements the `EntityTrait` trait. +/// * `A` - The model type that implements the `ActiveModelTrait` trait and is convertible from the corresponding model type of `E`. +/// +/// # Errors +/// +/// Returns a `MegaError` if an error occurs during the batch save operation. +/// Note: The function ignores `DbErr::RecordNotInserted` errors, which may lead to silent failures. +/// Use this function with caution and ensure that the `OnConflict` parameter is configured correctly to avoid unintended consequences. +pub async fn batch_save_model_with_conflict( connection: &impl ConnectionTrait, save_models: Vec, onconflict: OnConflict, @@ -61,9 +82,18 @@ where { // notice that sqlx not support packets larger than 16MB now let futures = save_models.chunks(1000).map(|chunk| { - E::insert_many(chunk.iter().cloned()) - .on_conflict(onconflict.clone()) - .exec(connection) + let insert = E::insert_many(chunk.iter().cloned()).on_conflict(onconflict.clone()); + let conn = connection; + async move { + match insert.exec(conn).await { + Ok(_) => Ok(()), + Err(DbErr::RecordNotInserted) => { + // ignore not inserted err + Ok(()) + } + Err(e) => Err(e), + } + } }); futures::future::try_join_all(futures).await?; Ok(()) diff --git a/mono/src/api/api_router.rs b/mono/src/api/api_router.rs index f7ac64cbe..37c31674a 100644 --- a/mono/src/api/api_router.rs +++ b/mono/src/api/api_router.rs @@ -12,8 +12,7 @@ use http::StatusCode; use ceres::{ api_service::ApiHandler, model::git::{ - BlobContentQuery, CodePreviewQuery, CreateFileInfo, LatestCommitInfo, TreeBriefItem, - TreeCommitItem, TreeQuery, + BlobContentQuery, CodePreviewQuery, CreateFileInfo, LatestCommitInfo, TreeBriefItem, TreeCommitItem, TreeHashItem, TreeQuery }, }; use common::model::CommonResult; @@ -182,14 +181,14 @@ async fn get_tree_commit_info( CodePreviewQuery ), responses( - (status = 200, body = CommonResult>, content_type = "application/json") + (status = 200, body = CommonResult>, content_type = "application/json") ), tag = GIT_TAG )] async fn get_tree_content_hash( Query(query): Query, state: State, -) -> Result>>, ApiError> { +) -> Result>>, ApiError> { let data = state .api_handler(query.path.clone().into()) .await? @@ -206,14 +205,14 @@ async fn get_tree_content_hash( CodePreviewQuery ), responses( - (status = 200, body = CommonResult>, content_type = "application/json") + (status = 200, body = CommonResult>, content_type = "application/json") ), tag = GIT_TAG )] async fn get_tree_dir_hash( Query(query): Query, state: State, -) -> Result>>, ApiError> { +) -> Result>>, ApiError> { let path = std::path::Path::new(&query.path); let parent_path = path .parent() diff --git a/mono/src/server/https_server.rs b/mono/src/server/https_server.rs index cf7f040ff..5de3f7ea6 100644 --- a/mono/src/server/https_server.rs +++ b/mono/src/server/https_server.rs @@ -10,6 +10,7 @@ use axum::http::{self, Request, Uri}; use axum::response::Response; use axum::routing::get; use axum::Router; +use http::HeaderValue; use lazy_static::lazy_static; use regex::Regex; use tower::ServiceBuilder; @@ -91,13 +92,15 @@ pub async fn app(context: Context, host: String, port: u16) -> Router { context: context.clone(), }; + let config = context.config.clone(); let api_state = MonoApiServiceState { context: context.clone(), - oauth_client: Some(oauth_client(context.config.oauth.clone().unwrap()).unwrap()), + oauth_client: Some(oauth_client(config.oauth.clone().unwrap()).unwrap()), store: Some(MemoryStore::new()), listen_addr: format!("http://{}:{}", host, port), }; + let cors_origin = HeaderValue::from_str(&config.oauth.clone().unwrap().ui_domain).expect("ui_domain in config not set"); // add RequestDecompressionLayer for handle gzip encode // add TraceLayer for log record // add CorsLayer to add cors header @@ -111,10 +114,9 @@ pub async fn app(context: Context, host: String, port: u16) -> Router { // Using Regular Expressions for Path Matching in Protocol .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, - http::header::CONTENT_TYPE, - ])), + ServiceBuilder::new().layer(CorsLayer::new().allow_origin(cors_origin).allow_headers( + vec![http::header::AUTHORIZATION, http::header::CONTENT_TYPE], + ).allow_methods(Any)), ) .layer(TraceLayer::new_for_http()) .layer(RequestDecompressionLayer::new()) diff --git a/moon/packages/types/generated.ts b/moon/packages/types/generated.ts index 1b461f479..76cfc0b6e 100644 --- a/moon/packages/types/generated.ts +++ b/moon/packages/types/generated.ts @@ -3125,9 +3125,19 @@ export type CommonResultVecTreeBriefItem = { export type CommonResultVecTreeCommitItem = { data?: { + commit_id: string + commit_message: string content_type: string date: string - message: string + name: string + }[] + err_message: string + req_result: boolean +} + +export type CommonResultVecTreeHashItem = { + data?: { + content_type: string name: string oid: string }[] @@ -3292,9 +3302,15 @@ export type TreeBriefItem = { } export type TreeCommitItem = { + commit_id: string + commit_message: string content_type: string date: string - message: string + name: string +} + +export type TreeHashItem = { + content_type: string name: string oid: string } @@ -4498,6 +4514,20 @@ export type GetApiTreeCommitInfoParams = { export type GetApiTreeCommitInfoData = CommonResultVecTreeCommitItem +export type GetApiTreeContentHashParams = { + refs?: string + path?: string +} + +export type GetApiTreeContentHashData = CommonResultVecTreeHashItem + +export type GetApiTreeDirHashParams = { + refs?: string + path?: string +} + +export type GetApiTreeDirHashData = CommonResultVecTreeHashItem + export type GetApiTreePathCanCloneParams = { path?: string } @@ -13283,6 +13313,55 @@ export class Api extends HttpClient { + const base = 'GET:/api/v1/tree/content-hash' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (params: GetApiTreeContentHashParams) => + dataTaggedQueryKey([base, params]), + request: (query: GetApiTreeContentHashParams, params: RequestParams = {}) => + this.request({ + path: `/api/v1/tree/content-hash`, + method: 'GET', + query: query, + ...params + }) + } + }, + + /** + * No description + * + * @tags git + * @name GetApiTreeDirHash + * @summary return the dir's hash + * @request GET:/api/v1/tree/dir-hash + */ + getApiTreeDirHash: () => { + const base = 'GET:/api/v1/tree/dir-hash' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (params: GetApiTreeDirHashParams) => dataTaggedQueryKey([base, params]), + request: (query: GetApiTreeDirHashParams, params: RequestParams = {}) => + this.request({ + path: `/api/v1/tree/dir-hash`, + method: 'GET', + query: query, + ...params + }) + } + }, + /** * No description *