systemprompt_users/repository/user/
session.rs1use chrono::Utc;
2use systemprompt_identifiers::{SessionId, UserId};
3
4use crate::error::Result;
5use crate::models::{UserSession, UserSessionRow};
6use crate::repository::{UserRepository, MAX_PAGE_SIZE};
7
8impl UserRepository {
9 pub async fn list_sessions(&self, user_id: &UserId) -> Result<Vec<UserSession>> {
10 let rows = sqlx::query_as!(
11 UserSessionRow,
12 r#"
13 SELECT session_id, user_id, ip_address, user_agent, device_type,
14 started_at, last_activity_at, ended_at
15 FROM user_sessions
16 WHERE user_id = $1
17 ORDER BY last_activity_at DESC
18 "#,
19 user_id.as_str()
20 )
21 .fetch_all(&*self.pool)
22 .await?;
23
24 Ok(rows.into_iter().map(UserSession::from).collect())
25 }
26
27 pub async fn list_active_sessions(&self, user_id: &UserId) -> Result<Vec<UserSession>> {
28 let rows = sqlx::query_as!(
29 UserSessionRow,
30 r#"
31 SELECT session_id, user_id, ip_address, user_agent, device_type,
32 started_at, last_activity_at, ended_at
33 FROM user_sessions
34 WHERE user_id = $1 AND ended_at IS NULL
35 ORDER BY last_activity_at DESC
36 "#,
37 user_id.as_str()
38 )
39 .fetch_all(&*self.pool)
40 .await?;
41
42 Ok(rows.into_iter().map(UserSession::from).collect())
43 }
44
45 pub async fn list_recent_sessions(
46 &self,
47 user_id: &UserId,
48 limit: i64,
49 ) -> Result<Vec<UserSession>> {
50 let safe_limit = limit.min(MAX_PAGE_SIZE);
51 let rows = sqlx::query_as!(
52 UserSessionRow,
53 r#"
54 SELECT session_id, user_id, ip_address, user_agent, device_type,
55 started_at, last_activity_at, ended_at
56 FROM user_sessions
57 WHERE user_id = $1
58 ORDER BY last_activity_at DESC
59 LIMIT $2
60 "#,
61 user_id.as_str(),
62 safe_limit
63 )
64 .fetch_all(&*self.pool)
65 .await?;
66
67 Ok(rows.into_iter().map(UserSession::from).collect())
68 }
69
70 pub async fn end_session(&self, session_id: &SessionId) -> Result<bool> {
71 let result = sqlx::query!(
72 r#"
73 UPDATE user_sessions
74 SET ended_at = $1
75 WHERE session_id = $2 AND ended_at IS NULL
76 "#,
77 Utc::now(),
78 session_id.as_str()
79 )
80 .execute(&*self.pool)
81 .await?;
82
83 Ok(result.rows_affected() > 0)
84 }
85
86 pub async fn end_all_sessions(&self, user_id: &UserId) -> Result<u64> {
87 let result = sqlx::query!(
88 r#"
89 UPDATE user_sessions
90 SET ended_at = $1
91 WHERE user_id = $2 AND ended_at IS NULL
92 "#,
93 Utc::now(),
94 user_id.as_str()
95 )
96 .execute(&*self.pool)
97 .await?;
98
99 Ok(result.rows_affected())
100 }
101}