use crate::AppService; use crate::error::AppError; use models::projects::{project_audit_log, project_like}; use models::users::user; use sea_orm::*; use serde::{Deserialize, Serialize}; use session::Session; use uuid::Uuid; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, utoipa::ToSchema)] pub struct LikeUserInfo { pub uid: Uuid, pub username: String, pub avatar_url: String, } impl AppService { pub async fn project_like(&self, ctx: &Session, project_name: String) -> Result<(), AppError> { let user_uid = ctx.user().ok_or(AppError::Unauthorized)?; let project = self.utils_find_project_by_name(project_name).await?; let like_exists = project_like::Entity::find() .filter(project_like::Column::User.eq(user_uid)) .filter(project_like::Column::Project.eq(project.id)) .one(&self.db) .await?; if like_exists.is_some() { return Err(AppError::BadRequest( "Already liked this project".to_string(), )); } project_like::Entity::insert(project_like::ActiveModel { project: Set(project.id), user: Set(user_uid), created_at: Set(chrono::Utc::now()), }) .exec(&self.db) .await?; let _ = self .project_log_activity( project.id, None, user_uid, super::activity::ActivityLogParams { event_type: "project_star".to_string(), title: format!("{} starred the project", user_uid), repo_id: None, content: None, event_id: None, event_sub_id: None, metadata: Some(serde_json::json!({ "project_name": project.name.clone(), })), is_private: false, }, ) .await; let log = project_audit_log::ActiveModel { project: Set(project.id), actor: Set(user_uid), action: Set("project_like".to_string()), details: Set(Some(serde_json::json!({ "project_name": project.name.clone(), }))), created_at: Set(chrono::Utc::now()), ..Default::default() }; log.insert(&self.db).await?; Ok(()) } pub async fn project_unlike( &self, ctx: &Session, project_name: String, ) -> Result<(), AppError> { let user_uid = ctx.user().ok_or(AppError::Unauthorized)?; let project = self.utils_find_project_by_name(project_name).await?; let like_exists = project_like::Entity::find() .filter(project_like::Column::User.eq(user_uid)) .filter(project_like::Column::Project.eq(project.id)) .one(&self.db) .await?; if like_exists.is_none() { return Err(AppError::NotFound("Like not found".to_string())); } project_like::Entity::delete_many() .filter(project_like::Column::User.eq(user_uid)) .filter(project_like::Column::Project.eq(project.id)) .exec(&self.db) .await?; let _ = self .project_log_activity( project.id, None, user_uid, super::activity::ActivityLogParams { event_type: "project_unstar".to_string(), title: format!("{} unstarred the project", user_uid), repo_id: None, content: None, event_id: None, event_sub_id: None, metadata: Some(serde_json::json!({ "project_name": project.name.clone(), })), is_private: false, }, ) .await; let log = project_audit_log::ActiveModel { project: Set(project.id), actor: Set(user_uid), action: Set("project_unlike".to_string()), details: Set(Some(serde_json::json!({ "project_name": project.name.clone(), }))), created_at: Set(chrono::Utc::now()), ..Default::default() }; log.insert(&self.db).await?; Ok(()) } pub async fn project_is_like( &self, ctx: &Session, project_name: String, ) -> Result { let user_uid = ctx.user().ok_or(AppError::Unauthorized)?; let project = self.utils_find_project_by_name(project_name).await?; let like_exists = project_like::Entity::find() .filter(project_like::Column::User.eq(user_uid)) .filter(project_like::Column::Project.eq(project.id)) .one(&self.db) .await?; Ok(like_exists.is_some()) } pub async fn project_likes(&self, project_name: String) -> Result { let project = self.utils_find_project_by_name(project_name).await?; let likes = project_like::Entity::find() .filter(project_like::Column::Project.eq(project.id)) .count(&self.db) .await?; Ok(likes) } pub async fn project_like_user_list( &self, project_name: String, pager: crate::Pager, ) -> Result, AppError> { let project = self.utils_find_project_by_name(project_name).await?; let likes = project_like::Entity::find() .filter(project_like::Column::Project.eq(project.id)) .order_by_desc(project_like::Column::CreatedAt) .limit(pager.par_page as u64) .offset(((pager.page - 1) * pager.par_page) as u64) .all(&self.db) .await?; let user_uids: Vec = likes.into_iter().map(|like| like.user).collect(); let users = if user_uids.is_empty() { vec![] } else { user::Entity::find() .filter(user::Column::Uid.is_in(user_uids)) .all(&self.db) .await? .into_iter() .map(|u| LikeUserInfo { uid: u.uid, username: u.username, avatar_url: u.avatar_url.unwrap_or_default(), }) .collect() }; Ok(users) } }