Skip to main content

sova_activity/
list.rs

1//! Query helpers for activity_log.
2
3use crate::entity;
4use chrono::{DateTime, Utc};
5use sova_core::Result;
6use sova_db::{DbError, DbHandle};
7use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
8use serde::Serialize;
9
10#[derive(Debug, Clone, Default)]
11pub struct ActivityFilter {
12    pub subject_type: Option<String>,
13    pub subject_id: Option<String>,
14    pub event: Option<String>,
15    pub actor_id: Option<i64>,
16    pub limit: u64,
17}
18
19#[derive(Debug, Clone, Serialize)]
20pub struct ActivityRow {
21    pub id: i64,
22    pub actor_id: Option<i64>,
23    pub subject_type: String,
24    pub subject_id: String,
25    pub event: String,
26    pub properties: serde_json::Value,
27    pub ip: Option<String>,
28    pub user_agent: Option<String>,
29    pub created_at: DateTime<Utc>,
30}
31
32impl From<entity::Model> for ActivityRow {
33    fn from(m: entity::Model) -> Self {
34        let properties = serde_json::from_str(&m.properties).unwrap_or(serde_json::json!({}));
35        Self {
36            id: m.id,
37            actor_id: m.actor_id,
38            subject_type: m.subject_type,
39            subject_id: m.subject_id,
40            event: m.event,
41            properties,
42            ip: m.ip,
43            user_agent: m.user_agent,
44            created_at: m.created_at,
45        }
46    }
47}
48
49pub async fn list_activity(db: &DbHandle, filter: ActivityFilter) -> Result<Vec<ActivityRow>> {
50    let limit = filter.limit.clamp(1, 200);
51    let mut q = entity::Entity::find();
52    if let Some(ref t) = filter.subject_type {
53        q = q.filter(entity::Column::SubjectType.eq(t.clone()));
54    }
55    if let Some(ref id) = filter.subject_id {
56        q = q.filter(entity::Column::SubjectId.eq(id.clone()));
57    }
58    if let Some(ref ev) = filter.event {
59        q = q.filter(entity::Column::Event.eq(ev.clone()));
60    }
61    if let Some(aid) = filter.actor_id {
62        q = q.filter(entity::Column::ActorId.eq(aid));
63    }
64    let rows = q
65        .order_by_desc(entity::Column::CreatedAt)
66        .limit(limit)
67        .all(db)
68        .await
69        .map_err(|e| sova_core::Error::from(DbError::from(e)))?;
70    Ok(rows.into_iter().map(ActivityRow::from).collect())
71}