systemprompt_users/sessions/
providers.rs1use crate::Result;
7use crate::repository::SessionRepository;
8use async_trait::async_trait;
9use chrono::Utc;
10use systemprompt_identifiers::{SessionId, UserId};
11use systemprompt_traits::session_store::CreateSessionParams;
12use systemprompt_traits::{
13 ActiveSession, AnalyticsProviderError, AnalyticsResult, AnalyticsSession, CreateSessionInput,
14 SessionProvider, SessionUsageCounters,
15};
16#[async_trait]
17impl SessionProvider for SessionRepository {
18 async fn create_session(&self, input: CreateSessionInput<'_>) -> AnalyticsResult<()> {
19 self.create_analytics_session(input)
20 .await
21 .map_err(|e| AnalyticsProviderError::Internal(e.into()))
22 }
23
24 async fn find_recent_session_by_fingerprint(
25 &self,
26 fingerprint: &str,
27 max_age_seconds: i64,
28 ) -> AnalyticsResult<Option<AnalyticsSession>> {
29 let result = self
30 .find_recent_anonymous_by_fingerprint(fingerprint, max_age_seconds)
31 .await
32 .map_err(|e| AnalyticsProviderError::Internal(e.into()))?;
33
34 Ok(result.map(|r| AnalyticsSession {
35 session_id: r.session_id,
36 user_id: r.user_id,
37 fingerprint: Some(fingerprint.to_owned()),
38 created_at: Utc::now(),
39 }))
40 }
41
42 async fn find_session_by_id(
43 &self,
44 session_id: &SessionId,
45 ) -> AnalyticsResult<Option<AnalyticsSession>> {
46 let result = self
47 .find_by_id(session_id)
48 .await
49 .map_err(|e| AnalyticsProviderError::Internal(e.into()))?;
50
51 Ok(result.map(|r| AnalyticsSession {
52 session_id: r.session_id,
53 user_id: r.user_id,
54 fingerprint: r.fingerprint_hash,
55 created_at: r.started_at.unwrap_or_else(Utc::now),
56 }))
57 }
58
59 async fn find_active_session_by_id(
60 &self,
61 session_id: &SessionId,
62 ) -> AnalyticsResult<Option<ActiveSession>> {
63 let result = self
64 .find_active_by_id(session_id)
65 .await
66 .map_err(|e| AnalyticsProviderError::Internal(e.into()))?;
67
68 Ok(result.map(|r| ActiveSession { user_id: r.user_id }))
69 }
70
71 async fn revoke_session(&self, session_id: &SessionId) -> AnalyticsResult<()> {
72 self.revoke_session(session_id)
73 .await
74 .map_err(|e| AnalyticsProviderError::Internal(e.into()))
75 }
76
77 async fn revoke_all_sessions_for_user(&self, user_id: &UserId) -> AnalyticsResult<u64> {
78 self.revoke_all_for_user(user_id)
79 .await
80 .map_err(|e| AnalyticsProviderError::Internal(e.into()))
81 }
82
83 async fn migrate_user_sessions(
84 &self,
85 from_user_id: &UserId,
86 to_user_id: &UserId,
87 ) -> AnalyticsResult<u64> {
88 self.migrate_user_sessions(from_user_id, to_user_id)
89 .await
90 .map_err(|e| AnalyticsProviderError::Internal(e.into()))
91 }
92
93 async fn mark_session_converted(&self, session_id: &SessionId) -> AnalyticsResult<()> {
94 self.mark_converted(session_id)
95 .await
96 .map_err(|e| AnalyticsProviderError::Internal(e.into()))
97 }
98}
99
100#[async_trait]
101impl SessionUsageCounters for SessionRepository {
102 async fn increment_task_count(&self, session_id: &SessionId) -> AnalyticsResult<()> {
103 Self::increment_task_count(self, session_id)
104 .await
105 .map_err(|e| AnalyticsProviderError::Internal(e.into()))
106 }
107
108 async fn increment_message_count(&self, session_id: &SessionId) -> AnalyticsResult<()> {
109 Self::increment_message_count(self, session_id)
110 .await
111 .map_err(|e| AnalyticsProviderError::Internal(e.into()))
112 }
113}
114
115impl SessionRepository {
116 pub async fn create_analytics_session(&self, input: CreateSessionInput<'_>) -> Result<()> {
117 let fingerprint = input.analytics.compute_fingerprint();
118
119 let params = CreateSessionParams {
120 session_id: input.session_id,
121 user_id: input.user_id,
122 session_source: input.session_source,
123 fingerprint_hash: Some(&fingerprint),
124 ip_address: input.analytics.ip_address.as_deref(),
125 user_agent: input.analytics.user_agent.as_deref(),
126 device_type: input.analytics.device_type.as_deref(),
127 browser: input.analytics.browser.as_deref(),
128 os: input.analytics.os.as_deref(),
129 country: input.analytics.country.as_deref(),
130 region: input.analytics.region.as_deref(),
131 city: input.analytics.city.as_deref(),
132 preferred_locale: input.analytics.preferred_locale.as_deref(),
133 referrer_source: input.analytics.referrer_source.as_deref(),
134 referrer_url: input.analytics.referrer_url.as_deref(),
135 landing_page: input.analytics.landing_page.as_deref(),
136 entry_url: input.analytics.entry_url.as_deref(),
137 utm_source: input.analytics.utm_source.as_deref(),
138 utm_medium: input.analytics.utm_medium.as_deref(),
139 utm_content: input.analytics.utm_content.as_deref(),
140 utm_term: input.analytics.utm_term.as_deref(),
141 utm_campaign: input.analytics.utm_campaign.as_deref(),
142 is_bot: input.is_bot,
143 is_ai_crawler: input.is_ai_crawler,
144 expires_at: input.expires_at,
145 };
146
147 self.create_session(¶ms).await?;
148
149 Ok(())
150 }
151}