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:
@@ -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::contributors::Contributor;
|
||||
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 limit: Option<i64>,
|
||||
pub offset: Option<i64>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/contributors",
|
||||
tag = "Repos",
|
||||
operation_id = "repoListContributors",
|
||||
params(PathParams, QueryParams),
|
||||
responses(
|
||||
(status = 200, description = "List of contributors", body = ApiResponse<Vec<Contributor>>),
|
||||
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repo not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn list_contributors(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
query: web::Query<QueryParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let result = service
|
||||
.repo
|
||||
.repo_contributors(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
query.limit.unwrap_or(50),
|
||||
query.offset.unwrap_or(0),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
+14
-3
@@ -4,7 +4,8 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::Repo;
|
||||
use crate::models::base_info::{resolve_users, resolve_workspaces};
|
||||
use crate::models::repos::RepoDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::service::repo::core::CreateRepoParams;
|
||||
use crate::session::Session;
|
||||
@@ -42,7 +43,7 @@ pub struct PathParams {
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 201, description = "Repository created successfully. Returns the newly created repository with full metadata.", body = ApiResponse<Repo>),
|
||||
(status = 201, description = "Repository created successfully. Returns the newly created repository with full metadata.", body = ApiResponse<RepoDetail>),
|
||||
(status = 400, description = "Invalid parameters: name too long, invalid characters, or invalid visibility", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions to create repositories in this workspace", body = ApiErrorResponse),
|
||||
@@ -65,5 +66,15 @@ pub async fn create(
|
||||
.repo_create(&session, &path.workspace_name, params.into_inner())
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(repo)))
|
||||
let db = &service.ctx.db;
|
||||
let users = resolve_users(db, &[repo.owner_id]).await?;
|
||||
let workspaces = resolve_workspaces(db, &[repo.workspace_id]).await?;
|
||||
let owner = users.get(&repo.owner_id).cloned().unwrap_or_default();
|
||||
let workspace = workspaces
|
||||
.get(&repo.workspace_id)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let detail = repo.into_detail(owner, workspace);
|
||||
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(detail)))
|
||||
}
|
||||
|
||||
+10
-29
@@ -9,42 +9,24 @@ use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
/// Workspace name (unique identifier)
|
||||
pub workspace_name: String,
|
||||
/// Repository name (unique within the workspace)
|
||||
pub repo_name: String,
|
||||
/// Branch ID (UUID)
|
||||
pub branch_id: uuid::Uuid,
|
||||
pub branch_name: String,
|
||||
}
|
||||
|
||||
/// Delete a branch
|
||||
///
|
||||
/// Permanently deletes a branch from the repository. The default branch cannot be deleted.
|
||||
/// Requires Write role or higher in the repository.
|
||||
///
|
||||
/// Effects:
|
||||
/// - Branch is permanently removed from the repository
|
||||
/// - All commits exclusive to this branch remain accessible via their SHA
|
||||
/// - Open pull requests targeting this branch will be closed
|
||||
///
|
||||
/// Returns success message on completion.
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/branches/{branch_id}",
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/branches/{branch_name}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoDeleteBranch",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Branch deleted successfully.", body = ApiResponse<String>),
|
||||
(status = 400, description = "Cannot delete the default branch", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions (requires Write role or higher)", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repository, workspace, or branch not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error or Git operation failed", body = ApiErrorResponse),
|
||||
(status = 200, description = "Branch deleted", body = ApiResponse<String>),
|
||||
(status = 400, description = "Cannot delete default branch", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Branch not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(
|
||||
("session_cookie" = [])
|
||||
)
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn delete_branch(
|
||||
service: web::Data<AppService>,
|
||||
@@ -53,13 +35,12 @@ pub async fn delete_branch(
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
service
|
||||
.repo
|
||||
.repo_delete_branch(
|
||||
.git_delete_branch(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.branch_id,
|
||||
&path.branch_name,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new("Branch deleted successfully".to_string())))
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new("Branch deleted".to_string())))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/fork",
|
||||
tag = "Repos",
|
||||
operation_id = "repoDeleteFork",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Fork deleted successfully", body = ApiResponse<String>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Fork not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn delete_fork(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
service
|
||||
.repo
|
||||
.repo_delete_fork(&session, &path.workspace_name, &path.repo_name)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new("Fork deleted successfully".to_string())))
|
||||
}
|
||||
+13
-27
@@ -9,41 +9,23 @@ use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
/// Workspace name (unique identifier)
|
||||
pub workspace_name: String,
|
||||
/// Repository name (unique within the workspace)
|
||||
pub repo_name: String,
|
||||
/// Tag ID (UUID)
|
||||
pub tag_id: uuid::Uuid,
|
||||
pub tag_name: String,
|
||||
}
|
||||
|
||||
/// Delete a tag
|
||||
///
|
||||
/// Permanently deletes a tag from the repository. The tagged commit remains accessible via its SHA.
|
||||
/// Requires Write role or higher in the repository.
|
||||
///
|
||||
/// Effects:
|
||||
/// - Tag is permanently removed from the repository
|
||||
/// - The tagged commit remains in the repository history
|
||||
/// - Releases associated with this tag are not automatically deleted
|
||||
///
|
||||
/// Returns success message on completion.
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/tags/{tag_id}",
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/tags/{tag_name}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoDeleteTag",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Tag deleted successfully.", body = ApiResponse<String>),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions (requires Write role or higher)", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repository, workspace, or tag not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error or Git operation failed", body = ApiErrorResponse),
|
||||
(status = 200, description = "Tag deleted", body = ApiResponse<String>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Tag not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(
|
||||
("session_cookie" = [])
|
||||
)
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn delete_tag(
|
||||
service: web::Data<AppService>,
|
||||
@@ -52,8 +34,12 @@ pub async fn delete_tag(
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
service
|
||||
.repo
|
||||
.repo_delete_tag(&session, &path.workspace_name, &path.repo_name, path.tag_id)
|
||||
.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())))
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new("Tag deleted".to_string())))
|
||||
}
|
||||
|
||||
+14
-3
@@ -4,7 +4,8 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::Repo;
|
||||
use crate::models::base_info::{resolve_users, resolve_workspaces};
|
||||
use crate::models::repos::RepoDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
@@ -42,7 +43,7 @@ use crate::service::repo::fork::ForkRepoParams;
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 201, description = "Repository forked successfully. Returns the newly created fork with full metadata.", body = ApiResponse<Repo>),
|
||||
(status = 201, description = "Repository forked successfully. Returns the newly created fork with full metadata.", body = ApiResponse<RepoDetail>),
|
||||
(status = 400, description = "Invalid parameters: target name conflicts or invalid characters", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions to fork or create in target workspace", body = ApiErrorResponse),
|
||||
@@ -70,5 +71,15 @@ pub async fn fork_repo(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(repo)))
|
||||
let db = &service.ctx.db;
|
||||
let users = resolve_users(db, &[repo.owner_id]).await?;
|
||||
let workspaces = resolve_workspaces(db, &[repo.workspace_id]).await?;
|
||||
let owner = users.get(&repo.owner_id).cloned().unwrap_or_default();
|
||||
let workspace = workspaces
|
||||
.get(&repo.workspace_id)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let detail = repo.into_detail(owner, workspace);
|
||||
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(detail)))
|
||||
}
|
||||
|
||||
+14
-3
@@ -4,7 +4,8 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::Repo;
|
||||
use crate::models::base_info::{resolve_users, resolve_workspaces};
|
||||
use crate::models::repos::RepoDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
@@ -32,7 +33,7 @@ pub struct PathParams {
|
||||
operation_id = "repoGet",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Repository retrieved successfully. Returns complete repository metadata including visibility, default branch, creation date, and statistics.", body = ApiResponse<Repo>),
|
||||
(status = 200, description = "Repository retrieved successfully. Returns complete repository metadata including visibility, default branch, creation date, and statistics.", body = ApiResponse<RepoDetail>),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions to access this repository", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repository not found or access denied", body = ApiErrorResponse),
|
||||
@@ -52,5 +53,15 @@ pub async fn get(
|
||||
.repo_get(&session, &path.workspace_name, &path.repo_name)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(repo)))
|
||||
let db = &service.ctx.db;
|
||||
let users = resolve_users(db, &[repo.owner_id]).await?;
|
||||
let workspaces = resolve_workspaces(db, &[repo.workspace_id]).await?;
|
||||
let owner = users.get(&repo.owner_id).cloned().unwrap_or_default();
|
||||
let workspace = workspaces
|
||||
.get(&repo.workspace_id)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let detail = repo.into_detail(owner, workspace);
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(detail)))
|
||||
}
|
||||
|
||||
@@ -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 branch_name: String,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/branches/{branch_name}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoGetBranch",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Branch retrieved", body = ApiResponse<crate::pb::repo::Branch>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Branch not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn 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)))
|
||||
}
|
||||
@@ -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::models::repos::RepoCommitStatus;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub repo_name: String,
|
||||
pub push_commit_id: uuid::Uuid,
|
||||
pub status_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/commits/{push_commit_id}/statuses/{status_id}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoGetCommitStatus",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Commit status retrieved successfully", body = ApiResponse<RepoCommitStatus>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Commit status not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn get_commit_status(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let statuses = service
|
||||
.repo
|
||||
.repo_commit_statuses(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.push_commit_id,
|
||||
1000,
|
||||
0,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let status = statuses
|
||||
.into_iter()
|
||||
.find(|s| s.id == path.status_id)
|
||||
.ok_or(AppError::NotFound("commit status not found".into()))?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(status)))
|
||||
}
|
||||
@@ -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::models::repos::RepoDeployKey;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub repo_name: String,
|
||||
pub key_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/deploy-keys/{key_id}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoGetDeployKey",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Deploy key retrieved successfully", body = ApiResponse<RepoDeployKey>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Deploy key not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn get_deploy_key(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let keys = service
|
||||
.repo
|
||||
.repo_deploy_keys(&session, &path.workspace_name, &path.repo_name, 1000, 0)
|
||||
.await?;
|
||||
|
||||
let key = keys
|
||||
.into_iter()
|
||||
.find(|k| k.id == path.key_id)
|
||||
.ok_or(AppError::NotFound("deploy key not found".into()))?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(key)))
|
||||
}
|
||||
@@ -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::models::repos::RepoInvitation;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub repo_name: String,
|
||||
pub invitation_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/invitations/{invitation_id}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoGetInvitation",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Invitation retrieved successfully", body = ApiResponse<RepoInvitation>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Invitation not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn get_invitation(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let invitations = service
|
||||
.repo
|
||||
.repo_invitations(&session, &path.workspace_name, &path.repo_name, 1000, 0)
|
||||
.await?;
|
||||
|
||||
let invitation = invitations
|
||||
.into_iter()
|
||||
.find(|i| i.id == path.invitation_id)
|
||||
.ok_or(AppError::NotFound("invitation not found".into()))?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(invitation)))
|
||||
}
|
||||
@@ -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::models::repos::RepoMember;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub repo_name: String,
|
||||
pub member_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/members/{member_id}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoGetMember",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Member retrieved successfully", body = ApiResponse<RepoMember>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Member not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn get_member(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let members = service
|
||||
.repo
|
||||
.repo_members(&session, &path.workspace_name, &path.repo_name, 1000, 0)
|
||||
.await?;
|
||||
|
||||
let member = members
|
||||
.into_iter()
|
||||
.find(|m| m.id == path.member_id)
|
||||
.ok_or(AppError::NotFound("member not found".into()))?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(member)))
|
||||
}
|
||||
@@ -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::models::repos::RepoRelease;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub repo_name: String,
|
||||
pub release_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/releases/{release_id}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoGetRelease",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Release retrieved successfully", body = ApiResponse<RepoRelease>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Release not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn get_release(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let releases = service
|
||||
.repo
|
||||
.repo_releases(&session, &path.workspace_name, &path.repo_name, 1000, 0)
|
||||
.await?;
|
||||
|
||||
let release = releases
|
||||
.into_iter()
|
||||
.find(|r| r.id == path.release_id)
|
||||
.ok_or(AppError::NotFound("release not found".into()))?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(release)))
|
||||
}
|
||||
@@ -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}/tags/{tag_name}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoGetTag",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Tag retrieved", body = ApiResponse<crate::pb::repo::Tag>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Tag not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn 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)))
|
||||
}
|
||||
@@ -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::models::repos::RepoWebhook;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub repo_name: String,
|
||||
pub webhook_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/webhooks/{webhook_id}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoGetWebhook",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Webhook retrieved successfully", body = ApiResponse<RepoWebhook>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Webhook not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn get_webhook(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let webhooks = service
|
||||
.repo
|
||||
.repo_webhooks(&session, &path.workspace_name, &path.repo_name, 1000, 0)
|
||||
.await?;
|
||||
|
||||
let webhook = webhooks
|
||||
.into_iter()
|
||||
.find(|w| w.id == path.webhook_id)
|
||||
.ok_or(AppError::NotFound("webhook not found".into()))?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(webhook)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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())))
|
||||
}
|
||||
@@ -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())))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
+19
-3
@@ -1,10 +1,12 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::Repo;
|
||||
use crate::models::base_info::{resolve_users, resolve_workspaces};
|
||||
use crate::models::repos::RepoDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
@@ -41,7 +43,7 @@ pub struct QueryParams {
|
||||
QueryParams,
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Repositories listed successfully. Returns an array of repository objects with metadata.", body = ApiResponse<Vec<Repo>>),
|
||||
(status = 200, description = "Repositories listed successfully. Returns an array of repository objects with metadata.", body = ApiResponse<Vec<RepoDetail>>),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions to access this workspace", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace not found", body = ApiErrorResponse),
|
||||
@@ -67,5 +69,19 @@ pub async fn list(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(repos)))
|
||||
let db = &service.ctx.db;
|
||||
let owner_ids: Vec<Uuid> = repos.iter().map(|r| r.owner_id).collect();
|
||||
let ws_ids: Vec<Uuid> = repos.iter().map(|r| r.workspace_id).collect();
|
||||
let users = resolve_users(db, &owner_ids).await?;
|
||||
let workspaces = resolve_workspaces(db, &ws_ids).await?;
|
||||
let details: Vec<RepoDetail> = repos
|
||||
.into_iter()
|
||||
.map(|r| {
|
||||
let owner = users.get(&r.owner_id).cloned().unwrap_or_default();
|
||||
let workspace = workspaces.get(&r.workspace_id).cloned().unwrap_or_default();
|
||||
r.into_detail(owner, workspace)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(details)))
|
||||
}
|
||||
|
||||
+19
-29
@@ -4,36 +4,21 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::RepoBranch;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
/// Workspace name (unique identifier)
|
||||
pub workspace_name: String,
|
||||
/// Repository name (unique within the workspace)
|
||||
pub repo_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct QueryParams {
|
||||
/// Maximum number of branches to return (default: 50, max: 100)
|
||||
pub limit: Option<i64>,
|
||||
/// Number of branches to skip for pagination (default: 0)
|
||||
pub offset: Option<i64>,
|
||||
}
|
||||
|
||||
/// List branches in a repository
|
||||
///
|
||||
/// Returns a paginated list of all branches in the repository, sorted by name alphabetically.
|
||||
/// Includes branch metadata such as:
|
||||
/// - Branch name and commit SHA
|
||||
/// - Protected status
|
||||
/// - Default branch flag
|
||||
/// - Last push information
|
||||
///
|
||||
/// Requires read access to the repository.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/branches",
|
||||
@@ -41,15 +26,11 @@ pub struct QueryParams {
|
||||
operation_id = "repoListBranches",
|
||||
params(PathParams, QueryParams),
|
||||
responses(
|
||||
(status = 200, description = "Branches listed successfully. Returns an array of branch objects with metadata.", body = ApiResponse<Vec<RepoBranch>>),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions to access this repository", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repository or workspace not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
(status = 200, description = "Branches listed successfully", body = ApiResponse<crate::pb::repo::ListBranchesResponse>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repository not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(
|
||||
("session_cookie" = [])
|
||||
)
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn list_branches(
|
||||
service: web::Data<AppService>,
|
||||
@@ -57,16 +38,25 @@ pub async fn list_branches(
|
||||
path: web::Path<PathParams>,
|
||||
query: web::Query<QueryParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let branches = service
|
||||
let limit = query.limit.unwrap_or(50).clamp(1, 100);
|
||||
let offset = query.offset.unwrap_or(0).max(0);
|
||||
let page_size = limit as u32;
|
||||
let page_token = if offset > 0 {
|
||||
format!("{offset}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let result = service
|
||||
.repo
|
||||
.repo_branches(
|
||||
.git_list_branches(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
query.limit.unwrap_or(50),
|
||||
query.offset.unwrap_or(0),
|
||||
None,
|
||||
page_size,
|
||||
Some(page_token),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(branches)))
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
|
||||
+12
-28
@@ -4,35 +4,21 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::RepoTag;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
/// Workspace name (unique identifier)
|
||||
pub workspace_name: String,
|
||||
/// Repository name (unique within the workspace)
|
||||
pub repo_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct QueryParams {
|
||||
/// Maximum number of tags to return (default: 50, max: 100)
|
||||
pub limit: Option<i64>,
|
||||
/// Number of tags to skip for pagination (default: 0)
|
||||
pub offset: Option<i64>,
|
||||
}
|
||||
|
||||
/// List tags in a repository
|
||||
///
|
||||
/// Returns a paginated list of all tags in the repository, sorted by creation date (newest first).
|
||||
/// Includes tag metadata such as:
|
||||
/// - Tag name and commit SHA
|
||||
/// - Tagger information and timestamp
|
||||
/// - Tag message (for annotated tags)
|
||||
///
|
||||
/// Requires read access to the repository.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/tags",
|
||||
@@ -40,15 +26,11 @@ pub struct QueryParams {
|
||||
operation_id = "repoListTags",
|
||||
params(PathParams, QueryParams),
|
||||
responses(
|
||||
(status = 200, description = "Tags listed successfully. Returns an array of tag objects with metadata.", body = ApiResponse<Vec<RepoTag>>),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions to access this repository", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repository or workspace not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
(status = 200, description = "Tags listed", body = ApiResponse<crate::pb::repo::ListTagsResponse>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repository not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(
|
||||
("session_cookie" = [])
|
||||
)
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn list_tags(
|
||||
service: web::Data<AppService>,
|
||||
@@ -56,16 +38,18 @@ pub async fn list_tags(
|
||||
path: web::Path<PathParams>,
|
||||
query: web::Query<QueryParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let tags = service
|
||||
let limit = query.limit.unwrap_or(50).clamp(1, 100);
|
||||
let page_size = limit as u32;
|
||||
|
||||
let result = service
|
||||
.repo
|
||||
.repo_tags(
|
||||
.git_list_tags(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
query.limit.unwrap_or(50),
|
||||
query.offset.unwrap_or(0),
|
||||
None,
|
||||
page_size,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(tags)))
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
|
||||
+95
-9
@@ -5,6 +5,7 @@ pub mod add_deploy_key;
|
||||
pub mod add_member;
|
||||
pub mod archive;
|
||||
pub mod check_branch_merge;
|
||||
pub mod contributors;
|
||||
pub mod create;
|
||||
pub mod create_branch;
|
||||
pub mod create_commit_comment;
|
||||
@@ -17,14 +18,24 @@ pub mod create_webhook;
|
||||
pub mod delete;
|
||||
pub mod delete_branch;
|
||||
pub mod delete_deploy_key;
|
||||
pub mod delete_fork;
|
||||
pub mod delete_protection_rule;
|
||||
pub mod delete_release;
|
||||
pub mod delete_tag;
|
||||
pub mod delete_webhook;
|
||||
pub mod fork_repo;
|
||||
pub mod get;
|
||||
pub mod get_branch;
|
||||
pub mod get_commit_status;
|
||||
pub mod get_deploy_key;
|
||||
pub mod get_invitation;
|
||||
pub mod get_member;
|
||||
pub mod get_protection_rule;
|
||||
pub mod get_release;
|
||||
pub mod get_stats;
|
||||
pub mod get_tag;
|
||||
pub mod get_webhook;
|
||||
pub mod git;
|
||||
pub mod leave_repo;
|
||||
pub mod list;
|
||||
pub mod list_branches;
|
||||
@@ -42,27 +53,33 @@ pub mod list_watchers;
|
||||
pub mod list_webhooks;
|
||||
pub mod match_protection;
|
||||
pub mod refresh_stats;
|
||||
pub mod release_assets;
|
||||
pub mod remove_member;
|
||||
pub mod repo_webhook_deliveries;
|
||||
pub mod repo_webhook_retry;
|
||||
pub mod resolve_commit_comment;
|
||||
pub mod revoke_invitation;
|
||||
pub mod set_branch_protection;
|
||||
pub mod set_default_branch;
|
||||
pub mod star_repo;
|
||||
pub mod sync_fork;
|
||||
pub mod topics;
|
||||
pub mod transfer_owner;
|
||||
pub mod unarchive;
|
||||
pub mod unstar_repo;
|
||||
pub mod unwatch_repo;
|
||||
pub mod update;
|
||||
pub mod update_commit_comment;
|
||||
pub mod update_member_role;
|
||||
pub mod update_protection_rule;
|
||||
pub mod update_release;
|
||||
pub mod update_tag;
|
||||
pub mod update_webhook;
|
||||
pub mod watch_repo;
|
||||
|
||||
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("/workspaces/{workspace_name}/repos")
|
||||
web::scope("")
|
||||
.route("", web::get().to(list::list))
|
||||
.route("", web::post().to(create::create))
|
||||
.route("/{repo_name}", web::get().to(get::get))
|
||||
@@ -86,21 +103,33 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
web::post().to(create_branch::create_branch),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/branches/{branch_id}/default",
|
||||
"/{repo_name}/branches/{branch_name}/default",
|
||||
web::put().to(set_default_branch::set_default_branch),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/branches/{branch_id}/protection",
|
||||
"/{repo_name}/branches/{branch_name}/protection",
|
||||
web::put().to(set_branch_protection::set_branch_protection),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/branches/{branch_id}",
|
||||
"/{repo_name}/branches/{branch_name}",
|
||||
web::get().to(get_branch::get_branch),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/branches/{branch_name}",
|
||||
web::delete().to(delete_branch::delete_branch),
|
||||
)
|
||||
.route("/{repo_name}/tags", web::get().to(list_tags::list_tags))
|
||||
.route("/{repo_name}/tags", web::post().to(create_tag::create_tag))
|
||||
.route(
|
||||
"/{repo_name}/tags/{tag_id}",
|
||||
"/{repo_name}/tags/{tag_name}",
|
||||
web::get().to(get_tag::get_tag),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/tags/{tag_name}",
|
||||
web::put().to(update_tag::update_tag),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/tags/{tag_name}",
|
||||
web::delete().to(delete_tag::delete_tag),
|
||||
)
|
||||
.route(
|
||||
@@ -111,6 +140,10 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
"/{repo_name}/releases",
|
||||
web::post().to(create_release::create_release),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/releases/{release_id}",
|
||||
web::get().to(get_release::get_release),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/releases/{release_id}",
|
||||
web::put().to(update_release::update_release),
|
||||
@@ -121,7 +154,12 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
)
|
||||
.route("/{repo_name}/forks", web::get().to(list_forks::list_forks))
|
||||
.route("/{repo_name}/fork", web::post().to(fork_repo::fork_repo))
|
||||
.route(
|
||||
"/{repo_name}/fork",
|
||||
web::delete().to(delete_fork::delete_fork),
|
||||
)
|
||||
.route("/{repo_name}/sync", web::post().to(sync_fork::sync_fork))
|
||||
.route("/{repo_name}/topics", web::put().to(topics::update_topics))
|
||||
.route("/{repo_name}/star", web::post().to(star_repo::star_repo))
|
||||
.route(
|
||||
"/{repo_name}/star",
|
||||
@@ -152,6 +190,10 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
"/{repo_name}/members/{member_id}/role",
|
||||
web::put().to(update_member_role::update_member_role),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/members/{member_id}",
|
||||
web::get().to(get_member::get_member),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/members/{member_id}",
|
||||
web::delete().to(remove_member::remove_member),
|
||||
@@ -165,6 +207,10 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
"/{repo_name}/invitations",
|
||||
web::post().to(create_invitation::create_invitation),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/invitations/{invitation_id}",
|
||||
web::get().to(get_invitation::get_invitation),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/invitations/{invitation_id}",
|
||||
web::delete().to(revoke_invitation::revoke_invitation),
|
||||
@@ -177,6 +223,10 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
"/{repo_name}/deploy-keys",
|
||||
web::post().to(add_deploy_key::add_deploy_key),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/deploy-keys/{key_id}",
|
||||
web::get().to(get_deploy_key::get_deploy_key),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/deploy-keys/{key_id}",
|
||||
web::delete().to(delete_deploy_key::delete_deploy_key),
|
||||
@@ -189,6 +239,10 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
"/{repo_name}/webhooks",
|
||||
web::post().to(create_webhook::create_webhook),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/webhooks/{webhook_id}",
|
||||
web::get().to(get_webhook::get_webhook),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/webhooks/{webhook_id}",
|
||||
web::put().to(update_webhook::update_webhook),
|
||||
@@ -197,6 +251,14 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
"/{repo_name}/webhooks/{webhook_id}",
|
||||
web::delete().to(delete_webhook::delete_webhook),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/webhooks/{webhook_id}/deliveries",
|
||||
web::get().to(repo_webhook_deliveries::repo_webhook_deliveries),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/webhooks/{webhook_id}/deliveries/{delivery_id}/retry",
|
||||
web::post().to(repo_webhook_retry::repo_webhook_retry),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/protection-rules",
|
||||
web::get().to(list_protection_rules::list_protection_rules),
|
||||
@@ -229,6 +291,10 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
"/{repo_name}/commits/{push_commit_id}/statuses",
|
||||
web::get().to(list_commit_statuses::list_commit_statuses),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/commits/{push_commit_id}/statuses/{status_id}",
|
||||
web::get().to(get_commit_status::get_commit_status),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/commit-statuses",
|
||||
web::post().to(create_commit_status::create_commit_status),
|
||||
@@ -245,14 +311,34 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
"/{repo_name}/commit-comments/{comment_id}/resolve",
|
||||
web::post().to(resolve_commit_comment::resolve_commit_comment),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/commit-comments/{comment_id}",
|
||||
web::put().to(update_commit_comment::update_commit_comment),
|
||||
)
|
||||
.route("/{repo_name}/stats", web::get().to(get_stats::get_stats))
|
||||
.route(
|
||||
"/{repo_name}/stats/refresh",
|
||||
web::post().to(refresh_stats::refresh_stats),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/contributors",
|
||||
web::get().to(contributors::list_contributors),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/releases/{release_id}/assets",
|
||||
web::post().to(release_assets::upload_asset),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/releases/{release_id}/assets",
|
||||
web::get().to(release_assets::list_assets),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/releases/{release_id}/assets/{asset_id}",
|
||||
web::delete().to(release_assets::delete_asset),
|
||||
)
|
||||
.route(
|
||||
"/{repo_name}/releases/{release_id}/assets/{asset_id}/download",
|
||||
web::get().to(release_assets::download_asset),
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/repos/invitations/accept",
|
||||
web::post().to(accept_invitation::accept_invitation),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
use actix_multipart::Multipart;
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::api::user::upload_avatar::parse_avatar_field;
|
||||
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 release_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct AssetPathParams {
|
||||
pub workspace_name: String,
|
||||
pub repo_name: String,
|
||||
pub release_id: uuid::Uuid,
|
||||
pub asset_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct ListQueryParams {
|
||||
pub limit: Option<i64>,
|
||||
pub offset: Option<i64>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/releases/{release_id}/assets",
|
||||
tag = "Repos",
|
||||
operation_id = "repoUploadReleaseAsset",
|
||||
params(PathParams),
|
||||
request_body(content_type = "multipart/form-data"),
|
||||
responses(
|
||||
(status = 201, description = "Asset uploaded", body = ApiResponse<crate::service::repo::release_assets::ReleaseAssetData>),
|
||||
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
|
||||
(status = 404, description = "Release not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn upload_asset(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
payload: Multipart,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let (data, content_type, file_name) = parse_avatar_field(payload).await?;
|
||||
let filename = file_name.unwrap_or_else(|| "asset.bin".to_string());
|
||||
let result = service
|
||||
.repo
|
||||
.repo_upload_release_asset(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.release_id,
|
||||
&filename,
|
||||
data,
|
||||
content_type
|
||||
.as_deref()
|
||||
.unwrap_or("application/octet-stream"),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(result)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/releases/{release_id}/assets",
|
||||
tag = "Repos",
|
||||
operation_id = "repoListReleaseAssets",
|
||||
params(PathParams, ListQueryParams),
|
||||
responses(
|
||||
(status = 200, description = "List of release assets", body = ApiResponse<Vec<crate::service::repo::release_assets::ReleaseAssetData>>),
|
||||
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn list_assets(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
query: web::Query<ListQueryParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let result = service
|
||||
.repo
|
||||
.repo_list_release_assets(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.release_id,
|
||||
query.limit.unwrap_or(50),
|
||||
query.offset.unwrap_or(0),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/releases/{release_id}/assets/{asset_id}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoDeleteReleaseAsset",
|
||||
params(AssetPathParams),
|
||||
responses(
|
||||
(status = 200, description = "Asset deleted", body = ApiResponse<String>),
|
||||
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
|
||||
(status = 404, description = "Not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn delete_asset(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<AssetPathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
service
|
||||
.repo
|
||||
.repo_delete_release_asset(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.release_id,
|
||||
path.asset_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new("asset deleted".to_string())))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/releases/{release_id}/assets/{asset_id}/download",
|
||||
tag = "Repos",
|
||||
operation_id = "repoDownloadReleaseAsset",
|
||||
params(AssetPathParams),
|
||||
responses(
|
||||
(status = 302, description = "Redirect to download URL"),
|
||||
(status = 404, description = "Not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn download_asset(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<AssetPathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let url = service
|
||||
.repo
|
||||
.repo_get_release_asset_download_url(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.release_id,
|
||||
path.asset_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Found()
|
||||
.insert_header(("Location", url))
|
||||
.finish())
|
||||
}
|
||||
@@ -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,
|
||||
pub webhook_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct QueryParams {
|
||||
pub limit: Option<i64>,
|
||||
pub offset: Option<i64>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/webhooks/{webhook_id}/deliveries",
|
||||
tag = "Repos",
|
||||
operation_id = "repoListWebhookDeliveries",
|
||||
params(PathParams, QueryParams),
|
||||
responses(
|
||||
(status = 200, description = "Webhook deliveries listed successfully", body = ApiResponse<String>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Webhook not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn repo_webhook_deliveries(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
query: web::Query<QueryParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let deliveries = service
|
||||
.repo
|
||||
.repo_webhook_deliveries(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.webhook_id,
|
||||
query.limit.unwrap_or(50),
|
||||
query.offset.unwrap_or(0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(deliveries)))
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
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 webhook_id: uuid::Uuid,
|
||||
pub delivery_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/webhooks/{webhook_id}/deliveries/{delivery_id}/retry",
|
||||
tag = "Repos",
|
||||
operation_id = "repoRetryWebhookDelivery",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Webhook delivery retried successfully", body = ApiResponse<String>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Webhook or delivery not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn repo_webhook_retry(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
service
|
||||
.repo
|
||||
.repo_retry_webhook_delivery(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.webhook_id,
|
||||
path.delivery_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(
|
||||
"Webhook delivery retried successfully".to_string(),
|
||||
)))
|
||||
}
|
||||
@@ -9,52 +9,29 @@ use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
/// Workspace name (unique identifier)
|
||||
pub workspace_name: String,
|
||||
/// Repository name (unique within the workspace)
|
||||
pub repo_name: String,
|
||||
/// Branch ID (UUID)
|
||||
pub branch_id: uuid::Uuid,
|
||||
pub branch_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct SetBranchProtectionParams {
|
||||
/// Whether to enable branch protection
|
||||
pub protected: bool,
|
||||
}
|
||||
|
||||
/// Set branch protection
|
||||
///
|
||||
/// Enables or disables protection for a specific branch.
|
||||
/// Requires Admin role or higher in the repository.
|
||||
///
|
||||
/// Effects:
|
||||
/// - When enabled: prevents force pushes and branch deletion
|
||||
/// - When disabled: allows force pushes and branch deletion
|
||||
///
|
||||
/// Returns success message on completion.
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/branches/{branch_id}/protection",
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/branches/{branch_name}/protection",
|
||||
tag = "Repos",
|
||||
operation_id = "repoSetBranchProtection",
|
||||
params(PathParams),
|
||||
request_body(
|
||||
content = SetBranchProtectionParams,
|
||||
description = "Branch protection parameters",
|
||||
content_type = "application/json"
|
||||
),
|
||||
request_body(content = SetBranchProtectionParams),
|
||||
responses(
|
||||
(status = 200, description = "Branch protection rules set successfully.", body = ApiResponse<String>),
|
||||
(status = 400, description = "Invalid parameters: negative approvals count or conflicting protection settings", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions (requires Admin role or higher)", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repository, workspace, or branch not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
(status = 200, description = "Branch protection set", body = ApiResponse<String>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Branch not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(
|
||||
("session_cookie" = [])
|
||||
)
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn set_branch_protection(
|
||||
service: web::Data<AppService>,
|
||||
@@ -62,18 +39,28 @@ pub async fn set_branch_protection(
|
||||
path: web::Path<PathParams>,
|
||||
params: web::Json<SetBranchProtectionParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
service
|
||||
// Verify branch exists via gRPC
|
||||
let _ = service
|
||||
.repo
|
||||
.repo_set_branch_protection(
|
||||
.git_get_branch(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.branch_id,
|
||||
&path.branch_name,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Update DB protection flag (platform metadata, no gRPC equivalent)
|
||||
service
|
||||
.repo
|
||||
.repo_set_branch_protection_by_name(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
&path.branch_name,
|
||||
params.protected,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(
|
||||
"Branch protection rules set successfully".to_string(),
|
||||
)))
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new("Branch protection updated".to_string())))
|
||||
}
|
||||
|
||||
@@ -9,57 +9,50 @@ use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
/// Workspace name (unique identifier)
|
||||
pub workspace_name: String,
|
||||
/// Repository name (unique within the workspace)
|
||||
pub repo_name: String,
|
||||
/// Branch ID (UUID)
|
||||
pub branch_id: uuid::Uuid,
|
||||
pub branch_name: String,
|
||||
}
|
||||
|
||||
/// Set default branch
|
||||
///
|
||||
/// Sets a branch as the repository's default branch. The default branch is used for:
|
||||
/// - New pull requests base branch
|
||||
/// - Repository cloning
|
||||
/// - New branch creation base
|
||||
///
|
||||
/// Requires Admin role or higher in the repository.
|
||||
///
|
||||
/// Returns success message on completion.
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/branches/{branch_id}/default",
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/branches/{branch_name}/default",
|
||||
tag = "Repos",
|
||||
operation_id = "repoSetDefaultBranch",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Default branch set successfully. All new operations will use this branch as the default.", body = ApiResponse<String>),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions (requires Admin role or higher)", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repository, workspace, or branch not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
(status = 200, description = "Default branch set", body = ApiResponse<String>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Branch not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(
|
||||
("session_cookie" = [])
|
||||
)
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn set_default_branch(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
service
|
||||
// 1. Call gRPC to update the actual git HEAD ref
|
||||
let _ = service
|
||||
.repo
|
||||
.repo_set_default_branch(
|
||||
.git_set_default_branch(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.branch_id,
|
||||
&path.branch_name,
|
||||
)
|
||||
.await;
|
||||
|
||||
// 2. Update DB metadata (platform-level default branch tracking)
|
||||
service
|
||||
.repo
|
||||
.repo_set_default_branch_by_name(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
&path.branch_name,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(
|
||||
"Default branch set successfully".to_string(),
|
||||
)))
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new("Default branch set".to_string())))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
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::core::UpdateRepoParams;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub repo_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, utoipa::ToSchema)]
|
||||
pub struct TopicsBody {
|
||||
pub topics: Vec<String>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/topics",
|
||||
tag = "Repos",
|
||||
operation_id = "repoUpdateTopics",
|
||||
params(PathParams),
|
||||
request_body(content = TopicsBody),
|
||||
responses(
|
||||
(status = 200, description = "Topics updated", body = ApiResponse<Vec<String>>),
|
||||
(status = 401, description = "Unauthorized", body = ApiErrorResponse),
|
||||
(status = 404, description = "Repo not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn update_topics(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
body: web::Json<TopicsBody>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let result = service
|
||||
.repo
|
||||
.repo_update(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
UpdateRepoParams {
|
||||
name: None,
|
||||
description: None,
|
||||
visibility: None,
|
||||
default_branch: None,
|
||||
topics: Some(body.topics.clone()),
|
||||
homepage: None,
|
||||
has_issues: None,
|
||||
has_wiki: None,
|
||||
has_pull_requests: None,
|
||||
allow_forking: None,
|
||||
allow_merge_commit: None,
|
||||
allow_squash_merge: None,
|
||||
allow_rebase_merge: None,
|
||||
delete_branch_on_merge: None,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result.topics)))
|
||||
}
|
||||
@@ -4,7 +4,8 @@ use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::Repo;
|
||||
use crate::models::base_info::{resolve_users, resolve_workspaces};
|
||||
use crate::models::repos::RepoDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
@@ -46,7 +47,7 @@ pub struct TransferOwnerParams {
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Ownership transferred successfully. Returns the repository with updated owner information.", body = ApiResponse<Repo>),
|
||||
(status = 200, description = "Ownership transferred successfully. Returns the repository with updated owner information.", body = ApiResponse<RepoDetail>),
|
||||
(status = 400, description = "Invalid new owner ID or user is not a repository member", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions (requires Owner role)", body = ApiErrorResponse),
|
||||
@@ -73,5 +74,15 @@ pub async fn transfer_owner(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(repo)))
|
||||
let db = &service.ctx.db;
|
||||
let users = resolve_users(db, &[repo.owner_id]).await?;
|
||||
let workspaces = resolve_workspaces(db, &[repo.workspace_id]).await?;
|
||||
let owner = users.get(&repo.owner_id).cloned().unwrap_or_default();
|
||||
let workspace = workspaces
|
||||
.get(&repo.workspace_id)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let detail = repo.into_detail(owner, workspace);
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(detail)))
|
||||
}
|
||||
|
||||
+14
-3
@@ -4,7 +4,8 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::Repo;
|
||||
use crate::models::base_info::{resolve_users, resolve_workspaces};
|
||||
use crate::models::repos::RepoDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::service::repo::core::UpdateRepoParams;
|
||||
use crate::session::Session;
|
||||
@@ -42,7 +43,7 @@ pub struct PathParams {
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Repository updated successfully. Returns the updated repository with full metadata.", body = ApiResponse<Repo>),
|
||||
(status = 200, description = "Repository updated successfully. Returns the updated repository with full metadata.", body = ApiResponse<RepoDetail>),
|
||||
(status = 400, description = "Invalid parameters: name too long, invalid characters, default branch doesn't exist, or public repos disabled", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions (requires Admin role or higher)", body = ApiErrorResponse),
|
||||
@@ -70,5 +71,15 @@ pub async fn update(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(repo)))
|
||||
let db = &service.ctx.db;
|
||||
let users = resolve_users(db, &[repo.owner_id]).await?;
|
||||
let workspaces = resolve_workspaces(db, &[repo.workspace_id]).await?;
|
||||
let owner = users.get(&repo.owner_id).cloned().unwrap_or_default();
|
||||
let workspace = workspaces
|
||||
.get(&repo.workspace_id)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let detail = repo.into_detail(owner, workspace);
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(detail)))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::repos::RepoCommitComment;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub repo_name: String,
|
||||
pub comment_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UpdateCommitCommentParams {
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/commit-comments/{comment_id}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoUpdateCommitComment",
|
||||
params(PathParams),
|
||||
request_body(
|
||||
content = UpdateCommitCommentParams,
|
||||
description = "Commit comment update parameters",
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Commit comment updated successfully", body = ApiResponse<RepoCommitComment>),
|
||||
(status = 400, description = "Invalid parameters", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Commit comment not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn update_commit_comment(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
params: web::Json<UpdateCommitCommentParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let comment = service
|
||||
.repo
|
||||
.repo_update_commit_comment(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
path.comment_id,
|
||||
¶ms.body,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(comment)))
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UpdateTagBody {
|
||||
pub new_name: Option<String>,
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/tags/{tag_name}",
|
||||
tag = "Repos",
|
||||
operation_id = "repoUpdateTag",
|
||||
params(PathParams),
|
||||
request_body(content = UpdateTagBody),
|
||||
responses(
|
||||
(status = 200, description = "Tag updated (delete+recreate if renamed)", body = ApiResponse<crate::pb::repo::Tag>),
|
||||
(status = 400, description = "Invalid parameters", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Tag not found", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn update_tag(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
body: web::Json<UpdateTagBody>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let msg = body.message.clone();
|
||||
let new_name = body.new_name.as_deref().unwrap_or(&path.tag_name);
|
||||
|
||||
let tag = service
|
||||
.repo
|
||||
.git_get_tag(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
&path.tag_name,
|
||||
)
|
||||
.await?;
|
||||
|
||||
service
|
||||
.repo
|
||||
.git_delete_tag(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
&path.tag_name,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let target_hex = tag.target_oid.map(|o| o.hex).unwrap_or_default();
|
||||
let result = service
|
||||
.repo
|
||||
.git_create_tag(
|
||||
&session,
|
||||
&path.workspace_name,
|
||||
&path.repo_name,
|
||||
new_name,
|
||||
&target_hex,
|
||||
msg.clone(),
|
||||
msg.is_some(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
Reference in New Issue
Block a user