Skip to main content

systemprompt_users/models/
mod.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use systemprompt_identifiers::{SessionId, UserId};
5
6pub use systemprompt_models::auth::{UserRole, UserStatus};
7
8#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
9pub struct User {
10    #[sqlx(try_from = "String")]
11    pub id: UserId,
12    pub name: String,
13    pub email: String,
14    pub full_name: Option<String>,
15    pub display_name: Option<String>,
16    pub status: Option<String>,
17    pub email_verified: Option<bool>,
18    pub roles: Vec<String>,
19    pub avatar_url: Option<String>,
20    pub is_bot: bool,
21    pub is_scanner: bool,
22    pub created_at: Option<DateTime<Utc>>,
23    pub updated_at: Option<DateTime<Utc>>,
24}
25
26impl User {
27    pub fn is_active(&self) -> bool {
28        self.status.as_deref() == Some(UserStatus::Active.as_str())
29    }
30
31    pub fn is_admin(&self) -> bool {
32        self.roles.contains(&UserRole::Admin.as_str().to_string())
33    }
34
35    pub fn has_role(&self, role: UserRole) -> bool {
36        self.roles.contains(&role.as_str().to_string())
37    }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
41pub struct UserActivity {
42    #[sqlx(try_from = "String")]
43    pub user_id: UserId,
44    pub last_active: Option<DateTime<Utc>>,
45    pub session_count: i64,
46    pub task_count: i64,
47    pub message_count: i64,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
51pub struct UserWithSessions {
52    #[sqlx(try_from = "String")]
53    pub id: UserId,
54    pub name: String,
55    pub email: String,
56    pub full_name: Option<String>,
57    pub status: Option<String>,
58    pub roles: Vec<String>,
59    pub created_at: Option<DateTime<Utc>>,
60    pub active_sessions: i64,
61    pub last_session_at: Option<DateTime<Utc>>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct UserSession {
66    pub session_id: SessionId,
67    pub user_id: Option<UserId>,
68    pub ip_address: Option<String>,
69    pub user_agent: Option<String>,
70    pub device_type: Option<String>,
71    pub started_at: Option<DateTime<Utc>>,
72    pub last_activity_at: Option<DateTime<Utc>>,
73    pub ended_at: Option<DateTime<Utc>>,
74}
75
76#[derive(Debug, Clone, FromRow)]
77pub(crate) struct UserSessionRow {
78    #[sqlx(try_from = "String")]
79    pub session_id: SessionId,
80    pub user_id: Option<String>,
81    pub ip_address: Option<String>,
82    pub user_agent: Option<String>,
83    pub device_type: Option<String>,
84    pub started_at: Option<DateTime<Utc>>,
85    pub last_activity_at: Option<DateTime<Utc>>,
86    pub ended_at: Option<DateTime<Utc>>,
87}
88
89impl From<UserSessionRow> for UserSession {
90    fn from(row: UserSessionRow) -> Self {
91        Self {
92            session_id: row.session_id,
93            user_id: row.user_id.map(UserId::new),
94            ip_address: row.ip_address,
95            user_agent: row.user_agent,
96            device_type: row.device_type,
97            started_at: row.started_at,
98            last_activity_at: row.last_activity_at,
99            ended_at: row.ended_at,
100        }
101    }
102}
103
104#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
105pub struct UserStats {
106    pub total: i64,
107    pub created_24h: i64,
108    pub created_7d: i64,
109    pub created_30d: i64,
110    pub active: i64,
111    pub suspended: i64,
112    pub admins: i64,
113    pub anonymous: i64,
114    pub bots: i64,
115    pub oldest_user: Option<DateTime<Utc>>,
116    pub newest_user: Option<DateTime<Utc>>,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct UserCountBreakdown {
121    pub total: i64,
122    pub by_status: std::collections::HashMap<String, i64>,
123    pub by_role: std::collections::HashMap<String, i64>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct UserExport {
128    pub id: String,
129    pub name: String,
130    pub email: String,
131    pub full_name: Option<String>,
132    pub display_name: Option<String>,
133    pub status: Option<String>,
134    pub email_verified: Option<bool>,
135    pub roles: Vec<String>,
136    pub is_bot: bool,
137    pub is_scanner: bool,
138    pub created_at: Option<DateTime<Utc>>,
139    pub updated_at: Option<DateTime<Utc>>,
140}
141
142impl From<User> for UserExport {
143    fn from(user: User) -> Self {
144        Self {
145            id: user.id.to_string(),
146            name: user.name,
147            email: user.email,
148            full_name: user.full_name,
149            display_name: user.display_name,
150            status: user.status,
151            email_verified: user.email_verified,
152            roles: user.roles,
153            is_bot: user.is_bot,
154            is_scanner: user.is_scanner,
155            created_at: user.created_at,
156            updated_at: user.updated_at,
157        }
158    }
159}