systemprompt_users/services/user/
mod.rs1mod provider;
9
10use std::collections::HashMap;
11use systemprompt_database::DbPool;
12use systemprompt_identifiers::{SessionId, UserId};
13
14use crate::error::Result;
15use crate::models::{
16 User, UserActivity, UserCountBreakdown, UserRole, UserSession, UserStats, UserStatus,
17 UserWithSessions,
18};
19use crate::repository::{MergeResult, UpdateUserParams, UserRepository};
20
21#[derive(Debug, Clone)]
22pub struct UserService {
23 repository: UserRepository,
24}
25
26impl UserService {
27 pub fn new(db: &DbPool) -> Result<Self> {
28 Ok(Self {
29 repository: UserRepository::new(db)?,
30 })
31 }
32
33 pub async fn find_by_id(&self, id: &UserId) -> Result<Option<User>> {
34 self.repository.find_by_id(id).await
35 }
36
37 pub async fn find_by_email(&self, email: &str) -> Result<Option<User>> {
38 self.repository.find_by_email(email).await
39 }
40
41 pub async fn find_by_name(&self, name: &str) -> Result<Option<User>> {
42 self.repository.find_by_name(name).await
43 }
44
45 pub async fn find_by_role(&self, role: UserRole) -> Result<Vec<User>> {
46 self.repository.find_by_role(role).await
47 }
48
49 pub async fn find_first_user(&self) -> Result<Option<User>> {
50 self.repository.find_first_user().await
51 }
52
53 pub async fn find_first_admin(&self) -> Result<Option<User>> {
54 self.repository.find_first_admin().await
55 }
56
57 pub async fn find_admin_owner(&self) -> Result<Option<User>> {
58 self.repository.find_admin_owner().await
59 }
60
61 pub async fn find_authenticated_user(&self, user_id: &UserId) -> Result<Option<User>> {
62 self.repository.find_authenticated_user(user_id).await
63 }
64
65 pub async fn find_with_sessions(&self, user_id: &UserId) -> Result<Option<UserWithSessions>> {
66 self.repository.find_with_sessions(user_id).await
67 }
68
69 pub async fn get_activity(&self, user_id: &UserId) -> Result<UserActivity> {
70 self.repository.get_activity(user_id).await
71 }
72
73 pub async fn list(&self, limit: i64, offset: i64) -> Result<Vec<User>> {
74 self.repository.list(limit, offset).await
75 }
76
77 pub async fn list_all(&self) -> Result<Vec<User>> {
78 self.repository.list_all().await
79 }
80
81 pub async fn search(&self, query: &str, limit: i64) -> Result<Vec<User>> {
82 self.repository.search(query, limit).await
83 }
84
85 pub async fn count(&self) -> Result<i64> {
86 self.repository.count().await
87 }
88
89 pub async fn is_temporary_anonymous(&self, id: &UserId) -> Result<bool> {
90 self.repository.is_temporary_anonymous(id).await
91 }
92
93 pub async fn list_non_anonymous_with_sessions(
94 &self,
95 limit: i64,
96 ) -> Result<Vec<UserWithSessions>> {
97 self.repository
98 .list_non_anonymous_with_sessions(limit)
99 .await
100 }
101
102 pub async fn list_sessions(&self, user_id: &UserId) -> Result<Vec<UserSession>> {
103 self.repository.list_sessions(user_id).await
104 }
105
106 pub async fn list_active_sessions(&self, user_id: &UserId) -> Result<Vec<UserSession>> {
107 self.repository.list_active_sessions(user_id).await
108 }
109
110 pub async fn list_recent_sessions(
111 &self,
112 user_id: &UserId,
113 limit: i64,
114 ) -> Result<Vec<UserSession>> {
115 self.repository.list_recent_sessions(user_id, limit).await
116 }
117
118 pub async fn session_exists(&self, session_id: &SessionId) -> Result<bool> {
119 self.repository.session_exists(session_id).await
120 }
121
122 pub async fn end_session(&self, session_id: &SessionId) -> Result<bool> {
123 self.repository.end_session(session_id).await
124 }
125
126 pub async fn end_all_sessions(&self, user_id: &UserId) -> Result<u64> {
127 self.repository.end_all_sessions(user_id).await
128 }
129
130 pub async fn create(
131 &self,
132 name: &str,
133 email: &str,
134 full_name: Option<&str>,
135 display_name: Option<&str>,
136 ) -> Result<User> {
137 self.repository
138 .create(name, email, full_name, display_name)
139 .await
140 }
141
142 pub async fn create_anonymous(&self, fingerprint: &str) -> Result<User> {
143 self.repository.create_anonymous(fingerprint).await
144 }
145
146 pub async fn find_or_create_federated(
147 &self,
148 issuer: &str,
149 external_sub: &str,
150 claims: &systemprompt_traits::FederatedIdentityClaims,
151 ) -> Result<User> {
152 self.repository
153 .find_or_create_federated(issuer, external_sub, claims)
154 .await
155 }
156
157 pub async fn update_email(&self, id: &UserId, email: &str) -> Result<User> {
158 self.repository.update_email(id, email).await
159 }
160
161 pub async fn update_full_name(&self, id: &UserId, full_name: &str) -> Result<User> {
162 self.repository.update_full_name(id, full_name).await
163 }
164
165 pub async fn update_status(&self, id: &UserId, status: UserStatus) -> Result<User> {
166 self.repository.update_status(id, status).await
167 }
168
169 pub async fn update_email_verified(&self, id: &UserId, verified: bool) -> Result<User> {
170 self.repository.update_email_verified(id, verified).await
171 }
172
173 pub async fn update_display_name(&self, id: &UserId, display_name: &str) -> Result<User> {
174 self.repository.update_display_name(id, display_name).await
175 }
176
177 pub async fn update_all_fields(
178 &self,
179 id: &UserId,
180 params: UpdateUserParams<'_>,
181 ) -> Result<User> {
182 self.repository.update_all_fields(id, params).await
183 }
184
185 pub async fn assign_roles(&self, id: &UserId, roles: &[String]) -> Result<User> {
186 self.repository.assign_roles(id, roles).await
187 }
188
189 pub async fn delete(&self, id: &UserId) -> Result<()> {
190 self.repository.delete(id).await
191 }
192
193 pub async fn cleanup_old_anonymous(&self, days: i32) -> Result<u64> {
194 self.repository.cleanup_old_anonymous(days).await
195 }
196
197 pub async fn count_with_breakdown(&self) -> Result<UserCountBreakdown> {
198 let total = self.repository.count().await?;
199 let by_status_vec = self.repository.count_by_status().await?;
200 let by_role_vec = self.repository.count_by_role().await?;
201
202 let by_status: HashMap<String, i64> = by_status_vec.into_iter().collect();
203 let by_role: HashMap<String, i64> = by_role_vec.into_iter().collect();
204
205 Ok(UserCountBreakdown {
206 total,
207 by_status,
208 by_role,
209 })
210 }
211
212 pub async fn get_stats(&self) -> Result<UserStats> {
213 self.repository.get_stats().await
214 }
215
216 pub async fn list_by_filter(
217 &self,
218 status: Option<&str>,
219 role: Option<&str>,
220 older_than_days: Option<i64>,
221 limit: i64,
222 ) -> Result<Vec<User>> {
223 self.repository
224 .list_by_filter(status, role, older_than_days, limit)
225 .await
226 }
227
228 pub async fn bulk_update_status(&self, user_ids: &[UserId], new_status: &str) -> Result<u64> {
229 self.repository
230 .bulk_update_status(user_ids, new_status)
231 .await
232 }
233
234 pub async fn bulk_delete(&self, user_ids: &[UserId]) -> Result<u64> {
235 self.repository.bulk_delete(user_ids).await
236 }
237
238 pub async fn merge_users(&self, source_id: &UserId, target_id: &UserId) -> Result<MergeResult> {
239 self.repository.merge_users(source_id, target_id).await
240 }
241}