use db::project::project_activities as activity_entity; use db::project::project_activities::Column as Ac; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, PaginatorTrait}; use uuid::Uuid; use crate::error::ProjectResult; use crate::features::ProjectService; use crate::permission; use super::{ActivityPage, ActivityView, ListActivityQuery}; pub async fn list_activities( svc: &ProjectService, actor: Uuid, space_id: Uuid, project_id: Uuid, q: ListActivityQuery, ) -> ProjectResult { crate::features::authz::require(svc, actor, space_id, project_id, permission::VIEW_PROJECT) .await?; let page = q.page.max(1); let per_page = q.per_page.clamp(1, svc.config.max_page_size); let offset = (page - 1) * per_page; let mut query = activity_entity::Entity::find() .filter(Ac::ProjectId.eq(project_id)) .order_by_desc(Ac::CreatedAt); if let Some(verb) = q.verb { query = query.filter(Ac::Verb.eq(verb)); } let reader = svc.db.reader(); let paginator = query.paginate(&reader, per_page); let total = paginator.num_items().await?; let rows = paginator.fetch_page(offset / per_page).await?; let items = rows.iter().map(activity_view).collect(); Ok(ActivityPage { total, items }) } fn activity_view(m: &activity_entity::Model) -> ActivityView { ActivityView { id: m.id, project_id: m.project_id, actor_id: m.actor_id, verb: m.verb.clone(), target_kind: m.target_kind.clone(), target_id: m.target_id, payload: m.payload.clone(), created_at: m.created_at, } }