use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use uuid::Uuid; use crate::access::{Capability, require}; use crate::errors::{SpaceError, SpaceResult}; use crate::services::service::SpaceService; fn access_error(error: api::AccessError) -> SpaceError { SpaceError::Internal(error.to_string()) } impl SpaceService { async fn space_of(&self, space_id: Uuid) -> SpaceResult> { match self.access.by_id(space_id).await { Ok(space) => Ok(Some(space)), Err(api::AccessError::NotFound) => Ok(None), Err(error) => Err(access_error(error)), } } pub async fn authorize_internal_repo(&self, user_id: i64, repo_id: Uuid) -> SpaceResult { let repo = models::repo::repos::Entity::find_by_id(repo_id) .one(self.access.database.read()) .await .map_err(|error| SpaceError::Internal(error.to_string()))? .ok_or(SpaceError::NotFound)?; let Some(space) = self.space_of(repo.space_id).await? else { return Ok(false); }; space .can( user_id, Capability::RepositoryRead { repository: repo.id, private: repo.is_private, }, ) .await .map_err(access_error) } pub async fn authorize_internal_issue( &self, user_id: i64, issue_id: Uuid, ) -> SpaceResult { let issue = models::issue::issues::Entity::find_by_id(issue_id) .one(self.access.database.read()) .await .map_err(|error| SpaceError::Internal(error.to_string()))? .ok_or(SpaceError::NotFound)?; let Some(space) = self.space_of(issue.space_id).await? else { return Ok(false); }; require(&space, user_id, Capability::Read) .await .map(|()| true) .or_else(|error| match error { api::AccessError::Forbidden => Ok(false), other => Err(access_error(other)), }) } pub async fn authorize_internal_pull(&self, user_id: i64, pull_id: Uuid) -> SpaceResult { let pull = models::pull_request::pull_requests::Entity::find_by_id(pull_id) .one(self.access.database.read()) .await .map_err(|error| SpaceError::Internal(error.to_string()))? .ok_or(SpaceError::NotFound)?; let repo = models::repo::repos::Entity::find_by_id(pull.repo_id) .one(self.access.database.read()) .await .map_err(|error| SpaceError::Internal(error.to_string()))? .ok_or(SpaceError::NotFound)?; let Some(space) = self.space_of(repo.space_id).await? else { return Ok(false); }; space .can( user_id, Capability::RepositoryRead { repository: repo.id, private: repo.is_private, }, ) .await .map_err(access_error) } pub async fn authorize_internal_space( &self, user_id: i64, space_id: Uuid, ) -> SpaceResult { let space = self.access.by_id(space_id).await.map_err(access_error)?; require(&space, user_id, Capability::Read) .await .map(|()| true) .or_else(|error| match error { api::AccessError::Forbidden => Ok(false), other => Err(access_error(other)), }) } } impl SpaceService { /// 内部事件扇出:列出空间(组织)的有效成员 id(未退出、未封禁)。 pub async fn internal_space_member_ids(&self, space_id: Uuid) -> SpaceResult> { let rows = models::space::space_members::Entity::find() .filter(models::space::space_members::Column::OrganizationId.eq(space_id)) .filter(models::space::space_members::Column::LeftAt.is_null()) .filter(models::space::space_members::Column::BannedAt.is_null()) .all(self.access.database.read()) .await .map_err(|error| SpaceError::Internal(error.to_string()))?; Ok(rows.into_iter().map(|row| row.user_id).collect()) } }