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