gitdataai/lib/git/role.rs
2026-05-30 01:38:40 +08:00

163 lines
4.6 KiB
Rust

use model::{
repos::{repo::RepoModel, repo_history_name::RepoHistoryNameModel},
users::user::UserModel,
workspace::{
wk_history_name::WkHistoryNameModel, wk_member::WkMemberModel,
workspace::WorkspaceModel,
},
};
use uuid::Uuid;
use crate::{
AppGitState,
errors::{GitError, GitResult},
};
#[derive(Clone, Debug)]
pub struct DbRepoStatus {
pub repo: RepoModel,
pub wk: WorkspaceModel,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MemberRole {
Owner,
Admin,
Member,
}
impl MemberRole {
pub fn can_write(self) -> bool {
matches!(self, Self::Owner | Self::Admin)
}
}
impl AppGitState {
pub async fn repo(
&self,
wk_name: String,
repo_name: String,
) -> GitResult<DbRepoStatus> {
let wk = self.resolve_wk(&wk_name).await?;
let repo = self.resolve_repo(wk.id, &repo_name).await?;
Ok(DbRepoStatus { repo, wk })
}
pub async fn member_check(
&self,
status: &DbRepoStatus,
user: &UserModel,
) -> GitResult<Option<MemberRole>> {
self.member_check_by_user_id(status, user.id).await
}
pub async fn member_check_by_user_id(
&self,
status: &DbRepoStatus,
user_id: Uuid,
) -> GitResult<Option<MemberRole>> {
let member = db::sqlx::query_as::<_, WkMemberModel>(
"SELECT wk, \"user\", owner, admin, join_at, leave_at \
FROM wk_member \
WHERE wk = $1 AND \"user\" = $2 AND leave_at IS NULL",
)
.bind(status.wk.id)
.bind(user_id)
.fetch_optional(self.db.reader())
.await?;
Ok(member.map(|member| {
if member.owner {
MemberRole::Owner
} else if member.admin {
MemberRole::Admin
} else {
MemberRole::Member
}
}))
}
async fn resolve_wk(&self, name: &str) -> GitResult<WorkspaceModel> {
if let Some(wk) = db::sqlx::query_as::<_, WorkspaceModel>(
"SELECT id, name, description, avatar_url, created_at \
FROM workspace \
WHERE name = $1",
)
.bind(&name)
.fetch_optional(self.db.reader())
.await?
{
return Ok(wk);
}
let Some(history) = db::sqlx::query_as::<_, WkHistoryNameModel>(
"SELECT id, wk, name, changed_by, created_at \
FROM wk_history_name \
WHERE name = $1 \
ORDER BY created_at DESC \
LIMIT 1",
)
.bind(&name)
.fetch_optional(self.db.reader())
.await?
else {
return Err(GitError::RepoNotFound);
};
db::sqlx::query_as::<_, WorkspaceModel>(
"SELECT id, name, description, avatar_url, created_at \
FROM workspace \
WHERE id = $1",
)
.bind(history.wk)
.fetch_optional(self.db.reader())
.await?
.ok_or(GitError::RepoNotFound)
}
async fn resolve_repo(&self, wk: Uuid, name: &str) -> GitResult<RepoModel> {
if let Some(repo) = db::sqlx::query_as::<_, RepoModel>(
"SELECT id, wk, name, description, default_branch, visibility, size_bytes, \
is_archived, is_template, is_mirror, created_by, created_at, updated_at, deleted_at \
FROM repo \
WHERE wk = $1 AND name = $2 AND deleted_at IS NULL",
)
.bind(wk)
.bind(&name)
.fetch_optional(self.db.reader())
.await?
{
return Ok(repo);
}
let Some(history) = db::sqlx::query_as::<_, RepoHistoryNameModel>(
"SELECT h.id, h.repo, h.name, h.changed_by, h.created_at \
FROM repo_history_name h \
INNER JOIN repo r ON h.repo = r.id \
WHERE h.name = $1 AND r.wk = $2 AND r.deleted_at IS NULL \
ORDER BY h.created_at DESC \
LIMIT 1",
)
.bind(&name)
.bind(wk)
.fetch_optional(self.db.reader())
.await?
else {
return Err(GitError::RepoNotFound);
};
db::sqlx::query_as::<_, RepoModel>(
"SELECT id, wk, name, description, default_branch, visibility, size_bytes, \
is_archived, is_template, is_mirror, created_by, created_at, updated_at, deleted_at \
FROM repo \
WHERE id = $1 AND wk = $2 AND deleted_at IS NULL",
)
.bind(history.repo)
.bind(wk)
.fetch_optional(self.db.reader())
.await?
.ok_or(GitError::RepoNotFound)
}
}