213 lines
7.0 KiB
Rust
213 lines
7.0 KiB
Rust
use crate::AppService;
|
|
use crate::error::AppError;
|
|
use chrono::Utc;
|
|
use models::repos::repo as repo_model;
|
|
use models::repos::{RepoStar, repo_star};
|
|
use sea_orm::*;
|
|
use serde::{Deserialize, Serialize};
|
|
use session::Session;
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct StarCountResponse {
|
|
pub count: i64,
|
|
}
|
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct StarUserInfo {
|
|
pub uid: String,
|
|
pub username: String,
|
|
pub avatar_url: String,
|
|
}
|
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct StarUserListResponse {
|
|
pub users: Vec<StarUserInfo>,
|
|
}
|
|
impl AppService {
|
|
pub async fn git_star(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
|
let repo = self
|
|
.utils_find_repo(namespace, repo_name.clone(), ctx)
|
|
.await?;
|
|
let existing = RepoStar::find()
|
|
.filter(repo_star::Column::User.eq(user_uid))
|
|
.filter(repo_star::Column::Repo.eq(repo.id))
|
|
.one(&self.db)
|
|
.await?;
|
|
if existing.is_some() {
|
|
return Err(AppError::InternalServerError("already starred".to_string()));
|
|
}
|
|
RepoStar::insert(repo_star::ActiveModel {
|
|
id: Default::default(),
|
|
repo: Set(repo.id),
|
|
user: Set(user_uid),
|
|
created_at: Set(Utc::now()),
|
|
})
|
|
.exec(&self.db)
|
|
.await?;
|
|
|
|
// Log activity: need to look up project_id from repo
|
|
let project_id = match repo_model::Entity::find_by_id(repo.id).one(&self.db).await {
|
|
Ok(Some(r)) => r.project,
|
|
Ok(None) => Uuid::nil(),
|
|
Err(e) => {
|
|
slog::warn!(
|
|
self.logs,
|
|
"failed to look up project_id for activity log: {}",
|
|
e
|
|
);
|
|
Uuid::nil()
|
|
}
|
|
};
|
|
let _ = self
|
|
.project_log_activity(
|
|
project_id,
|
|
Some(repo.id),
|
|
user_uid,
|
|
super::super::project::activity::ActivityLogParams {
|
|
event_type: "repo_star".to_string(),
|
|
title: format!("{} starred repository '{}'", user_uid, repo_name),
|
|
repo_id: Some(repo.id),
|
|
content: None,
|
|
event_id: None,
|
|
event_sub_id: None,
|
|
metadata: Some(serde_json::json!({
|
|
"repo_name": repo_name,
|
|
})),
|
|
is_private: false,
|
|
},
|
|
)
|
|
.await;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn git_unstar(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
|
let repo = self
|
|
.utils_find_repo(namespace, repo_name.clone(), ctx)
|
|
.await?;
|
|
let deleted = RepoStar::delete_many()
|
|
.filter(repo_star::Column::User.eq(user_uid))
|
|
.filter(repo_star::Column::Repo.eq(repo.id))
|
|
.exec(&self.db)
|
|
.await?;
|
|
if deleted.rows_affected == 0 {
|
|
return Err(AppError::InternalServerError("not starred".to_string()));
|
|
}
|
|
let project_id = match repo_model::Entity::find_by_id(repo.id).one(&self.db).await {
|
|
Ok(Some(r)) => r.project,
|
|
Ok(None) => Uuid::nil(),
|
|
Err(e) => {
|
|
slog::warn!(
|
|
self.logs,
|
|
"failed to look up project_id for activity log: {}",
|
|
e
|
|
);
|
|
Uuid::nil()
|
|
}
|
|
};
|
|
let _ = self
|
|
.project_log_activity(
|
|
project_id,
|
|
Some(repo.id),
|
|
user_uid,
|
|
super::super::project::activity::ActivityLogParams {
|
|
event_type: "repo_unstar".to_string(),
|
|
title: format!("{} unstarred repository '{}'", user_uid, repo_name),
|
|
repo_id: Some(repo.id),
|
|
content: None,
|
|
event_id: None,
|
|
event_sub_id: None,
|
|
metadata: Some(serde_json::json!({"repo_name": repo_name})),
|
|
is_private: false,
|
|
},
|
|
)
|
|
.await;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn git_is_starred(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<bool, AppError> {
|
|
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
|
|
let repo = self
|
|
.utils_find_repo(namespace, repo_name.clone(), ctx)
|
|
.await?;
|
|
let existing = RepoStar::find()
|
|
.filter(repo_star::Column::User.eq(user_uid))
|
|
.filter(repo_star::Column::Repo.eq(repo.id))
|
|
.one(&self.db)
|
|
.await?;
|
|
Ok(existing.is_some())
|
|
}
|
|
|
|
pub async fn git_star_count(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<StarCountResponse, AppError> {
|
|
let repo = self
|
|
.utils_find_repo(namespace, repo_name.clone(), ctx)
|
|
.await?;
|
|
let count = RepoStar::find()
|
|
.filter(repo_star::Column::Repo.eq(repo.id))
|
|
.count(&self.db)
|
|
.await?;
|
|
Ok(StarCountResponse {
|
|
count: count as i64,
|
|
})
|
|
}
|
|
|
|
pub async fn git_star_user_list(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
pager: crate::Pager,
|
|
ctx: &Session,
|
|
) -> Result<StarUserListResponse, AppError> {
|
|
let repo = self
|
|
.utils_find_repo(namespace, repo_name.clone(), ctx)
|
|
.await?;
|
|
let page = std::cmp::Ord::max(pager.page, 1);
|
|
let par_page = std::cmp::Ord::min(std::cmp::Ord::max(pager.par_page, 1), 1000);
|
|
let offset_val = (page - 1).saturating_mul(par_page);
|
|
let offset = offset_val as u64;
|
|
let stars = RepoStar::find()
|
|
.filter(repo_star::Column::Repo.eq(repo.id))
|
|
.order_by_desc(repo_star::Column::CreatedAt)
|
|
.limit(par_page as u64)
|
|
.offset(offset)
|
|
.all(&self.db)
|
|
.await?;
|
|
let user_uids: Vec<uuid::Uuid> = stars.into_iter().map(|s| s.user).collect();
|
|
if user_uids.is_empty() {
|
|
return Ok(StarUserListResponse { users: vec![] });
|
|
}
|
|
let users = models::users::user::Entity::find()
|
|
.filter(models::users::user::Column::Uid.is_in(user_uids))
|
|
.all(&self.db)
|
|
.await?
|
|
.into_iter()
|
|
.map(|u| StarUserInfo {
|
|
uid: u.uid.to_string(),
|
|
username: u.username,
|
|
avatar_url: u.avatar_url.unwrap_or_default(),
|
|
})
|
|
.collect();
|
|
Ok(StarUserListResponse { users })
|
|
}
|
|
}
|