systemprompt_users/services/user/
mod.rs1mod bulk;
22mod merge;
23mod provider;
24
25use std::fmt;
26use std::sync::Arc;
27use systemprompt_identifiers::{SessionId, UserId};
28use systemprompt_traits::DynOwnerReassignment;
29
30use crate::error::Result;
31use crate::models::{User, UserActivity, UserRole, UserSession, UserStatus, UserWithSessions};
32use crate::repository::{PurgeCount, UpdateUserParams, UserRepository};
33
34#[derive(Clone)]
35pub struct UserService {
36 pub(super) repository: Arc<UserRepository>,
37 pub(super) owner_reassignments: Arc<[DynOwnerReassignment]>,
38}
39
40impl fmt::Debug for UserService {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 let domains: Vec<&str> = self
43 .owner_reassignments
44 .iter()
45 .map(|reassignment| reassignment.domain())
46 .collect();
47 f.debug_struct("UserService")
48 .field("repository", &self.repository)
49 .field("owner_reassignments", &domains)
50 .finish()
51 }
52}
53
54impl UserService {
55 pub fn new(repository: Arc<UserRepository>) -> Self {
56 Self {
57 repository,
58 owner_reassignments: Arc::from([]),
59 }
60 }
61
62 #[must_use]
63 pub fn with_owner_reassignments(mut self, reassignments: Vec<DynOwnerReassignment>) -> Self {
64 self.owner_reassignments = Arc::from(reassignments);
65 self
66 }
67
68 pub async fn find_by_id(&self, id: &UserId) -> Result<Option<User>> {
69 self.repository.find_by_id(id).await
70 }
71
72 pub async fn find_by_email(&self, email: &str) -> Result<Option<User>> {
73 self.repository.find_by_email(email).await
74 }
75
76 pub async fn find_by_name(&self, name: &str) -> Result<Option<User>> {
77 self.repository.find_by_name(name).await
78 }
79
80 pub async fn list_by_role(&self, role: UserRole) -> Result<Vec<User>> {
81 self.repository.list_by_role(role).await
82 }
83
84 pub async fn find_first_user(&self) -> Result<Option<User>> {
85 self.repository.find_first_user().await
86 }
87
88 pub async fn find_first_admin(&self) -> Result<Option<User>> {
89 self.repository.find_first_admin().await
90 }
91
92 pub async fn find_authenticated_user(&self, user_id: &UserId) -> Result<Option<User>> {
93 self.repository.find_authenticated_user(user_id).await
94 }
95
96 pub async fn find_with_sessions(&self, user_id: &UserId) -> Result<Option<UserWithSessions>> {
97 self.repository.find_with_sessions(user_id).await
98 }
99
100 pub async fn get_activity(&self, user_id: &UserId) -> Result<UserActivity> {
101 self.repository.get_activity(user_id).await
102 }
103
104 pub async fn list(&self, limit: i64, offset: i64) -> Result<Vec<User>> {
105 self.repository.list(limit, offset).await
106 }
107
108 pub async fn list_including_anonymous(&self, limit: i64, offset: i64) -> Result<Vec<User>> {
109 self.repository
110 .list_including_anonymous(limit, offset)
111 .await
112 }
113
114 pub async fn list_all(&self) -> Result<Vec<User>> {
115 self.repository.list_all().await
116 }
117
118 pub async fn search(&self, query: &str, limit: i64) -> Result<Vec<User>> {
119 self.repository.search(query, limit).await
120 }
121
122 pub async fn search_including_anonymous(&self, query: &str, limit: i64) -> Result<Vec<User>> {
123 self.repository
124 .search_including_anonymous(query, limit)
125 .await
126 }
127
128 pub async fn count(&self) -> Result<i64> {
129 self.repository.count().await
130 }
131
132 pub async fn count_including_anonymous(&self) -> Result<i64> {
133 self.repository.count_including_anonymous().await
134 }
135
136 pub async fn is_temporary_anonymous(&self, id: &UserId) -> Result<bool> {
137 self.repository.is_temporary_anonymous(id).await
138 }
139
140 pub async fn list_non_anonymous_with_sessions(
141 &self,
142 limit: i64,
143 ) -> Result<Vec<UserWithSessions>> {
144 self.repository
145 .list_non_anonymous_with_sessions(limit)
146 .await
147 }
148
149 pub async fn list_sessions(&self, user_id: &UserId) -> Result<Vec<UserSession>> {
150 self.repository.list_sessions(user_id).await
151 }
152
153 pub async fn list_active_sessions(&self, user_id: &UserId) -> Result<Vec<UserSession>> {
154 self.repository.list_active_sessions(user_id).await
155 }
156
157 pub async fn list_recent_sessions(
158 &self,
159 user_id: &UserId,
160 limit: i64,
161 ) -> Result<Vec<UserSession>> {
162 self.repository.list_recent_sessions(user_id, limit).await
163 }
164
165 pub async fn session_exists(&self, session_id: &SessionId) -> Result<bool> {
166 self.repository.session_exists(session_id).await
167 }
168
169 pub async fn end_session(&self, session_id: &SessionId) -> Result<bool> {
170 self.repository.end_session(session_id).await
171 }
172
173 pub async fn end_all_sessions(&self, user_id: &UserId) -> Result<u64> {
174 self.repository.end_all_sessions(user_id).await
175 }
176
177 pub async fn create(
178 &self,
179 name: &str,
180 email: &str,
181 full_name: Option<&str>,
182 display_name: Option<&str>,
183 ) -> Result<User> {
184 self.repository
185 .create(name, email, full_name, display_name)
186 .await
187 }
188
189 pub async fn create_if_absent(
190 &self,
191 name: &str,
192 email: &str,
193 full_name: Option<&str>,
194 display_name: Option<&str>,
195 ) -> Result<Option<User>> {
196 self.repository
197 .create_if_absent(name, email, full_name, display_name)
198 .await
199 }
200
201 pub async fn create_anonymous(&self, fingerprint: &str) -> Result<User> {
202 self.repository.create_anonymous(fingerprint).await
203 }
204
205 pub async fn find_or_create_federated(
206 &self,
207 issuer: &str,
208 external_sub: &str,
209 claims: &systemprompt_traits::FederatedIdentityClaims,
210 ) -> Result<User> {
211 self.repository
212 .find_or_create_federated(issuer, external_sub, claims)
213 .await
214 }
215
216 pub async fn update_email(&self, id: &UserId, email: &str) -> Result<User> {
217 self.repository.update_email(id, email).await
218 }
219
220 pub async fn update_full_name(&self, id: &UserId, full_name: &str) -> Result<User> {
221 self.repository.update_full_name(id, full_name).await
222 }
223
224 pub async fn update_status(&self, id: &UserId, status: UserStatus) -> Result<User> {
225 self.repository.update_status(id, status).await
226 }
227
228 pub async fn update_email_verified(&self, id: &UserId, verified: bool) -> Result<User> {
229 self.repository.update_email_verified(id, verified).await
230 }
231
232 pub async fn update_display_name(&self, id: &UserId, display_name: &str) -> Result<User> {
233 self.repository.update_display_name(id, display_name).await
234 }
235
236 pub async fn update_all_fields(
237 &self,
238 id: &UserId,
239 params: UpdateUserParams<'_>,
240 ) -> Result<User> {
241 self.repository.update_all_fields(id, params).await
242 }
243
244 pub async fn assign_roles(&self, id: &UserId, roles: &[String]) -> Result<User> {
245 self.repository.assign_roles(id, roles).await
246 }
247
248 pub async fn delete(&self, id: &UserId) -> Result<Vec<PurgeCount>> {
249 self.repository.delete(id).await
250 }
251
252 pub async fn purge_preview(&self, id: &UserId) -> Result<Vec<PurgeCount>> {
253 self.repository.purge_preview(id).await
254 }
255
256 pub async fn cleanup_old_anonymous(&self, days: i32) -> Result<u64> {
257 self.repository.cleanup_old_anonymous(days).await
258 }
259
260 pub async fn count_old_anonymous(&self, days: i32) -> Result<i64> {
261 self.repository.count_old_anonymous(days).await
262 }
263}