194 lines
6.5 KiB
Rust
194 lines
6.5 KiB
Rust
use crate::AppService;
|
|
use crate::error::AppError;
|
|
use models::projects::{MemberRole, ProjectMember, project_audit_log, project_members};
|
|
use models::repos::repo;
|
|
use sea_orm::*;
|
|
use serde::{Deserialize, Serialize};
|
|
use session::Session;
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
|
|
pub struct TransferRepoParams {
|
|
pub target_project_name: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
|
|
pub struct TransferRepoResponse {
|
|
pub repo_id: Uuid,
|
|
pub old_project_name: String,
|
|
pub new_project_name: String,
|
|
pub repo_name: String,
|
|
}
|
|
|
|
impl AppService {
|
|
pub async fn transfer_repo(
|
|
&self,
|
|
ctx: &Session,
|
|
source_project_name: String,
|
|
repo_name: String,
|
|
params: TransferRepoParams,
|
|
) -> Result<TransferRepoResponse, AppError> {
|
|
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
|
|
|
let source_project = self
|
|
.utils_find_project_by_name(source_project_name.clone())
|
|
.await?;
|
|
let target_project = self
|
|
.utils_find_project_by_name(params.target_project_name.clone())
|
|
.await?;
|
|
|
|
let source_member = ProjectMember::find()
|
|
.filter(project_members::Column::Project.eq(source_project.id))
|
|
.filter(project_members::Column::User.eq(user_uid))
|
|
.one(&self.db)
|
|
.await?
|
|
.ok_or(AppError::PermissionDenied)?;
|
|
|
|
if source_member
|
|
.scope_role()
|
|
.map_err(|_| AppError::InternalError)?
|
|
!= MemberRole::Owner
|
|
&& source_member
|
|
.scope_role()
|
|
.map_err(|_| AppError::InternalError)?
|
|
!= MemberRole::Admin
|
|
{
|
|
return Err(AppError::PermissionDenied);
|
|
}
|
|
|
|
let target_member = ProjectMember::find()
|
|
.filter(project_members::Column::Project.eq(target_project.id))
|
|
.filter(project_members::Column::User.eq(user_uid))
|
|
.one(&self.db)
|
|
.await?
|
|
.ok_or(AppError::PermissionDenied)?;
|
|
|
|
if target_member
|
|
.scope_role()
|
|
.map_err(|_| AppError::InternalError)?
|
|
!= MemberRole::Owner
|
|
&& target_member
|
|
.scope_role()
|
|
.map_err(|_| AppError::InternalError)?
|
|
!= MemberRole::Admin
|
|
{
|
|
return Err(AppError::PermissionDenied);
|
|
}
|
|
|
|
let repository = repo::Entity::find()
|
|
.filter(repo::Column::RepoName.eq(repo_name.clone()))
|
|
.one(&self.db)
|
|
.await?
|
|
.ok_or(AppError::NotFound("Repository not found".to_string()))?;
|
|
|
|
if repository.project != source_project.id {
|
|
return Err(AppError::NotFound(
|
|
"Repository not found in source project".to_string(),
|
|
));
|
|
}
|
|
|
|
let target_repo_exists = repo::Entity::find()
|
|
.filter(repo::Column::RepoName.eq(repo_name.clone()))
|
|
.one(&self.db)
|
|
.await?;
|
|
|
|
if target_repo_exists.is_some() {
|
|
return Err(AppError::InternalServerError(
|
|
"Repository with same name already exists in target project".to_string(),
|
|
));
|
|
}
|
|
|
|
let txn = self.db.begin().await?;
|
|
|
|
let old_project_name = source_project.name.clone();
|
|
let mut active_repo: repo::ActiveModel = repository.clone().into();
|
|
active_repo.project = Set(target_project.id);
|
|
|
|
let updated_repo = active_repo.update(&txn).await?;
|
|
|
|
let source_log = project_audit_log::ActiveModel {
|
|
project: Set(source_project.id),
|
|
actor: Set(user_uid),
|
|
action: Set("repo_transfer_out".to_string()),
|
|
details: Set(Some(serde_json::json!({
|
|
"repo_id": repository.id,
|
|
"repo_name": repo_name,
|
|
"target_project": target_project.name,
|
|
}))),
|
|
created_at: Set(chrono::Utc::now()),
|
|
..Default::default()
|
|
};
|
|
source_log.insert(&txn).await?;
|
|
|
|
let target_log = project_audit_log::ActiveModel {
|
|
project: Set(target_project.id),
|
|
actor: Set(user_uid),
|
|
action: Set("repo_transfer_in".to_string()),
|
|
details: Set(Some(serde_json::json!({
|
|
"repo_id": repository.id,
|
|
"repo_name": repo_name,
|
|
"source_project": source_project.name,
|
|
}))),
|
|
created_at: Set(chrono::Utc::now()),
|
|
..Default::default()
|
|
};
|
|
target_log.insert(&txn).await?;
|
|
|
|
txn.commit().await?;
|
|
|
|
let _ = self
|
|
.project_log_activity(
|
|
source_project.id,
|
|
Some(updated_repo.id),
|
|
user_uid,
|
|
super::activity::ActivityLogParams {
|
|
event_type: "repo_transfer_out".to_string(),
|
|
title: format!(
|
|
"{} transferred repository '{}' to project '{}'",
|
|
user_uid, repo_name, target_project.name
|
|
),
|
|
repo_id: Some(updated_repo.id),
|
|
content: None,
|
|
event_id: None,
|
|
event_sub_id: None,
|
|
metadata: Some(serde_json::json!({
|
|
"repo_name": repo_name,
|
|
"target_project": target_project.name,
|
|
})),
|
|
is_private: false,
|
|
},
|
|
)
|
|
.await;
|
|
let _ = self
|
|
.project_log_activity(
|
|
target_project.id,
|
|
Some(updated_repo.id),
|
|
user_uid,
|
|
super::activity::ActivityLogParams {
|
|
event_type: "repo_transfer_in".to_string(),
|
|
title: format!(
|
|
"{} transferred repository '{}' from project '{}'",
|
|
user_uid, repo_name, source_project.name
|
|
),
|
|
repo_id: Some(updated_repo.id),
|
|
content: None,
|
|
event_id: None,
|
|
event_sub_id: None,
|
|
metadata: Some(serde_json::json!({
|
|
"repo_name": repo_name,
|
|
"source_project": source_project.name,
|
|
})),
|
|
is_private: false,
|
|
},
|
|
)
|
|
.await;
|
|
|
|
Ok(TransferRepoResponse {
|
|
repo_id: updated_repo.id,
|
|
old_project_name,
|
|
new_project_name: target_project.name,
|
|
repo_name,
|
|
})
|
|
}
|
|
}
|