Skip to main content

systemprompt_users/repository/user/
session.rs

1use 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}