Skip to content
Clone
a06673b
161 lines 4.6 KB Rust
use db::conn::sea_orm::*;
use db::repo::repo_collaborations as collab_entity;
use db::repo::repo_collaborations::CollaborationMode;
use time::OffsetDateTime;
use uuid::Uuid;
use crate::error::{RepoError, RepoResult};
use crate::features::RepoService;
use super::types::{
AddCollaboratorOption, CollaboratorPage, CollaboratorView, RepoCollaboratorPermission,
UserSummary,
};
fn parse_mode(s: &str) -> RepoResult<CollaborationMode> {
match s {
"read" | "Read" | "READ" => Ok(CollaborationMode::Read),
"write" | "Write" | "WRITE" => Ok(CollaborationMode::Write),
"admin" | "Admin" | "ADMIN" => Ok(CollaborationMode::Admin),
_ => Err(RepoError::BadRequest(format!("invalid permission: {s}"))),
}
}
fn mode_to_str(m: CollaborationMode) -> String {
match m {
CollaborationMode::Read => "read".to_string(),
CollaborationMode::Write => "write".to_string(),
CollaborationMode::Admin => "admin".to_string(),
}
}
fn require_admin(actor: Uuid) -> RepoResult<()> {
if actor.is_nil() {
return Err(RepoError::Unauthorized("authentication required".into()));
}
Ok(())
}
pub async fn list_collaborators(
svc: &RepoService,
actor: Uuid,
repo_id: Uuid,
page: Option<u64>,
limit: Option<u64>,
) -> RepoResult<CollaboratorPage> {
let _ = actor;
let limit = limit.unwrap_or(svc.config.default_pagination);
let offset = page.unwrap_or(0).saturating_mul(limit);
let limit = std::cmp::min(limit, svc.config.max_pagination);
let db = svc.db.reader();
let query = collab_entity::Entity::find().filter(collab_entity::Column::RepoId.eq(repo_id));
let total = query.clone().count(&db).await?;
let items = query
.limit(Some(limit))
.offset(Some(offset))
.all(&db)
.await?
.into_iter()
.map(CollaboratorView::from)
.collect();
Ok(CollaboratorPage {
items,
total: total as i64,
})
}
pub async fn is_collaborator(
svc: &RepoService,
actor: Uuid,
repo_id: Uuid,
user_id: Uuid,
) -> RepoResult<bool> {
let _ = actor;
let db = svc.db.reader();
let hit = collab_entity::Entity::find()
.filter(collab_entity::Column::RepoId.eq(repo_id))
.filter(collab_entity::Column::UserId.eq(user_id))
.one(&db)
.await?;
Ok(hit.is_some())
}
pub async fn add_collaborator(
svc: &RepoService,
actor: Uuid,
repo_id: Uuid,
user_id: Uuid,
input: AddCollaboratorOption,
) -> RepoResult<()> {
require_admin(actor)?;
let mode = parse_mode(&input.permission)?;
let db = svc.db.writer();
let now = OffsetDateTime::now_utc();
let existing = collab_entity::Entity::find()
.filter(collab_entity::Column::RepoId.eq(repo_id))
.filter(collab_entity::Column::UserId.eq(user_id))
.one(&db)
.await?;
if let Some(current) = existing {
let mut active: collab_entity::ActiveModel = current.into();
active.mode = Set(mode);
active.updated_at = Set(now);
active.update(&db).await?;
} else {
let active: collab_entity::ActiveModel = collab_entity::ActiveModel {
id: Set(Uuid::new_v4()),
repo_id: Set(repo_id),
user_id: Set(user_id),
mode: Set(mode),
created_at: Set(now),
updated_at: Set(now),
};
active.insert(&db).await?;
}
Ok(())
}
pub async fn delete_collaborator(
svc: &RepoService,
actor: Uuid,
repo_id: Uuid,
user_id: Uuid,
) -> RepoResult<()> {
require_admin(actor)?;
let db = svc.db.writer();
collab_entity::Entity::delete_many()
.filter(collab_entity::Column::RepoId.eq(repo_id))
.filter(collab_entity::Column::UserId.eq(user_id))
.exec(&db)
.await?;
Ok(())
}
pub async fn get_repo_permissions(
svc: &RepoService,
actor: Uuid,
repo_id: Uuid,
user_id: Uuid,
) -> RepoResult<RepoCollaboratorPermission> {
let _ = actor;
let db = svc.db.reader();
let collab = collab_entity::Entity::find()
.filter(collab_entity::Column::RepoId.eq(repo_id))
.filter(collab_entity::Column::UserId.eq(user_id))
.one(&db)
.await?;
let permission = match collab {
Some(c) => mode_to_str(c.mode),
None => "none".to_string(),
};
Ok(RepoCollaboratorPermission {
permission,
role: "collaborator".to_string(),
user: UserSummary {
id: user_id,
name: String::new(),
login: String::new(),
email: None,
},
})
}