feat: init
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
use crate::error::AppError;
|
||||
use crate::service::RepoService;
|
||||
use crate::session::Session;
|
||||
|
||||
impl RepoService {
|
||||
pub async fn git_blame(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
revision: &str,
|
||||
path: &str,
|
||||
page_size: u32,
|
||||
) -> Result<crate::pb::repo::BlameResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.blame;
|
||||
let resp = svc
|
||||
.blame(tonic::Request::new(crate::pb::repo::BlameRequest {
|
||||
repository: Some(header),
|
||||
revision: Some(crate::pb::repo::ObjectSelector {
|
||||
selector: Some(crate::pb::repo::object_selector::Selector::Revision(
|
||||
crate::pb::repo::ObjectName {
|
||||
revision: revision.to_string(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
path: path.to_string(),
|
||||
range: None,
|
||||
options: None,
|
||||
pagination: Some(crate::pb::repo::Pagination {
|
||||
page_size,
|
||||
page_token: String::new(),
|
||||
}),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
use crate::error::AppError;
|
||||
use crate::service::RepoService;
|
||||
use crate::session::Session;
|
||||
|
||||
impl RepoService {
|
||||
pub async fn git_list_branches(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
pattern: Option<String>,
|
||||
page_size: u32,
|
||||
page_token: Option<String>,
|
||||
) -> Result<crate::pb::repo::ListBranchesResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.branch;
|
||||
let resp = svc
|
||||
.list_branches(tonic::Request::new(crate::pb::repo::ListBranchesRequest {
|
||||
repository: Some(header),
|
||||
pattern: pattern.unwrap_or_default(),
|
||||
merged_into_head: false,
|
||||
not_merged_into_head: false,
|
||||
pagination: Some(crate::pb::repo::Pagination {
|
||||
page_size,
|
||||
page_token: page_token.unwrap_or_default(),
|
||||
}),
|
||||
sort_direction: crate::pb::repo::SortDirection::Desc as i32,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_get_branch(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
branch_name: &str,
|
||||
) -> Result<crate::pb::repo::Branch, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.branch;
|
||||
let resp = svc
|
||||
.get_branch(tonic::Request::new(crate::pb::repo::GetBranchRequest {
|
||||
repository: Some(header),
|
||||
name: branch_name.to_string(),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_create_branch(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
branch_name: &str,
|
||||
start_point: &str,
|
||||
) -> Result<crate::pb::repo::Branch, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Member)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.branch;
|
||||
let resp = svc
|
||||
.create_branch(tonic::Request::new(crate::pb::repo::CreateBranchRequest {
|
||||
repository: Some(header),
|
||||
name: branch_name.to_string(),
|
||||
start_point: Some(crate::pb::repo::ObjectSelector {
|
||||
selector: Some(crate::pb::repo::object_selector::Selector::Revision(
|
||||
crate::pb::repo::ObjectName {
|
||||
revision: start_point.to_string(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
force: false,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_delete_branch(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
branch_name: &str,
|
||||
) -> Result<(), AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Admin)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.branch;
|
||||
svc.delete_branch(tonic::Request::new(crate::pb::repo::DeleteBranchRequest {
|
||||
repository: Some(header),
|
||||
name: branch_name.to_string(),
|
||||
force: false,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn git_compare_branches(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
source_branch: &str,
|
||||
target_branch: &str,
|
||||
) -> Result<crate::pb::repo::CompareBranchResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.branch;
|
||||
let resp = svc
|
||||
.compare_branch(tonic::Request::new(crate::pb::repo::CompareBranchRequest {
|
||||
repository: Some(header),
|
||||
source_branch: source_branch.to_string(),
|
||||
target_branch: target_branch.to_string(),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
use crate::error::AppError;
|
||||
use crate::service::RepoService;
|
||||
use crate::session::Session;
|
||||
|
||||
impl RepoService {
|
||||
pub async fn git_list_commits(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
revision: &str,
|
||||
path: Option<String>,
|
||||
page_size: u32,
|
||||
) -> Result<crate::pb::repo::ListCommitsResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.commit;
|
||||
let resp = svc
|
||||
.list_commits(tonic::Request::new(crate::pb::repo::ListCommitsRequest {
|
||||
repository: Some(header),
|
||||
revision: Some(crate::pb::repo::ObjectSelector {
|
||||
selector: Some(crate::pb::repo::object_selector::Selector::Revision(
|
||||
crate::pb::repo::ObjectName {
|
||||
revision: revision.to_string(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
path: path.unwrap_or_default(),
|
||||
since: None,
|
||||
until: None,
|
||||
first_parent: false,
|
||||
all: false,
|
||||
reverse: false,
|
||||
max_parents: 0,
|
||||
min_parents: 0,
|
||||
pagination: Some(crate::pb::repo::Pagination {
|
||||
page_size,
|
||||
page_token: String::new(),
|
||||
}),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_get_commit(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
revision: &str,
|
||||
) -> Result<crate::pb::repo::Commit, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.commit;
|
||||
let resp = svc
|
||||
.get_commit(tonic::Request::new(crate::pb::repo::GetCommitRequest {
|
||||
repository: Some(header),
|
||||
revision: Some(crate::pb::repo::ObjectSelector {
|
||||
selector: Some(crate::pb::repo::object_selector::Selector::Revision(
|
||||
crate::pb::repo::ObjectName {
|
||||
revision: revision.to_string(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
include_stats: true,
|
||||
include_raw: false,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
use crate::error::AppError;
|
||||
use crate::service::RepoService;
|
||||
use crate::session::Session;
|
||||
|
||||
fn rev(revision: &str) -> crate::pb::repo::ObjectSelector {
|
||||
crate::pb::repo::ObjectSelector {
|
||||
selector: Some(crate::pb::repo::object_selector::Selector::Revision(
|
||||
crate::pb::repo::ObjectName {
|
||||
revision: revision.to_string(),
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
impl RepoService {
|
||||
pub async fn git_diff(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
base: &str,
|
||||
head: &str,
|
||||
page_size: u32,
|
||||
) -> Result<crate::pb::repo::GetDiffResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.diff;
|
||||
let resp = svc
|
||||
.get_diff(tonic::Request::new(crate::pb::repo::GetDiffRequest {
|
||||
repository: Some(header),
|
||||
base: Some(rev(base)),
|
||||
head: Some(rev(head)),
|
||||
options: None,
|
||||
pagination: Some(crate::pb::repo::Pagination {
|
||||
page_size,
|
||||
page_token: String::new(),
|
||||
}),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_diff_stats(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
base: &str,
|
||||
head: &str,
|
||||
) -> Result<crate::pb::repo::DiffStats, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.diff;
|
||||
let resp = svc
|
||||
.get_diff_stats(tonic::Request::new(crate::pb::repo::GetDiffStatsRequest {
|
||||
repository: Some(header),
|
||||
base: Some(rev(base)),
|
||||
head: Some(rev(head)),
|
||||
options: None,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::AppError;
|
||||
use crate::service::RepoService;
|
||||
use crate::session::Session;
|
||||
|
||||
fn rev(r: &str) -> crate::pb::repo::ObjectSelector {
|
||||
crate::pb::repo::ObjectSelector {
|
||||
selector: Some(crate::pb::repo::object_selector::Selector::Revision(
|
||||
crate::pb::repo::ObjectName {
|
||||
revision: r.to_string(),
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, utoipa::ToSchema)]
|
||||
pub struct MergeParams {
|
||||
pub target_branch: String,
|
||||
pub source: String,
|
||||
pub message: Option<String>,
|
||||
pub squash: Option<bool>,
|
||||
pub no_commit: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, utoipa::ToSchema)]
|
||||
pub struct RebaseParams {
|
||||
pub branch: String,
|
||||
pub upstream: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, utoipa::ToSchema)]
|
||||
pub struct CherryPickParams {
|
||||
pub commit: String,
|
||||
pub branch: String,
|
||||
pub message: Option<String>,
|
||||
pub mainline: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, utoipa::ToSchema)]
|
||||
pub struct RevertParams {
|
||||
pub commit: String,
|
||||
pub branch: String,
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, utoipa::ToSchema)]
|
||||
pub struct CreateCommitParams {
|
||||
pub branch: String,
|
||||
pub message: String,
|
||||
pub start_revision: Option<String>,
|
||||
pub actions: Vec<CommitAction>,
|
||||
pub force: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, utoipa::ToSchema)]
|
||||
pub struct CommitAction {
|
||||
pub action: String,
|
||||
pub file_path: String,
|
||||
pub previous_path: Option<String>,
|
||||
pub content: Option<String>,
|
||||
pub executable: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, utoipa::ToSchema)]
|
||||
pub struct CompareParams {
|
||||
pub base: String,
|
||||
pub head: String,
|
||||
pub page_size: Option<u32>,
|
||||
}
|
||||
|
||||
impl RepoService {
|
||||
pub async fn git_check_merge(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
target: &str,
|
||||
source: &str,
|
||||
) -> Result<crate::pb::repo::MergeResult, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.merge;
|
||||
let resp = svc
|
||||
.check_merge(tonic::Request::new(crate::pb::repo::CheckMergeRequest {
|
||||
repository: Some(header),
|
||||
target: Some(rev(target)),
|
||||
source: Some(rev(source)),
|
||||
options: None,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_merge(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
params: MergeParams,
|
||||
) -> Result<crate::pb::repo::MergeResult, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Member)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let message = params
|
||||
.message
|
||||
.unwrap_or_else(|| format!("Merge {} into {}", params.source, params.target_branch));
|
||||
|
||||
let options = crate::pb::repo::MergeOptions {
|
||||
strategy: crate::pb::repo::merge_options::Strategy::MergeStrategyOrt as i32,
|
||||
fast_forward:
|
||||
crate::pb::repo::merge_options::FastForwardMode::MergeFastForwardModeAllowed as i32,
|
||||
squash: params.squash.unwrap_or(false),
|
||||
no_commit: params.no_commit.unwrap_or(false),
|
||||
allow_unrelated_histories: false,
|
||||
strategy_options: vec![],
|
||||
};
|
||||
|
||||
let mut svc = self.git_client(&repo)?.merge;
|
||||
let resp = svc
|
||||
.merge(tonic::Request::new(crate::pb::repo::MergeRequest {
|
||||
repository: Some(header),
|
||||
target_branch: params.target_branch,
|
||||
source: Some(rev(¶ms.source)),
|
||||
committer: None,
|
||||
message,
|
||||
options: Some(options),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_rebase(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
params: RebaseParams,
|
||||
) -> Result<crate::pb::repo::RebaseResult, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Member)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.merge;
|
||||
let resp = svc
|
||||
.rebase(tonic::Request::new(crate::pb::repo::RebaseRequest {
|
||||
repository: Some(header),
|
||||
branch: params.branch,
|
||||
upstream: Some(rev(¶ms.upstream)),
|
||||
committer: None,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_cherry_pick(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
params: CherryPickParams,
|
||||
) -> Result<crate::pb::repo::CreateCommitResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Member)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let message = params
|
||||
.message
|
||||
.unwrap_or_else(|| format!("Cherry-pick {}", params.commit));
|
||||
let mut svc = self.git_client(&repo)?.commit;
|
||||
let resp = svc
|
||||
.cherry_pick_commit(tonic::Request::new(
|
||||
crate::pb::repo::CherryPickCommitRequest {
|
||||
repository: Some(header),
|
||||
commit: Some(rev(¶ms.commit)),
|
||||
branch: params.branch,
|
||||
committer: None,
|
||||
message,
|
||||
mainline: params.mainline.unwrap_or(1),
|
||||
},
|
||||
))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_revert(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
params: RevertParams,
|
||||
) -> Result<crate::pb::repo::CreateCommitResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Member)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let message = params
|
||||
.message
|
||||
.unwrap_or_else(|| format!("Revert {}", params.commit));
|
||||
let mut svc = self.git_client(&repo)?.commit;
|
||||
let resp = svc
|
||||
.revert_commit(tonic::Request::new(crate::pb::repo::RevertCommitRequest {
|
||||
repository: Some(header),
|
||||
commit: Some(rev(¶ms.commit)),
|
||||
branch: params.branch,
|
||||
committer: None,
|
||||
message,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_create_commit(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
params: CreateCommitParams,
|
||||
) -> Result<crate::pb::repo::CreateCommitResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Member)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
|
||||
let actions: Vec<crate::pb::repo::CreateCommitAction> = params
|
||||
.actions
|
||||
.iter()
|
||||
.map(|a| {
|
||||
let action = match a.action.as_str() {
|
||||
"create" => {
|
||||
crate::pb::repo::create_commit_action::Action::CreateCommitActionCreate
|
||||
as i32
|
||||
}
|
||||
"update" => {
|
||||
crate::pb::repo::create_commit_action::Action::CreateCommitActionUpdate
|
||||
as i32
|
||||
}
|
||||
"delete" => {
|
||||
crate::pb::repo::create_commit_action::Action::CreateCommitActionDelete
|
||||
as i32
|
||||
}
|
||||
"move" => {
|
||||
crate::pb::repo::create_commit_action::Action::CreateCommitActionMove as i32
|
||||
}
|
||||
"chmod" => {
|
||||
crate::pb::repo::create_commit_action::Action::CreateCommitActionChmod
|
||||
as i32
|
||||
}
|
||||
_ => {
|
||||
crate::pb::repo::create_commit_action::Action::CreateCommitActionUnspecified
|
||||
as i32
|
||||
}
|
||||
};
|
||||
crate::pb::repo::CreateCommitAction {
|
||||
action,
|
||||
file_path: a.file_path.clone(),
|
||||
previous_path: a.previous_path.clone().unwrap_or_default(),
|
||||
content: a
|
||||
.content
|
||||
.as_ref()
|
||||
.map(|c| c.as_bytes().to_vec())
|
||||
.unwrap_or_default(),
|
||||
encoding: String::new(),
|
||||
executable: a.executable.unwrap_or(false),
|
||||
last_commit_oid: None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let start_revision = params.start_revision.as_ref().map(|r| rev(r));
|
||||
let mut svc = self.git_client(&repo)?.commit;
|
||||
let resp = svc
|
||||
.create_commit(tonic::Request::new(crate::pb::repo::CreateCommitRequest {
|
||||
repository: Some(header),
|
||||
branch: params.branch,
|
||||
message: params.message,
|
||||
author: None,
|
||||
committer: None,
|
||||
actions,
|
||||
start_revision,
|
||||
force: params.force.unwrap_or(false),
|
||||
trailers: vec![],
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_compare_commits(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
params: CompareParams,
|
||||
) -> Result<crate::pb::repo::CompareCommitsResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let page_size = params.page_size.unwrap_or(30);
|
||||
let mut svc = self.git_client(&repo)?.commit;
|
||||
let resp = svc
|
||||
.compare_commits(tonic::Request::new(
|
||||
crate::pb::repo::CompareCommitsRequest {
|
||||
repository: Some(header),
|
||||
base: Some(rev(¶ms.base)),
|
||||
head: Some(rev(¶ms.head)),
|
||||
straight: false,
|
||||
first_parent: false,
|
||||
pagination: Some(crate::pb::repo::Pagination {
|
||||
page_size,
|
||||
page_token: String::new(),
|
||||
}),
|
||||
},
|
||||
))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_list_conflicts(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
target: &str,
|
||||
source: &str,
|
||||
page_size: u32,
|
||||
) -> Result<crate::pb::repo::ListMergeConflictsResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.merge;
|
||||
let resp = svc
|
||||
.list_merge_conflicts(tonic::Request::new(
|
||||
crate::pb::repo::ListMergeConflictsRequest {
|
||||
repository: Some(header),
|
||||
target: Some(rev(target)),
|
||||
source: Some(rev(source)),
|
||||
pagination: Some(crate::pb::repo::Pagination {
|
||||
page_size,
|
||||
page_token: String::new(),
|
||||
}),
|
||||
},
|
||||
))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
pub mod blame;
|
||||
pub mod branch;
|
||||
pub mod commit;
|
||||
pub mod diff;
|
||||
pub mod merge;
|
||||
pub mod repository;
|
||||
pub mod tag;
|
||||
pub mod tree;
|
||||
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::Repo;
|
||||
use crate::models::workspaces::Workspace;
|
||||
use crate::pb::RepoClient;
|
||||
use crate::pb::repo::RepositoryHeader;
|
||||
use crate::service::RepoService;
|
||||
|
||||
impl RepoService {
|
||||
pub(crate) fn repo_header(&self, repo: &Repo, ws: &Workspace) -> RepositoryHeader {
|
||||
RepositoryHeader {
|
||||
storage_name: ws.name.clone(),
|
||||
relative_path: format!("{}.git", repo.name),
|
||||
storage_path: repo.storage_path.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn git_client(&self, repo: &Repo) -> Result<RepoClient, AppError> {
|
||||
self.ctx
|
||||
.registry
|
||||
.get_git_client(&repo.primary_storage_node_id)
|
||||
.ok_or_else(|| AppError::Config("primary git node not available".into()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
use crate::error::AppError;
|
||||
use crate::service::RepoService;
|
||||
use crate::session::Session;
|
||||
|
||||
impl RepoService {
|
||||
pub async fn git_repo_info(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
) -> Result<crate::pb::repo::Repository, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.repository;
|
||||
let resp = svc
|
||||
.get_repository(tonic::Request::new(crate::pb::repo::GetRepositoryRequest {
|
||||
repository: Some(header),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_repo_exists(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
) -> Result<bool, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.repository;
|
||||
let resp = svc
|
||||
.repository_exists(tonic::Request::new(
|
||||
crate::pb::repo::RepositoryExistsRequest {
|
||||
repository: Some(header),
|
||||
},
|
||||
))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner().exists)
|
||||
}
|
||||
|
||||
pub async fn git_repo_stats(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
) -> Result<crate::pb::repo::RepositoryStatistics, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.repository;
|
||||
let resp = svc
|
||||
.get_repository_statistics(tonic::Request::new(
|
||||
crate::pb::repo::RepositoryStatisticsRequest {
|
||||
repository: Some(header),
|
||||
},
|
||||
))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_repo_health(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
) -> Result<crate::pb::repo::RepositoryHealthResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Admin)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.repository;
|
||||
let resp = svc
|
||||
.check_repository_health(tonic::Request::new(
|
||||
crate::pb::repo::RepositoryHealthRequest {
|
||||
repository: Some(header),
|
||||
connectivity_only: false,
|
||||
},
|
||||
))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_garbage_collect(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
) -> Result<crate::pb::repo::RepositoryMaintenanceResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Admin)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.repository;
|
||||
let resp = svc
|
||||
.garbage_collect(tonic::Request::new(
|
||||
crate::pb::repo::GarbageCollectRequest {
|
||||
repository: Some(header),
|
||||
prune: true,
|
||||
aggressive: false,
|
||||
},
|
||||
))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
use crate::error::AppError;
|
||||
use crate::service::RepoService;
|
||||
use crate::session::Session;
|
||||
|
||||
impl RepoService {
|
||||
pub async fn git_list_tags(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
pattern: Option<String>,
|
||||
page_size: u32,
|
||||
) -> Result<crate::pb::repo::ListTagsResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.tag;
|
||||
let resp = svc
|
||||
.list_tags(tonic::Request::new(crate::pb::repo::ListTagsRequest {
|
||||
repository: Some(header),
|
||||
pattern: pattern.unwrap_or_default(),
|
||||
pagination: Some(crate::pb::repo::Pagination {
|
||||
page_size,
|
||||
page_token: String::new(),
|
||||
}),
|
||||
sort_direction: crate::pb::repo::SortDirection::Desc as i32,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn git_create_tag(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
tag_name: &str,
|
||||
target: &str,
|
||||
message: Option<String>,
|
||||
annotated: bool,
|
||||
) -> Result<crate::pb::repo::Tag, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Member)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.tag;
|
||||
let resp = svc
|
||||
.create_tag(tonic::Request::new(crate::pb::repo::CreateTagRequest {
|
||||
repository: Some(header),
|
||||
name: tag_name.to_string(),
|
||||
target: Some(crate::pb::repo::ObjectSelector {
|
||||
selector: Some(crate::pb::repo::object_selector::Selector::Revision(
|
||||
crate::pb::repo::ObjectName {
|
||||
revision: target.to_string(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
message: message.unwrap_or_default(),
|
||||
tagger: None,
|
||||
force: false,
|
||||
annotated,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_delete_tag(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
tag_name: &str,
|
||||
) -> Result<(), AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_role_at_least(user_uid, &repo, crate::models::common::Role::Admin)
|
||||
.await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.tag;
|
||||
svc.delete_tag(tonic::Request::new(crate::pb::repo::DeleteTagRequest {
|
||||
repository: Some(header),
|
||||
name: tag_name.to_string(),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::error::AppError;
|
||||
use crate::service::RepoService;
|
||||
use crate::session::Session;
|
||||
|
||||
impl RepoService {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn git_list_tree(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
revision: &str,
|
||||
path: Option<String>,
|
||||
recursive: bool,
|
||||
page_size: u32,
|
||||
) -> Result<crate::pb::repo::ListTreeResponse, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.tree;
|
||||
let resp = svc
|
||||
.list_tree(tonic::Request::new(crate::pb::repo::ListTreeRequest {
|
||||
repository: Some(header),
|
||||
revision: Some(crate::pb::repo::ObjectSelector {
|
||||
selector: Some(crate::pb::repo::object_selector::Selector::Revision(
|
||||
crate::pb::repo::ObjectName {
|
||||
revision: revision.to_string(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
path: path.unwrap_or_default(),
|
||||
recursive,
|
||||
pagination: Some(crate::pb::repo::Pagination {
|
||||
page_size,
|
||||
page_token: String::new(),
|
||||
}),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
|
||||
pub async fn git_get_blob(
|
||||
&self,
|
||||
ctx: &Session,
|
||||
wk_name: &str,
|
||||
repo_name: &str,
|
||||
revision: &str,
|
||||
path: &str,
|
||||
) -> Result<crate::pb::repo::Blob, AppError> {
|
||||
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
||||
let repo = self.resolve_repo(wk_name, repo_name).await?;
|
||||
self.ensure_repo_readable(user_uid, &repo).await?;
|
||||
let ws = self.resolve_workspace(wk_name).await?;
|
||||
let header = self.repo_header(&repo, &ws);
|
||||
let mut svc = self.git_client(&repo)?.tree;
|
||||
let resp = svc
|
||||
.get_blob(tonic::Request::new(crate::pb::repo::GetBlobRequest {
|
||||
repository: Some(header),
|
||||
revision: Some(crate::pb::repo::ObjectSelector {
|
||||
selector: Some(crate::pb::repo::object_selector::Selector::Revision(
|
||||
crate::pb::repo::ObjectName {
|
||||
revision: revision.to_string(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
path: path.to_string(),
|
||||
oid: None,
|
||||
max_bytes: 0,
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
||||
Ok(resp.into_inner())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user