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