use git::facade as access; use git::facade::GitContext; use models::pull_request::pull_requests; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use uuid::Uuid; use crate::errors::PullError; use crate::services::service::PullService; #[derive(Clone)] pub struct PullHandle { pub ctx: GitContext, pub pull: pull_requests::Model, pub caller: Option, pub admin: bool, } pub async fn repo_read( svc: &PullService, viewer: Option, space_name: String, repo_name: String, ) -> Result<(GitContext, Option), PullError> { let ctx = svc.git.context(space_name, repo_name).await?; if ctx.model.is_private { let uid = viewer.ok_or(PullError::Unauthorized)?; access::authorize_repo_access(&svc.git, uid, &ctx, false, false).await?; return Ok((ctx, Some(uid))); } Ok((ctx, viewer)) } pub async fn repo_write( svc: &PullService, uid: i64, space_name: String, repo_name: String, ) -> Result<(GitContext, i64), PullError> { let ctx = svc.git.context(space_name, repo_name).await?; access::authorize_repo_access(&svc.git, uid, &ctx, true, false).await?; Ok((ctx, uid)) } pub async fn is_admin(svc: &PullService, uid: i64, ctx: &GitContext) -> bool { access::authorize_repo_access(&svc.git, uid, ctx, true, true) .await .is_ok() } /// 按 PR 编号(`123`)或 UUID 在仓库内定位合并请求。 pub async fn find_pull( svc: &PullService, repo_id: Uuid, pull_ref: &str, ) -> Result { let trimmed = pull_ref.trim(); if trimmed.is_empty() { return Err(PullError::BadRequest("empty pull reference".to_string())); } let row = if let Ok(number) = trimmed.parse::() { pull_requests::Entity::find() .filter(pull_requests::Column::RepoId.eq(repo_id)) .filter(pull_requests::Column::AutoincrementId.eq(number)) .one(svc.db_read()) .await? } else if let Ok(id) = Uuid::parse_str(trimmed) { pull_requests::Entity::find() .filter(pull_requests::Column::RepoId.eq(repo_id)) .filter(pull_requests::Column::Id.eq(id)) .one(svc.db_read()) .await? } else { return Err(PullError::BadRequest( "pull reference must be a number or a uuid".to_string(), )); }; row.ok_or(PullError::NotFound) } pub async fn pull_handle_read( svc: &PullService, viewer: Option, space_name: String, repo_name: String, pull_ref: String, ) -> Result { let (ctx, caller) = repo_read(svc, viewer, space_name, repo_name).await?; let pull = find_pull(svc, ctx.model.id, &pull_ref).await?; let admin = match caller { Some(uid) => is_admin(svc, uid, &ctx).await, None => false, }; Ok(PullHandle { ctx, pull, caller, admin, }) } pub async fn pull_handle_write( svc: &PullService, uid: i64, space_name: String, repo_name: String, pull_ref: String, ) -> Result { let (ctx, uid) = repo_write(svc, uid, space_name, repo_name).await?; let pull = find_pull(svc, ctx.model.id, &pull_ref).await?; let admin = is_admin(svc, uid, &ctx).await; Ok(PullHandle { ctx, pull, caller: Some(uid), admin, }) }