systemprompt_users/repository/user/
find.rs1use systemprompt_identifiers::UserId;
7
8use crate::error::Result;
9use crate::models::{User, UserRole, UserRow, UserStatus};
10use crate::repository::UserRepository;
11
12impl UserRepository {
13 pub async fn find_by_id(&self, id: &UserId) -> Result<Option<User>> {
14 let deleted_status = UserStatus::Deleted.as_str();
15 let row = sqlx::query_as!(
16 UserRow,
17 r#"
18 SELECT id, name, email, full_name, display_name, status, email_verified,
19 roles, avatar_url, is_bot, is_scanner, created_at, updated_at
20 FROM users
21 WHERE id = $1 AND status != $2
22 "#,
23 id.as_str(),
24 deleted_status
25 )
26 .fetch_optional(&*self.pool)
27 .await?
28 .map(User::try_from)
29 .transpose()?;
30
31 Ok(row)
32 }
33
34 pub async fn find_by_email(&self, email: &str) -> Result<Option<User>> {
35 let email = crate::models::normalise_email(email);
36 let deleted_status = UserStatus::Deleted.as_str();
37 let row = sqlx::query_as!(
38 UserRow,
39 r#"
40 SELECT id, name, email, full_name, display_name, status, email_verified,
41 roles, avatar_url, is_bot, is_scanner, created_at, updated_at
42 FROM users
43 WHERE email = $1 AND status != $2
44 "#,
45 email,
46 deleted_status
47 )
48 .fetch_optional(&*self.pool)
49 .await?
50 .map(User::try_from)
51 .transpose()?;
52
53 Ok(row)
54 }
55
56 pub async fn find_by_name(&self, name: &str) -> Result<Option<User>> {
57 let deleted_status = UserStatus::Deleted.as_str();
58 let row = sqlx::query_as!(
59 UserRow,
60 r#"
61 SELECT id, name, email, full_name, display_name, status, email_verified,
62 roles, avatar_url, is_bot, is_scanner, created_at, updated_at
63 FROM users
64 WHERE name = $1 AND status != $2
65 ORDER BY created_at ASC
66 LIMIT 1
67 "#,
68 name,
69 deleted_status
70 )
71 .fetch_optional(&*self.pool)
72 .await?
73 .map(User::try_from)
74 .transpose()?;
75
76 Ok(row)
77 }
78
79 pub async fn list_by_role(&self, role: UserRole) -> Result<Vec<User>> {
80 let deleted_status = UserStatus::Deleted.as_str();
81 let rows = sqlx::query_as!(
82 UserRow,
83 r#"
84 SELECT id, name, email, full_name, display_name, status, email_verified,
85 roles, avatar_url, is_bot, is_scanner, created_at, updated_at
86 FROM users
87 WHERE $1 = ANY(roles) AND status != $2
88 ORDER BY created_at DESC
89 "#,
90 role.as_str(),
91 deleted_status
92 )
93 .fetch_all(&*self.pool)
94 .await?
95 .into_iter()
96 .map(User::try_from)
97 .collect::<Result<Vec<_>>>()?;
98
99 Ok(rows)
100 }
101
102 pub async fn find_first_user(&self) -> Result<Option<User>> {
103 let deleted_status = UserStatus::Deleted.as_str();
104 let row = sqlx::query_as!(
105 UserRow,
106 r#"
107 SELECT id, name, email, full_name, display_name, status, email_verified,
108 roles, avatar_url, is_bot, is_scanner, created_at, updated_at
109 FROM users
110 WHERE status != $1
111 ORDER BY created_at ASC
112 LIMIT 1
113 "#,
114 deleted_status
115 )
116 .fetch_optional(&*self.pool)
117 .await?
118 .map(User::try_from)
119 .transpose()?;
120
121 Ok(row)
122 }
123
124 pub async fn find_first_admin(&self) -> Result<Option<User>> {
125 let deleted_status = UserStatus::Deleted.as_str();
126 let admin_role = UserRole::Admin.as_str();
127 let row = sqlx::query_as!(
128 UserRow,
129 r#"
130 SELECT id, name, email, full_name, display_name, status, email_verified,
131 roles, avatar_url, is_bot, is_scanner, created_at, updated_at
132 FROM users
133 WHERE $1 = ANY(roles) AND status != $2
134 ORDER BY created_at ASC
135 LIMIT 1
136 "#,
137 admin_role,
138 deleted_status
139 )
140 .fetch_optional(&*self.pool)
141 .await?
142 .map(User::try_from)
143 .transpose()?;
144
145 Ok(row)
146 }
147
148 pub async fn find_authenticated_user(&self, user_id: &UserId) -> Result<Option<User>> {
149 let active_status = UserStatus::Active.as_str();
150 let row = sqlx::query_as!(
151 UserRow,
152 r#"
153 SELECT id, name, email, full_name, display_name, status, email_verified,
154 roles, avatar_url, is_bot, is_scanner, created_at, updated_at
155 FROM users
156 WHERE id = $1 AND status = $2
157 "#,
158 user_id.as_str(),
159 active_status
160 )
161 .fetch_optional(&*self.pool)
162 .await?
163 .map(User::try_from)
164 .transpose()?;
165
166 Ok(row)
167 }
168}
169
170impl UserRepository {
171 pub async fn missing_ids(&self, candidates: &[UserId]) -> Result<Vec<UserId>> {
172 let ids: Vec<String> = candidates.iter().map(ToString::to_string).collect();
173 let rows = sqlx::query_scalar!(
174 r#"
175 SELECT candidate AS "candidate!"
176 FROM UNNEST($1::text[]) AS candidate
177 WHERE NOT EXISTS (SELECT 1 FROM users WHERE users.id = candidate)
178 "#,
179 &ids[..]
180 )
181 .fetch_all(&*self.pool)
182 .await?;
183 Ok(rows.into_iter().map(UserId::new).collect())
184 }
185}