systemprompt_analytics/services/
providers.rs1use async_trait::async_trait;
14use chrono::Utc;
15use http::HeaderMap;
16use systemprompt_identifiers::{SessionId, UserId};
17use systemprompt_traits::{
18 ActiveSession, AnalyticsProvider, AnalyticsProviderError, AnalyticsResult, AnalyticsSession,
19 CreateSessionInput, ExtractSignals, FingerprintProvider, SessionAnalytics,
20 SessionUsageCounters,
21};
22
23use super::service::AnalyticsService;
24use crate::repository::{FingerprintRepository, SessionRepository};
25
26#[async_trait]
27impl AnalyticsProvider for AnalyticsService {
28 fn extract_analytics(
29 &self,
30 headers: &HeaderMap,
31 signals: ExtractSignals<'_>,
32 ) -> SessionAnalytics {
33 Self::extract_analytics(self, headers, signals)
34 }
35
36 async fn create_session(&self, input: CreateSessionInput<'_>) -> AnalyticsResult<()> {
37 self.create_analytics_session(input)
38 .await
39 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
40 }
41
42 async fn find_recent_session_by_fingerprint(
43 &self,
44 fingerprint: &str,
45 max_age_seconds: i64,
46 ) -> AnalyticsResult<Option<AnalyticsSession>> {
47 let result = Self::find_recent_session_by_fingerprint(self, fingerprint, max_age_seconds)
48 .await
49 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))?;
50
51 Ok(result.map(|r| AnalyticsSession {
52 session_id: r.session_id,
53 user_id: r.user_id,
54 fingerprint: Some(fingerprint.to_owned()),
55 created_at: Utc::now(),
56 }))
57 }
58
59 async fn find_session_by_id(
60 &self,
61 session_id: &SessionId,
62 ) -> AnalyticsResult<Option<AnalyticsSession>> {
63 let result = self
64 .session_repo()
65 .find_by_id(session_id)
66 .await
67 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))?;
68
69 Ok(result.map(|r| AnalyticsSession {
70 session_id: r.session_id,
71 user_id: r.user_id,
72 fingerprint: r.fingerprint_hash,
73 created_at: r.started_at.unwrap_or_else(Utc::now),
74 }))
75 }
76
77 async fn find_active_session_by_id(
78 &self,
79 session_id: &SessionId,
80 ) -> AnalyticsResult<Option<ActiveSession>> {
81 let result = self
82 .session_repo()
83 .find_active_by_id(session_id)
84 .await
85 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))?;
86
87 Ok(result.map(|r| ActiveSession { user_id: r.user_id }))
88 }
89
90 async fn revoke_session(&self, session_id: &SessionId) -> AnalyticsResult<()> {
91 self.session_repo()
92 .revoke_session(session_id)
93 .await
94 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
95 }
96
97 async fn revoke_all_sessions_for_user(&self, user_id: &UserId) -> AnalyticsResult<u64> {
98 self.session_repo()
99 .revoke_all_for_user(user_id)
100 .await
101 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
102 }
103
104 async fn migrate_user_sessions(
105 &self,
106 from_user_id: &UserId,
107 to_user_id: &UserId,
108 ) -> AnalyticsResult<u64> {
109 self.session_repo()
110 .migrate_user_sessions(from_user_id, to_user_id)
111 .await
112 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
113 }
114
115 async fn mark_session_converted(&self, session_id: &SessionId) -> AnalyticsResult<()> {
116 self.session_repo()
117 .mark_converted(session_id)
118 .await
119 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
120 }
121}
122
123#[async_trait]
124impl FingerprintProvider for FingerprintRepository {
125 async fn count_active_sessions(&self, fingerprint: &str) -> AnalyticsResult<i64> {
126 self.count_active_sessions(fingerprint)
127 .await
128 .map(i64::from)
129 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
130 }
131
132 async fn find_reusable_session(&self, fingerprint: &str) -> AnalyticsResult<Option<String>> {
133 self.find_reusable_session(fingerprint)
134 .await
135 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
136 }
137
138 async fn upsert_fingerprint(
139 &self,
140 fingerprint: &str,
141 ip_address: Option<&str>,
142 user_agent: Option<&str>,
143 _screen_info: Option<&str>,
144 ) -> AnalyticsResult<()> {
145 self.upsert_fingerprint(fingerprint, ip_address, user_agent, None)
146 .await
147 .map(|_| ())
148 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
149 }
150}
151
152#[async_trait]
153impl SessionUsageCounters for SessionRepository {
154 async fn increment_task_count(&self, session_id: &SessionId) -> AnalyticsResult<()> {
155 Self::increment_task_count(self, session_id)
156 .await
157 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
158 }
159
160 async fn increment_message_count(&self, session_id: &SessionId) -> AnalyticsResult<()> {
161 Self::increment_message_count(self, session_id)
162 .await
163 .map_err(|e| AnalyticsProviderError::Internal(e.to_string()))
164 }
165}