use crate::AppService; use crate::error::AppError; use chrono::{DateTime, Utc}; use models::projects::{MemberRole, project_label, project_like, project_members, project_watch}; use models::system::label; use models::users::user; use sea_orm::*; use serde::{Deserialize, Serialize}; use session::Session; use utoipa::ToSchema; use uuid::Uuid; #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub struct ProjectInfoRelational { pub uid: Uuid, pub name: String, pub display_name: String, pub avatar_url: Option, pub description: Option, pub is_public: bool, pub created_at: DateTime, pub updated_at: DateTime, pub created_by: Uuid, pub created_username_name: String, pub created_display_name: Option, pub created_avatar_url: Option, pub member_count: i64, pub like_count: i64, pub watch_count: i64, pub keys: Vec, pub labels: Vec, pub role: Option, pub is_like: bool, pub is_watch: bool, } #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub struct ProjectInfoKeyValue { pub key: String, pub value: String, } #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub struct ProjectInfoLabel { pub name: String, pub color: String, } impl AppService { pub async fn project_info( &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 is_member = self .utils_project_context_role(&ctx, project.name.clone()) .await .ok(); if !project.is_public && is_member.is_none() { return Err(AppError::NotFound("Project not found".to_string())); } let creator = user::Entity::find() .filter(user::Column::Uid.eq(project.created_by)) .one(&self.db) .await? .ok_or(AppError::UserNotFound)?; let member_count = project_members::Entity::find() .filter(project_members::Column::Project.eq(project.id)) .count(&self.db) .await?; let like_count = project_like::Entity::find() .filter(project_like::Column::Project.eq(project.id)) .count(&self.db) .await?; let watch_count = project_watch::Entity::find() .filter(project_watch::Column::Project.eq(project.id)) .count(&self.db) .await?; let labels_model = project_label::Entity::find() .filter(project_label::Column::Project.eq(project.id)) .all(&self.db) .await? .iter() .map(|x| x.label) .collect::>(); let labes = label::Entity::find() .filter(label::Column::Id.is_in(labels_model)) .all(&self.db) .await?; let labels = labes .into_iter() .map(|l| ProjectInfoLabel { name: l.name, color: l.color, }) .collect(); let is_like = project_like::Entity::find() .filter(project_like::Column::Project.eq(project.id)) .filter(project_like::Column::User.eq(user_uid)) .count(&self.db) .await? > 0; let is_watch = project_watch::Entity::find() .filter(project_watch::Column::Project.eq(project.id)) .filter(project_watch::Column::User.eq(user_uid)) .count(&self.db) .await? > 0; Ok(ProjectInfoRelational { uid: project.id, name: project.name, display_name: project.display_name, avatar_url: project.avatar_url, description: project.description, is_public: project.is_public, created_at: project.created_at, updated_at: project.updated_at, created_by: project.created_by, created_username_name: creator.username, created_display_name: creator.display_name, created_avatar_url: creator.avatar_url, member_count: member_count as i64, like_count: like_count as i64, watch_count: watch_count as i64, keys: Vec::new(), labels, role: is_member, is_like, is_watch, }) } }