feat(api): expand API endpoints for repo, PR, user, workspace management

- Add git operation endpoints: archive, compare branches, diff, tree,
  repository extras
- Add repo endpoints: contributors, delete fork, get branch/commit
  status/deploy key/invitation/member/release/tag/webhook, topics,
  release assets, webhook deliveries/retry
- Add PR endpoints: review requests, templates
- Add user endpoints: block/unblock, follow/unfollow, presence,
  personal access tokens, account restore
- Add workspace endpoints: billing history, approvals, domains,
  integrations, invitations, members, webhooks, restore
- Add internal API, notification API, IM API modules
- Update route configuration and OpenAPI spec
This commit is contained in:
zhenyi
2026-06-10 18:49:27 +08:00
parent 4586b79cb8
commit cec6dce955
161 changed files with 7522 additions and 349 deletions
+56
View File
@@ -0,0 +1,56 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub revision: String,
pub path: String,
pub page_size: Option<u32>,
}
/// Blame a file
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/blame",
tag = "Git",
operation_id = "gitBlame",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Blame retrieved successfully", body = ApiResponse<crate::pb::repo::BlameResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_blame(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_blame(
&session,
&path.workspace_name,
&path.repo_name,
&query.revision,
&query.path,
query.page_size.unwrap_or(30),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+55
View File
@@ -0,0 +1,55 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub revision: String,
pub path: String,
}
/// Get blob content
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/blobs",
tag = "Git",
operation_id = "gitGetBlob",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Blob retrieved successfully", body = ApiResponse<crate::pb::repo::Blob>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Blob not found", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_blob(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_get_blob(
&session,
&path.workspace_name,
&path.repo_name,
&query.revision,
&query.path,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+54
View File
@@ -0,0 +1,54 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::service::repo::git::merge::CherryPickParams;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Cherry-pick a commit
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/cherry-pick",
tag = "Git",
operation_id = "gitCherryPick",
params(PathParams),
request_body(
content = CherryPickParams,
description = "Cherry-pick parameters",
content_type = "application/json"
),
responses(
(status = 200, description = "Cherry-pick completed successfully", body = ApiResponse<crate::pb::repo::CreateCommitResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 409, description = "Cherry-pick conflict", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_cherry_pick(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
params: web::Json<CherryPickParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_cherry_pick(
&session,
&path.workspace_name,
&path.repo_name,
params.into_inner(),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+137
View File
@@ -0,0 +1,137 @@
use crate::api::response::ApiResponse;
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct RevisionPathParams {
pub workspace_name: String,
pub repo_name: String,
pub revision: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct FindCommitQuery {
pub revision: String,
}
#[utoipa::path(get, path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/find-commit", tag = "Git", operation_id = "gitFindCommit", params(PathParams, FindCommitQuery), responses((status=200,body=ApiResponse<crate::pb::repo::Commit>)), security(("session_cookie"=[])))]
pub async fn git_find_commit(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
q: web::Query<FindCommitQuery>,
) -> Result<HttpResponse, AppError> {
let r = service
.repo
.git_find_commit(&session, &path.workspace_name, &path.repo_name, &q.revision)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(r)))
}
#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct ListCommitsByOidBody {
pub oids: Vec<String>,
}
#[utoipa::path(post, path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/commits/by-oid", tag = "Git", operation_id = "gitCommitsByOid", params(PathParams), request_body(content=ListCommitsByOidBody), responses((status=200,body=ApiResponse<crate::pb::repo::ListCommitsByOidResponse>)), security(("session_cookie"=[])))]
pub async fn git_commits_by_oid(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
body: web::Json<ListCommitsByOidBody>,
) -> Result<HttpResponse, AppError> {
let oids: Vec<Vec<u8>> = body
.oids
.iter()
.map(|s| hex::decode(s).unwrap_or_default())
.collect();
let r = service
.repo
.git_list_commits_by_oid(&session, &path.workspace_name, &path.repo_name, oids)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(r)))
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct AncestorQuery {
pub ancestor: String,
pub descendant: String,
}
#[utoipa::path(get, path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/commit-is-ancestor", tag = "Git", operation_id = "gitCommitIsAncestor", params(PathParams, AncestorQuery), responses((status=200,body=ApiResponse<bool>)), security(("session_cookie"=[])))]
pub async fn git_commit_is_ancestor(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
q: web::Query<AncestorQuery>,
) -> Result<HttpResponse, AppError> {
let r = service
.repo
.git_commit_is_ancestor(
&session,
&path.workspace_name,
&path.repo_name,
&q.ancestor,
&q.descendant,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(r)))
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct LastCommitQuery {
pub path: String,
pub revision: Option<String>,
}
#[utoipa::path(get, path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/last-commit", tag = "Git", operation_id = "gitLastCommitForPath", params(PathParams, LastCommitQuery), responses((status=200,body=ApiResponse<crate::pb::repo::LastCommitForPathResponse>)), security(("session_cookie"=[])))]
pub async fn git_last_commit(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
q: web::Query<LastCommitQuery>,
) -> Result<HttpResponse, AppError> {
let r = service
.repo
.git_last_commit_for_path(
&session,
&path.workspace_name,
&path.repo_name,
&q.path,
q.revision.as_deref(),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(r)))
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct CommitsByMsgQuery {
pub q: String,
pub revision: Option<String>,
pub limit: Option<u32>,
}
#[utoipa::path(get, path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/commits/search", tag = "Git", operation_id = "gitCommitsByMessage", params(PathParams, CommitsByMsgQuery), responses((status=200,body=ApiResponse<crate::pb::repo::CommitsByMessageResponse>)), security(("session_cookie"=[])))]
pub async fn git_commits_by_message(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
q: web::Query<CommitsByMsgQuery>,
) -> Result<HttpResponse, AppError> {
let r = service
.repo
.git_commits_by_message(
&session,
&path.workspace_name,
&path.repo_name,
&q.q,
q.revision.as_deref(),
q.limit.unwrap_or(20),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(r)))
}
+45
View File
@@ -0,0 +1,45 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
pub revision: String,
}
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/commits/{revision}/stats",
tag = "Git",
operation_id = "gitCommitStats",
params(PathParams),
responses(
(status = 200, description = "Commit stats", body = ApiResponse<crate::pb::repo::CommitStats>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Not found", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_commit_stats(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_commit_stats(
&session,
&path.workspace_name,
&path.repo_name,
&path.revision,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+59
View File
@@ -0,0 +1,59 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::service::repo::git::merge::CompareParams;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub base: String,
pub head: String,
pub page_size: Option<u32>,
}
/// Compare two commits
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/compare",
tag = "Git",
operation_id = "gitCompare",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Comparison completed successfully", body = ApiResponse<crate::pb::repo::CompareCommitsResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_compare(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_compare_commits(
&session,
&path.workspace_name,
&path.repo_name,
CompareParams {
base: query.base.clone(),
head: query.head.clone(),
page_size: query.page_size,
},
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+56
View File
@@ -0,0 +1,56 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub target: String,
pub source: String,
pub page_size: Option<u32>,
}
/// List merge conflicts
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/conflicts",
tag = "Git",
operation_id = "gitListConflicts",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Conflicts listed successfully", body = ApiResponse<crate::pb::repo::ListMergeConflictsResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_conflicts(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_list_conflicts(
&session,
&path.workspace_name,
&path.repo_name,
&query.target,
&query.source,
query.page_size.unwrap_or(30),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+56
View File
@@ -0,0 +1,56 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub revision: Option<String>,
pub path: Option<String>,
pub since: Option<String>,
pub until: Option<String>,
}
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/commits/count",
tag = "Git",
operation_id = "gitCountCommits",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Commit count", body = ApiResponse<crate::pb::repo::CountCommitsResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Not found", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_count_commits(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_count_commits(
&session,
&path.workspace_name,
&path.repo_name,
query.revision.as_deref(),
query.path.as_deref(),
query.since.as_deref(),
query.until.as_deref(),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+52
View File
@@ -0,0 +1,52 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub left: String,
pub right: String,
}
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/compare/diverging",
tag = "Git",
operation_id = "gitCountDivergingCommits",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Diverging commit counts", body = ApiResponse<crate::pb::repo::CountDivergingCommitsResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Not found", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_count_diverging(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_count_diverging_commits(
&session,
&path.workspace_name,
&path.repo_name,
&query.left,
&query.right,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+59
View File
@@ -0,0 +1,59 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::{IntoParams, ToSchema};
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams, ToSchema)]
pub struct CreateBranchBody {
pub branch_name: String,
pub start_point: String,
}
/// Create a branch
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/branches",
tag = "Git",
operation_id = "gitCreateBranch",
params(PathParams),
request_body(
content = CreateBranchBody,
description = "Branch creation parameters",
content_type = "application/json"
),
responses(
(status = 201, description = "Branch created successfully", body = ApiResponse<crate::pb::repo::Branch>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_create_branch(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
body: web::Json<CreateBranchBody>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_create_branch(
&session,
&path.workspace_name,
&path.repo_name,
&body.branch_name,
&body.start_point,
)
.await?;
Ok(HttpResponse::Created().json(ApiResponse::new(result)))
}
+53
View File
@@ -0,0 +1,53 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::service::repo::git::merge::CreateCommitParams;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Create a commit
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/commits",
tag = "Git",
operation_id = "gitCreateCommit",
params(PathParams),
request_body(
content = CreateCommitParams,
description = "Commit creation parameters",
content_type = "application/json"
),
responses(
(status = 201, description = "Commit created successfully", body = ApiResponse<crate::pb::repo::CreateCommitResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_create_commit(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
params: web::Json<CreateCommitParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_create_commit(
&session,
&path.workspace_name,
&path.repo_name,
params.into_inner(),
)
.await?;
Ok(HttpResponse::Created().json(ApiResponse::new(result)))
}
+63
View File
@@ -0,0 +1,63 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::{IntoParams, ToSchema};
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams, ToSchema)]
pub struct CreateTagBody {
pub tag_name: String,
pub target: String,
pub message: Option<String>,
pub annotated: Option<bool>,
}
/// Create a tag
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/tags",
tag = "Git",
operation_id = "gitCreateTag",
params(PathParams),
request_body(
content = CreateTagBody,
description = "Tag creation parameters",
content_type = "application/json"
),
responses(
(status = 201, description = "Tag created successfully", body = ApiResponse<crate::pb::repo::Tag>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_create_tag(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
body: web::Json<CreateTagBody>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_create_tag(
&session,
&path.workspace_name,
&path.repo_name,
&body.tag_name,
&body.target,
body.message.clone(),
body.annotated.unwrap_or(false),
)
.await?;
Ok(HttpResponse::Created().json(ApiResponse::new(result)))
}
+47
View File
@@ -0,0 +1,47 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
pub branch_name: String,
}
/// Delete a branch
#[utoipa::path(
delete,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/branches/{branch_name}",
tag = "Git",
operation_id = "gitDeleteBranch",
params(PathParams),
responses(
(status = 200, description = "Branch deleted successfully", body = ApiResponse<String>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_delete_branch(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
service
.repo
.git_delete_branch(
&session,
&path.workspace_name,
&path.repo_name,
&path.branch_name,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new("Branch deleted successfully".to_string())))
}
+47
View File
@@ -0,0 +1,47 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
pub tag_name: String,
}
/// Delete a tag
#[utoipa::path(
delete,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/tags/{tag_name}",
tag = "Git",
operation_id = "gitDeleteTag",
params(PathParams),
responses(
(status = 200, description = "Tag deleted successfully", body = ApiResponse<String>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_delete_tag(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
service
.repo
.git_delete_tag(
&session,
&path.workspace_name,
&path.repo_name,
&path.tag_name,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new("Tag deleted successfully".to_string())))
}
+56
View File
@@ -0,0 +1,56 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub base: String,
pub head: String,
pub page_size: Option<u32>,
}
/// Get diff between two revisions
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/diff",
tag = "Git",
operation_id = "gitDiff",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Diff retrieved successfully", body = ApiResponse<crate::pb::repo::GetDiffResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_diff(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_diff(
&session,
&path.workspace_name,
&path.repo_name,
&query.base,
&query.head,
query.page_size.unwrap_or(30),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+54
View File
@@ -0,0 +1,54 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub base: String,
pub head: String,
}
/// Get diff statistics between two revisions
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/diff-stats",
tag = "Git",
operation_id = "gitDiffStats",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Diff stats retrieved successfully", body = ApiResponse<crate::pb::repo::DiffStats>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_diff_stats(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_diff_stats(
&session,
&path.workspace_name,
&path.repo_name,
&query.base,
&query.head,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+41
View File
@@ -0,0 +1,41 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Check if repository exists
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/exists",
tag = "Git",
operation_id = "gitRepoExists",
params(PathParams),
responses(
(status = 200, description = "Repository existence check completed", body = ApiResponse<bool>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_exists(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_repo_exists(&session, &path.workspace_name, &path.repo_name)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+41
View File
@@ -0,0 +1,41 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Run garbage collection
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/garbage-collect",
tag = "Git",
operation_id = "gitGarbageCollect",
params(PathParams),
responses(
(status = 200, description = "Garbage collection completed", body = ApiResponse<crate::pb::repo::RepositoryMaintenanceResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_gc(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_garbage_collect(&session, &path.workspace_name, &path.repo_name)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+48
View File
@@ -0,0 +1,48 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
pub branch_name: String,
}
/// Get a branch
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/branches/{branch_name}",
tag = "Git",
operation_id = "gitGetBranch",
params(PathParams),
responses(
(status = 200, description = "Branch retrieved successfully", body = ApiResponse<crate::pb::repo::Branch>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Branch not found", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_get_branch(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_get_branch(
&session,
&path.workspace_name,
&path.repo_name,
&path.branch_name,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+48
View File
@@ -0,0 +1,48 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
pub revision: String,
}
/// Get a single commit
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/commits/{revision}",
tag = "Git",
operation_id = "gitGetCommit",
params(PathParams),
responses(
(status = 200, description = "Commit retrieved successfully", body = ApiResponse<crate::pb::repo::Commit>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Commit not found", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_get_commit(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_get_commit(
&session,
&path.workspace_name,
&path.repo_name,
&path.revision,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+45
View File
@@ -0,0 +1,45 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
pub tag_name: String,
}
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/tags/{tag_name}",
tag = "Git",
operation_id = "gitGetTag",
params(PathParams),
responses(
(status = 200, description = "Tag details", body = ApiResponse<crate::pb::repo::Tag>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Not found", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_get_tag(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_get_tag(
&session,
&path.workspace_name,
&path.repo_name,
&path.tag_name,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+41
View File
@@ -0,0 +1,41 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Check repository health
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/health",
tag = "Git",
operation_id = "gitRepoHealth",
params(PathParams),
responses(
(status = 200, description = "Repository health check completed", body = ApiResponse<crate::pb::repo::RepositoryHealthResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_health(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_repo_health(&session, &path.workspace_name, &path.repo_name)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+41
View File
@@ -0,0 +1,41 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Get repository info
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/info",
tag = "Git",
operation_id = "gitRepoInfo",
params(PathParams),
responses(
(status = 200, description = "Repository info retrieved successfully", body = ApiResponse<crate::pb::repo::Repository>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_info(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_repo_info(&session, &path.workspace_name, &path.repo_name)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+39
View File
@@ -0,0 +1,39 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/license",
tag = "Git",
operation_id = "gitLicense",
params(PathParams),
responses(
(status = 200, description = "License detection result", body = ApiResponse<crate::pb::repo::FindLicenseResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Not found", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_license(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_find_license(&session, &path.workspace_name, &path.repo_name)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+56
View File
@@ -0,0 +1,56 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub pattern: Option<String>,
pub page_size: Option<u32>,
pub page_token: Option<String>,
}
/// List branches
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/branches",
tag = "Git",
operation_id = "gitListBranches",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Branches listed successfully", body = ApiResponse<crate::pb::repo::ListBranchesResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_list_branches(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_list_branches(
&session,
&path.workspace_name,
&path.repo_name,
query.pattern.clone(),
query.page_size.unwrap_or(30),
query.page_token.clone(),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+56
View File
@@ -0,0 +1,56 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub revision: String,
pub path: Option<String>,
pub page_size: Option<u32>,
}
/// List commits
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/commits",
tag = "Git",
operation_id = "gitListCommits",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Commits listed successfully", body = ApiResponse<crate::pb::repo::ListCommitsResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_list_commits(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_list_commits(
&session,
&path.workspace_name,
&path.repo_name,
&query.revision,
query.path.clone(),
query.page_size.unwrap_or(30),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+54
View File
@@ -0,0 +1,54 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::service::repo::git::merge::MergeParams;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Merge branches
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/merge",
tag = "Git",
operation_id = "gitMerge",
params(PathParams),
request_body(
content = MergeParams,
description = "Merge parameters",
content_type = "application/json"
),
responses(
(status = 200, description = "Merge completed successfully", body = ApiResponse<crate::pb::repo::MergeResult>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 409, description = "Merge conflict", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_merge(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
params: web::Json<MergeParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_merge(
&session,
&path.workspace_name,
&path.repo_name,
params.into_inner(),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+54
View File
@@ -0,0 +1,54 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub target: String,
pub source: String,
}
/// Check if a merge is possible
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/merge-check",
tag = "Git",
operation_id = "gitMergeCheck",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Merge check completed successfully", body = ApiResponse<crate::pb::repo::MergeResult>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_merge_check(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_check_merge(
&session,
&path.workspace_name,
&path.repo_name,
&query.target,
&query.source,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+54
View File
@@ -0,0 +1,54 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::service::repo::git::merge::RebaseParams;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Rebase a branch
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/rebase",
tag = "Git",
operation_id = "gitRebase",
params(PathParams),
request_body(
content = RebaseParams,
description = "Rebase parameters",
content_type = "application/json"
),
responses(
(status = 200, description = "Rebase completed successfully", body = ApiResponse<crate::pb::repo::RebaseResult>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 409, description = "Rebase conflict", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_rebase(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
params: web::Json<RebaseParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_rebase(
&session,
&path.workspace_name,
&path.repo_name,
params.into_inner(),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+53
View File
@@ -0,0 +1,53 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
pub branch_name: String,
}
#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct RenameBody {
pub new_name: String,
}
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/branches/{branch_name}/rename",
tag = "Git",
operation_id = "gitRenameBranch",
params(PathParams),
request_body(content = RenameBody),
responses(
(status = 200, description = "Branch renamed", body = ApiResponse<crate::pb::repo::Branch>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Not found", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_rename_branch(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
body: web::Json<RenameBody>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_rename_branch(
&session,
&path.workspace_name,
&path.repo_name,
&path.branch_name,
&body.new_name,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+53
View File
@@ -0,0 +1,53 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::service::repo::git::merge::RevertParams;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Revert a commit
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/revert",
tag = "Git",
operation_id = "gitRevert",
params(PathParams),
request_body(
content = RevertParams,
description = "Revert parameters",
content_type = "application/json"
),
responses(
(status = 200, description = "Revert completed successfully", body = ApiResponse<crate::pb::repo::CreateCommitResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_revert(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
params: web::Json<RevertParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_revert(
&session,
&path.workspace_name,
&path.repo_name,
params.into_inner(),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+98
View File
@@ -0,0 +1,98 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct SearchQueryParams {
pub q: String,
pub revision: Option<String>,
pub max_results: Option<u32>,
pub case_sensitive: Option<bool>,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct SearchFilesQueryParams {
pub q: String,
pub revision: Option<String>,
pub max_results: Option<u32>,
pub recursive: Option<bool>,
}
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/search/content",
tag = "Git",
operation_id = "gitSearchContent",
params(PathParams, SearchQueryParams),
responses(
(status = 200, description = "Search results", body = ApiResponse<crate::pb::repo::SearchFilesByContentResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Not found", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_search_content(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<SearchQueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_search_content(
&session,
&path.workspace_name,
&path.repo_name,
&query.q,
query.revision.as_deref(),
query.max_results.unwrap_or(100),
query.case_sensitive.unwrap_or(false),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/search/files",
tag = "Git",
operation_id = "gitSearchFiles",
params(PathParams, SearchFilesQueryParams),
responses(
(status = 200, description = "File search results", body = ApiResponse<crate::pb::repo::SearchFilesByNameResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Not found", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_search_files(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<SearchFilesQueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_search_files(
&session,
&path.workspace_name,
&path.repo_name,
&query.q,
query.revision.as_deref(),
query.max_results.unwrap_or(100),
query.recursive.unwrap_or(true),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+41
View File
@@ -0,0 +1,41 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
/// Get repository statistics
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/stats",
tag = "Git",
operation_id = "gitRepoStats",
params(PathParams),
responses(
(status = 200, description = "Repository statistics retrieved successfully", body = ApiResponse<crate::pb::repo::RepositoryStatistics>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_stats(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_repo_stats(&session, &path.workspace_name, &path.repo_name)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+54
View File
@@ -0,0 +1,54 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub pattern: Option<String>,
pub page_size: Option<u32>,
}
/// List tags
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/tags",
tag = "Git",
operation_id = "gitListTags",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Tags listed successfully", body = ApiResponse<crate::pb::repo::ListTagsResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_tags(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_list_tags(
&session,
&path.workspace_name,
&path.repo_name,
query.pattern.clone(),
query.page_size.unwrap_or(30),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+58
View File
@@ -0,0 +1,58 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct QueryParams {
pub revision: String,
pub path: Option<String>,
pub recursive: Option<bool>,
pub page_size: Option<u32>,
}
/// List tree contents
#[utoipa::path(
get,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/tree",
tag = "Git",
operation_id = "gitListTree",
params(PathParams, QueryParams),
responses(
(status = 200, description = "Tree listed successfully", body = ApiResponse<crate::pb::repo::ListTreeResponse>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_tree(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
query: web::Query<QueryParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_list_tree(
&session,
&path.workspace_name,
&path.repo_name,
&query.revision,
query.path.clone(),
query.recursive.unwrap_or(false),
query.page_size.unwrap_or(30),
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+45
View File
@@ -0,0 +1,45 @@
use actix_web::{HttpResponse, web};
use serde::Deserialize;
use utoipa::IntoParams;
use crate::api::response::{ApiErrorResponse, ApiResponse};
use crate::error::AppError;
use crate::service::AppService;
use crate::session::Session;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PathParams {
pub workspace_name: String,
pub repo_name: String,
pub tag_name: String,
}
#[utoipa::path(
post,
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/git/tags/{tag_name}/verify",
tag = "Git",
operation_id = "gitVerifyTag",
params(PathParams),
responses(
(status = 200, description = "Tag signature verification result", body = ApiResponse<crate::pb::repo::VerifiedSignature>),
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
(status = 404, description = "Not found", body = ApiErrorResponse),
),
security(("session_cookie" = []))
)]
pub async fn git_verify_tag(
service: web::Data<AppService>,
session: Session,
path: web::Path<PathParams>,
) -> Result<HttpResponse, AppError> {
let result = service
.repo
.git_verify_tag(
&session,
&path.workspace_name,
&path.repo_name,
&path.tag_name,
)
.await?;
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
}
+218
View File
@@ -0,0 +1,218 @@
pub mod git_archive;
pub mod git_blame;
pub mod git_blob;
pub mod git_cherry_pick;
pub mod git_commit_extras2;
pub mod git_commit_stats;
pub mod git_compare;
pub mod git_compare_branch;
pub mod git_conflicts;
pub mod git_count_commits;
pub mod git_count_diverging;
pub mod git_create_branch;
pub mod git_create_commit;
pub mod git_create_tag;
pub mod git_delete_branch;
pub mod git_delete_tag;
pub mod git_diff;
pub mod git_diff_extras;
pub mod git_diff_stats;
pub mod git_exists;
pub mod git_gc;
pub mod git_get_branch;
pub mod git_get_commit;
pub mod git_get_tag;
pub mod git_health;
pub mod git_info;
pub mod git_license;
pub mod git_list_branches;
pub mod git_list_commits;
pub mod git_merge;
pub mod git_merge_check;
pub mod git_rebase;
pub mod git_rename_branch;
pub mod git_repository_extras;
pub mod git_revert;
pub mod git_search;
pub mod git_stats;
pub mod git_tags;
pub mod git_tree;
pub mod git_tree_extras;
pub mod git_verify_tag;
use actix_web::web;
pub fn configure(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/git")
.route(
"/commits",
web::get().to(git_list_commits::git_list_commits),
)
.route(
"/commits/{revision}",
web::get().to(git_get_commit::git_get_commit),
)
.route(
"/commits/{revision}/stats",
web::get().to(git_commit_stats::git_commit_stats),
)
.route(
"/commits",
web::post().to(git_create_commit::git_create_commit),
)
.route(
"/commits/count",
web::get().to(git_count_commits::git_count_commits),
)
.route("/diff", web::get().to(git_diff::git_diff))
.route("/diff-stats", web::get().to(git_diff_stats::git_diff_stats))
.route("/compare", web::get().to(git_compare::git_compare))
.route(
"/compare/diverging",
web::get().to(git_count_diverging::git_count_diverging),
)
.route("/archive", web::get().to(git_archive::git_archive))
.route("/license", web::get().to(git_license::git_license))
.route(
"/search/content",
web::get().to(git_search::git_search_content),
)
.route("/search/files", web::get().to(git_search::git_search_files))
.route(
"/branches",
web::get().to(git_list_branches::git_list_branches),
)
.route(
"/branches/{branch_name}",
web::get().to(git_get_branch::git_get_branch),
)
.route(
"/branches/{branch_name}/compare",
web::get().to(git_compare_branch::git_compare_branch),
)
.route(
"/branches/{branch_name}/rename",
web::post().to(git_rename_branch::git_rename_branch),
)
.route(
"/branches",
web::post().to(git_create_branch::git_create_branch),
)
.route(
"/branches/{branch_name}",
web::delete().to(git_delete_branch::git_delete_branch),
)
.route(
"/merge-check",
web::get().to(git_merge_check::git_merge_check),
)
.route("/merge", web::post().to(git_merge::git_merge))
.route("/rebase", web::post().to(git_rebase::git_rebase))
.route(
"/cherry-pick",
web::post().to(git_cherry_pick::git_cherry_pick),
)
.route("/revert", web::post().to(git_revert::git_revert))
.route("/conflicts", web::get().to(git_conflicts::git_conflicts))
.route("/tree", web::get().to(git_tree::git_tree))
.route("/blobs", web::get().to(git_blob::git_blob))
.route("/blame", web::get().to(git_blame::git_blame))
.route("/tags", web::get().to(git_tags::git_tags))
.route("/tags", web::post().to(git_create_tag::git_create_tag))
.route("/tags/{tag_name}", web::get().to(git_get_tag::git_get_tag))
.route(
"/tags/{tag_name}/verify",
web::post().to(git_verify_tag::git_verify_tag),
)
.route(
"/tags/{tag_name}",
web::delete().to(git_delete_tag::git_delete_tag),
)
.route("/info", web::get().to(git_info::git_info))
.route("/exists", web::get().to(git_exists::git_exists))
.route("/stats", web::get().to(git_stats::git_stats))
.route("/health", web::get().to(git_health::git_health))
.route("/garbage-collect", web::post().to(git_gc::git_gc))
.route(
"/default-branch",
web::get().to(git_repository_extras::git_default_branch),
)
.route(
"/object-format",
web::get().to(git_repository_extras::git_object_format),
)
.route(
"/size",
web::get().to(git_repository_extras::git_repository_size),
)
.route(
"/objects-size",
web::post().to(git_repository_extras::git_objects_size),
)
.route(
"/check-objects",
web::post().to(git_repository_extras::git_check_objects),
)
.route(
"/merge-base",
web::get().to(git_repository_extras::git_merge_base),
)
.route(
"/archive/entries",
web::get().to(git_repository_extras::git_archive_entries),
)
.route(
"/commits/{revision}/ancestors",
web::get().to(git_repository_extras::git_commit_ancestors),
)
.route(
"/find-commit",
web::get().to(git_commit_extras2::git_find_commit),
)
.route(
"/commits/by-oid",
web::post().to(git_commit_extras2::git_commits_by_oid),
)
.route(
"/commit-is-ancestor",
web::get().to(git_commit_extras2::git_commit_is_ancestor),
)
.route(
"/last-commit",
web::get().to(git_commit_extras2::git_last_commit),
)
.route(
"/commits/search",
web::get().to(git_commit_extras2::git_commits_by_message),
)
.route("/raw-blob", web::get().to(git_tree_extras::git_raw_blob))
.route(
"/file-metadata",
web::get().to(git_tree_extras::git_file_metadata),
)
.route(
"/find-files",
web::get().to(git_tree_extras::git_find_files),
)
.route("/get-tree", web::get().to(git_tree_extras::git_get_tree))
.route(
"/commit-diff/{revision}",
web::get().to(git_diff_extras::git_commit_diff),
)
.route("/patch", web::get().to(git_diff_extras::git_patch))
.route("/raw-diff", web::get().to(git_diff_extras::git_raw_diff))
.route(
"/changed-paths",
web::get().to(git_diff_extras::git_changed_paths),
)
.route(
"/stream-blame",
web::get().to(git_diff_extras::git_stream_blame),
)
.route(
"/resolve-conflicts",
web::post().to(git_diff_extras::git_resolve_conflicts),
),
);
}