1use crate::models::UserRow;
4use crate::{DatabasePool, Result};
5use chrono::Utc;
6use sqlx::Row;
7use uuid::Uuid;
8
9pub struct UserStore {
11 pool: DatabasePool,
12}
13
14impl UserStore {
15 pub fn new(pool: DatabasePool) -> Self {
17 Self { pool }
18 }
19
20 pub async fn create(
22 &self,
23 id: Uuid,
24 username: String,
25 email: String,
26 password_hash: String,
27 full_name: Option<String>,
28 ) -> Result<UserRow> {
29 let id_str = id.to_string();
30 let now = Utc::now().to_rfc3339();
31
32 sqlx::query(
33 r"
34 INSERT INTO users (id, username, email, password_hash, full_name, created_at, updated_at)
35 VALUES (?, ?, ?, ?, ?, ?, ?)
36 ",
37 )
38 .bind(&id_str)
39 .bind(&username)
40 .bind(&email)
41 .bind(&password_hash)
42 .bind(&full_name)
43 .bind(&now)
44 .bind(&now)
45 .execute(self.pool.pool())
46 .await?;
47
48 Ok(UserRow {
49 id: id_str,
50 username,
51 email,
52 password_hash,
53 full_name,
54 created_at: now.clone(),
55 updated_at: now,
56 last_login: None,
57 is_active: true,
58 is_verified: false,
59 })
60 }
61
62 pub async fn get(&self, id: &Uuid) -> Result<Option<UserRow>> {
64 let row = sqlx::query(
65 r"
66 SELECT id, username, email, password_hash, full_name, created_at, updated_at, last_login, is_active, is_verified
67 FROM users
68 WHERE id = ?
69 ",
70 )
71 .bind(id.to_string())
72 .fetch_optional(self.pool.pool())
73 .await?;
74
75 match row {
76 Some(row) => Ok(Some(UserRow {
77 id: row.get("id"),
78 username: row.get("username"),
79 email: row.get("email"),
80 password_hash: row.get("password_hash"),
81 full_name: row.get("full_name"),
82 created_at: row.get("created_at"),
83 updated_at: row.get("updated_at"),
84 last_login: row.get("last_login"),
85 is_active: row.get("is_active"),
86 is_verified: row.get("is_verified"),
87 })),
88 None => Ok(None),
89 }
90 }
91
92 pub async fn get_by_email(&self, email: &str) -> Result<Option<UserRow>> {
94 let row = sqlx::query(
95 r"
96 SELECT id, username, email, password_hash, full_name, created_at, updated_at, last_login, is_active, is_verified
97 FROM users
98 WHERE email = ?
99 ",
100 )
101 .bind(email)
102 .fetch_optional(self.pool.pool())
103 .await?;
104
105 match row {
106 Some(row) => Ok(Some(UserRow {
107 id: row.get("id"),
108 username: row.get("username"),
109 email: row.get("email"),
110 password_hash: row.get("password_hash"),
111 full_name: row.get("full_name"),
112 created_at: row.get("created_at"),
113 updated_at: row.get("updated_at"),
114 last_login: row.get("last_login"),
115 is_active: row.get("is_active"),
116 is_verified: row.get("is_verified"),
117 })),
118 None => Ok(None),
119 }
120 }
121
122 pub async fn get_by_username(&self, username: &str) -> Result<Option<UserRow>> {
124 let row = sqlx::query(
125 r"
126 SELECT id, username, email, password_hash, full_name, created_at, updated_at, last_login, is_active, is_verified
127 FROM users
128 WHERE username = ?
129 ",
130 )
131 .bind(username)
132 .fetch_optional(self.pool.pool())
133 .await?;
134
135 match row {
136 Some(row) => Ok(Some(UserRow {
137 id: row.get("id"),
138 username: row.get("username"),
139 email: row.get("email"),
140 password_hash: row.get("password_hash"),
141 full_name: row.get("full_name"),
142 created_at: row.get("created_at"),
143 updated_at: row.get("updated_at"),
144 last_login: row.get("last_login"),
145 is_active: row.get("is_active"),
146 is_verified: row.get("is_verified"),
147 })),
148 None => Ok(None),
149 }
150 }
151
152 pub async fn update_last_login(&self, id: &Uuid) -> Result<()> {
154 let now = Utc::now().to_rfc3339();
155 sqlx::query(
156 r"
157 UPDATE users
158 SET last_login = ?
159 WHERE id = ?
160 ",
161 )
162 .bind(&now)
163 .bind(id.to_string())
164 .execute(self.pool.pool())
165 .await?;
166
167 Ok(())
168 }
169
170 #[allow(dead_code)]
172 pub async fn update_full_name(&self, id: &Uuid, full_name: Option<String>) -> Result<()> {
173 sqlx::query(
174 r"
175 UPDATE users
176 SET full_name = ?
177 WHERE id = ?
178 ",
179 )
180 .bind(full_name)
181 .bind(id.to_string())
182 .execute(self.pool.pool())
183 .await?;
184
185 Ok(())
186 }
187
188 #[allow(dead_code)]
190 pub async fn delete(&self, id: &Uuid) -> Result<()> {
191 sqlx::query(
192 r"
193 DELETE FROM users
194 WHERE id = ?
195 ",
196 )
197 .bind(id.to_string())
198 .execute(self.pool.pool())
199 .await?;
200
201 Ok(())
202 }
203
204 #[allow(dead_code)]
206 pub async fn list(&self) -> Result<Vec<UserRow>> {
207 let rows = sqlx::query(
208 r"
209 SELECT id, username, email, password_hash, full_name, created_at, updated_at, last_login, is_active, is_verified
210 FROM users
211 ORDER BY created_at DESC
212 ",
213 )
214 .fetch_all(self.pool.pool())
215 .await?;
216
217 let users = rows
218 .into_iter()
219 .map(|row| UserRow {
220 id: row.get("id"),
221 username: row.get("username"),
222 email: row.get("email"),
223 password_hash: row.get("password_hash"),
224 full_name: row.get("full_name"),
225 created_at: row.get("created_at"),
226 updated_at: row.get("updated_at"),
227 last_login: row.get("last_login"),
228 is_active: row.get("is_active"),
229 is_verified: row.get("is_verified"),
230 })
231 .collect();
232
233 Ok(users)
234 }
235
236 pub async fn get_roles(&self, user_id: &str) -> Result<Vec<String>> {
238 let roles = sqlx::query(
239 r"
240 SELECT role
241 FROM user_roles
242 WHERE user_id = ?
243 ORDER BY granted_at
244 ",
245 )
246 .bind(user_id)
247 .fetch_all(self.pool.pool())
248 .await?;
249
250 Ok(roles.into_iter().map(|row| row.get("role")).collect())
251 }
252
253 pub async fn add_role(&self, user_id: &Uuid, role: String) -> Result<()> {
255 sqlx::query(
256 r"
257 INSERT OR IGNORE INTO user_roles (user_id, role)
258 VALUES (?, ?)
259 ",
260 )
261 .bind(user_id.to_string())
262 .bind(role)
263 .execute(self.pool.pool())
264 .await?;
265
266 Ok(())
267 }
268
269 #[allow(dead_code)]
271 pub async fn remove_role(&self, user_id: &Uuid, role: &str) -> Result<()> {
272 sqlx::query(
273 r"
274 DELETE FROM user_roles
275 WHERE user_id = ? AND role = ?
276 ",
277 )
278 .bind(user_id.to_string())
279 .bind(role)
280 .execute(self.pool.pool())
281 .await?;
282
283 Ok(())
284 }
285
286 pub async fn get_permissions(&self, user_id: &str) -> Result<Vec<String>> {
288 let permissions = sqlx::query(
289 r"
290 SELECT permission
291 FROM user_permissions
292 WHERE user_id = ?
293 ORDER BY granted_at
294 ",
295 )
296 .bind(user_id)
297 .fetch_all(self.pool.pool())
298 .await?;
299
300 Ok(permissions
301 .into_iter()
302 .map(|row| row.get("permission"))
303 .collect())
304 }
305
306 pub async fn add_permission(&self, user_id: &Uuid, permission: String) -> Result<()> {
308 sqlx::query(
309 r"
310 INSERT OR IGNORE INTO user_permissions (user_id, permission)
311 VALUES (?, ?)
312 ",
313 )
314 .bind(user_id.to_string())
315 .bind(permission)
316 .execute(self.pool.pool())
317 .await?;
318
319 Ok(())
320 }
321
322 #[allow(dead_code)]
324 pub async fn remove_permission(&self, user_id: &Uuid, permission: &str) -> Result<()> {
325 sqlx::query(
326 r"
327 DELETE FROM user_permissions
328 WHERE user_id = ? AND permission = ?
329 ",
330 )
331 .bind(user_id.to_string())
332 .bind(permission)
333 .execute(self.pool.pool())
334 .await?;
335
336 Ok(())
337 }
338
339 pub async fn exists_by_email(&self, email: &str) -> Result<bool> {
341 let row = sqlx::query(
342 r"
343 SELECT COUNT(*) as count
344 FROM users
345 WHERE email = ?
346 ",
347 )
348 .bind(email)
349 .fetch_one(self.pool.pool())
350 .await?;
351
352 let count: i64 = row.get("count");
353 Ok(count > 0)
354 }
355}