1use async_trait::async_trait;
2use std::collections::HashMap;
3use std::sync::{Arc, Mutex};
4use chrono::{DateTime, Utc};
5use uuid::Uuid;
6
7use crate::types::{User, Session, Account, Verification, CreateUser, UpdateUser, CreateSession, CreateAccount, CreateVerification};
8use crate::error::{AuthResult, AuthError};
9
10#[async_trait]
12pub trait DatabaseAdapter: Send + Sync {
13 async fn create_user(&self, user: CreateUser) -> AuthResult<User>;
15 async fn get_user_by_id(&self, id: &str) -> AuthResult<Option<User>>;
16 async fn get_user_by_email(&self, email: &str) -> AuthResult<Option<User>>;
17 async fn update_user(&self, id: &str, update: UpdateUser) -> AuthResult<User>;
18 async fn delete_user(&self, id: &str) -> AuthResult<()>;
19
20 async fn create_session(&self, session: CreateSession) -> AuthResult<Session>;
22 async fn get_session(&self, token: &str) -> AuthResult<Option<Session>>;
23 async fn get_user_sessions(&self, user_id: &str) -> AuthResult<Vec<Session>>;
24 async fn update_session_expiry(&self, token: &str, expires_at: DateTime<Utc>) -> AuthResult<()>;
25 async fn delete_session(&self, token: &str) -> AuthResult<()>;
26 async fn delete_user_sessions(&self, user_id: &str) -> AuthResult<()>;
27 async fn delete_expired_sessions(&self) -> AuthResult<usize>;
28
29 async fn create_account(&self, account: CreateAccount) -> AuthResult<Account>;
31 async fn get_account(&self, provider: &str, provider_account_id: &str) -> AuthResult<Option<Account>>;
32 async fn get_user_accounts(&self, user_id: &str) -> AuthResult<Vec<Account>>;
33 async fn delete_account(&self, id: &str) -> AuthResult<()>;
34
35 async fn create_verification(&self, verification: CreateVerification) -> AuthResult<Verification>;
37 async fn get_verification(&self, identifier: &str, value: &str) -> AuthResult<Option<Verification>>;
38 async fn get_verification_by_value(&self, value: &str) -> AuthResult<Option<Verification>>;
39 async fn delete_verification(&self, id: &str) -> AuthResult<()>;
40 async fn delete_expired_verifications(&self) -> AuthResult<usize>;
41}
42
43pub struct MemoryDatabaseAdapter {
45 users: Arc<Mutex<HashMap<String, User>>>,
46 sessions: Arc<Mutex<HashMap<String, Session>>>,
47 accounts: Arc<Mutex<HashMap<String, Account>>>,
48 verifications: Arc<Mutex<HashMap<String, Verification>>>,
49 email_index: Arc<Mutex<HashMap<String, String>>>, }
51
52impl MemoryDatabaseAdapter {
53 pub fn new() -> Self {
54 Self {
55 users: Arc::new(Mutex::new(HashMap::new())),
56 sessions: Arc::new(Mutex::new(HashMap::new())),
57 accounts: Arc::new(Mutex::new(HashMap::new())),
58 verifications: Arc::new(Mutex::new(HashMap::new())),
59 email_index: Arc::new(Mutex::new(HashMap::new())),
60 }
61 }
62}
63
64impl Default for MemoryDatabaseAdapter {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70#[async_trait]
71impl DatabaseAdapter for MemoryDatabaseAdapter {
72 async fn create_user(&self, create_user: CreateUser) -> AuthResult<User> {
73 let mut users = self.users.lock().unwrap();
74 let mut email_index = self.email_index.lock().unwrap();
75
76 let id = create_user.id.unwrap_or_else(|| Uuid::new_v4().to_string());
77
78 if let Some(email) = &create_user.email {
80 if email_index.contains_key(email) {
81 return Err(AuthError::config("Email already exists"));
82 }
83 }
84
85 let now = Utc::now();
86 let user = User {
87 id: id.clone(),
88 name: create_user.name,
89 email: create_user.email.clone(),
90 email_verified: create_user.email_verified.unwrap_or(false),
91 image: create_user.image,
92 created_at: now,
93 updated_at: now,
94 username: create_user.username,
95 display_username: create_user.display_username,
96 two_factor_enabled: false,
97 role: create_user.role,
98 banned: false,
99 ban_reason: None,
100 ban_expires: None,
101 metadata: create_user.metadata.unwrap_or_default(),
102 };
103
104 users.insert(id.clone(), user.clone());
105
106 if let Some(email) = &create_user.email {
107 email_index.insert(email.clone(), id);
108 }
109
110 Ok(user)
111 }
112
113 async fn get_user_by_id(&self, id: &str) -> AuthResult<Option<User>> {
114 let users = self.users.lock().unwrap();
115 Ok(users.get(id).cloned())
116 }
117
118 async fn get_user_by_email(&self, email: &str) -> AuthResult<Option<User>> {
119 let email_index = self.email_index.lock().unwrap();
120 let users = self.users.lock().unwrap();
121
122 if let Some(user_id) = email_index.get(email) {
123 Ok(users.get(user_id).cloned())
124 } else {
125 Ok(None)
126 }
127 }
128
129 async fn update_user(&self, id: &str, update: UpdateUser) -> AuthResult<User> {
130 let mut users = self.users.lock().unwrap();
131 let mut email_index = self.email_index.lock().unwrap();
132
133 let user = users.get_mut(id).ok_or(AuthError::UserNotFound)?;
134
135 if let Some(new_email) = &update.email {
137 if let Some(old_email) = &user.email {
138 email_index.remove(old_email);
139 }
140 email_index.insert(new_email.clone(), id.to_string());
141 user.email = Some(new_email.clone());
142 }
143
144 if let Some(name) = update.name {
145 user.name = Some(name);
146 }
147
148 if let Some(image) = update.image {
149 user.image = Some(image);
150 }
151
152 if let Some(email_verified) = update.email_verified {
153 user.email_verified = email_verified;
154 }
155
156 if let Some(username) = update.username {
157 user.username = Some(username);
158 }
159
160 if let Some(display_username) = update.display_username {
161 user.display_username = Some(display_username);
162 }
163
164 if let Some(role) = update.role {
165 user.role = Some(role);
166 }
167
168 if let Some(banned) = update.banned {
169 user.banned = banned;
170 }
171
172 if let Some(ban_reason) = update.ban_reason {
173 user.ban_reason = Some(ban_reason);
174 }
175
176 if let Some(ban_expires) = update.ban_expires {
177 user.ban_expires = Some(ban_expires);
178 }
179
180 if let Some(two_factor_enabled) = update.two_factor_enabled {
181 user.two_factor_enabled = two_factor_enabled;
182 }
183
184 if let Some(metadata) = update.metadata {
185 user.metadata = metadata;
186 }
187
188 user.updated_at = Utc::now();
189
190 Ok(user.clone())
191 }
192
193 async fn delete_user(&self, id: &str) -> AuthResult<()> {
194 let mut users = self.users.lock().unwrap();
195 let mut email_index = self.email_index.lock().unwrap();
196
197 if let Some(user) = users.remove(id) {
198 if let Some(email) = &user.email {
199 email_index.remove(email);
200 }
201 }
202
203 Ok(())
204 }
205
206 async fn create_session(&self, create_session: CreateSession) -> AuthResult<Session> {
207 let mut sessions = self.sessions.lock().unwrap();
208
209 let token = format!("session_{}", Uuid::new_v4());
210 let now = Utc::now();
211 let session = Session {
212 id: Uuid::new_v4().to_string(),
213 expires_at: create_session.expires_at,
214 token: token.clone(),
215 created_at: now,
216 updated_at: now,
217 ip_address: create_session.ip_address,
218 user_agent: create_session.user_agent,
219 user_id: create_session.user_id,
220 impersonated_by: create_session.impersonated_by,
221 active_organization_id: create_session.active_organization_id,
222 active: true,
223 };
224
225 sessions.insert(token, session.clone());
226 Ok(session)
227 }
228
229 async fn get_session(&self, token: &str) -> AuthResult<Option<Session>> {
230 let sessions = self.sessions.lock().unwrap();
231 Ok(sessions.get(token).cloned())
232 }
233
234 async fn get_user_sessions(&self, user_id: &str) -> AuthResult<Vec<Session>> {
235 let sessions = self.sessions.lock().unwrap();
236 Ok(sessions.values()
237 .filter(|session| session.user_id == user_id && session.active)
238 .cloned()
239 .collect())
240 }
241
242 async fn update_session_expiry(&self, token: &str, expires_at: DateTime<Utc>) -> AuthResult<()> {
243 let mut sessions = self.sessions.lock().unwrap();
244 if let Some(session) = sessions.get_mut(token) {
245 session.expires_at = expires_at;
246 }
247 Ok(())
248 }
249
250 async fn delete_session(&self, token: &str) -> AuthResult<()> {
251 let mut sessions = self.sessions.lock().unwrap();
252 sessions.remove(token);
253 Ok(())
254 }
255
256 async fn delete_user_sessions(&self, user_id: &str) -> AuthResult<()> {
257 let mut sessions = self.sessions.lock().unwrap();
258 sessions.retain(|_, session| session.user_id != user_id);
259 Ok(())
260 }
261
262 async fn delete_expired_sessions(&self) -> AuthResult<usize> {
263 let mut sessions = self.sessions.lock().unwrap();
264 let now = Utc::now();
265 let initial_count = sessions.len();
266
267 sessions.retain(|_, session| session.expires_at > now && session.active);
268
269 Ok(initial_count - sessions.len())
270 }
271
272 async fn create_account(&self, create_account: CreateAccount) -> AuthResult<Account> {
273 let mut accounts = self.accounts.lock().unwrap();
274
275 let now = Utc::now();
276 let account = Account {
277 id: Uuid::new_v4().to_string(),
278 account_id: create_account.account_id,
279 provider_id: create_account.provider_id,
280 user_id: create_account.user_id,
281 access_token: create_account.access_token,
282 refresh_token: create_account.refresh_token,
283 id_token: create_account.id_token,
284 access_token_expires_at: create_account.access_token_expires_at,
285 refresh_token_expires_at: create_account.refresh_token_expires_at,
286 scope: create_account.scope,
287 password: create_account.password,
288 created_at: now,
289 updated_at: now,
290 };
291
292 accounts.insert(account.id.clone(), account.clone());
293 Ok(account)
294 }
295
296 async fn get_account(&self, provider: &str, provider_account_id: &str) -> AuthResult<Option<Account>> {
297 let accounts = self.accounts.lock().unwrap();
298 Ok(accounts.values()
299 .find(|acc| acc.provider_id == provider && acc.account_id == provider_account_id)
300 .cloned())
301 }
302
303 async fn get_user_accounts(&self, user_id: &str) -> AuthResult<Vec<Account>> {
304 let accounts = self.accounts.lock().unwrap();
305 Ok(accounts.values()
306 .filter(|acc| acc.user_id == user_id)
307 .cloned()
308 .collect())
309 }
310
311 async fn delete_account(&self, id: &str) -> AuthResult<()> {
312 let mut accounts = self.accounts.lock().unwrap();
313 accounts.remove(id);
314 Ok(())
315 }
316
317 async fn create_verification(&self, create_verification: CreateVerification) -> AuthResult<Verification> {
318 let mut verifications = self.verifications.lock().unwrap();
319
320 let now = Utc::now();
321 let verification = Verification {
322 id: Uuid::new_v4().to_string(),
323 identifier: create_verification.identifier,
324 value: create_verification.value.clone(),
325 expires_at: create_verification.expires_at,
326 created_at: now,
327 updated_at: now,
328 };
329
330 verifications.insert(verification.id.clone(), verification.clone());
331 Ok(verification)
332 }
333
334 async fn get_verification(&self, identifier: &str, value: &str) -> AuthResult<Option<Verification>> {
335 let verifications = self.verifications.lock().unwrap();
336 let now = Utc::now();
337
338 Ok(verifications.values()
339 .find(|v| v.identifier == identifier && v.value == value && v.expires_at > now)
340 .cloned())
341 }
342
343 async fn get_verification_by_value(&self, value: &str) -> AuthResult<Option<Verification>> {
344 let verifications = self.verifications.lock().unwrap();
345 let now = Utc::now();
346
347 Ok(verifications.values()
348 .find(|v| v.value == value && v.expires_at > now)
349 .cloned())
350 }
351
352 async fn delete_verification(&self, id: &str) -> AuthResult<()> {
353 let mut verifications = self.verifications.lock().unwrap();
354 verifications.remove(id);
355 Ok(())
356 }
357
358 async fn delete_expired_verifications(&self) -> AuthResult<usize> {
359 let mut verifications = self.verifications.lock().unwrap();
360 let now = Utc::now();
361 let initial_count = verifications.len();
362
363 verifications.retain(|_, verification| verification.expires_at > now);
364
365 Ok(initial_count - verifications.len())
366 }
367}
368
369
370#[cfg(feature = "sqlx-postgres")]
371pub mod sqlx_adapter {
372 use super::*;
373 use sqlx::PgPool;
374
375 pub struct SqlxAdapter {
376 pool: PgPool,
377 }
378
379 impl SqlxAdapter {
380 pub async fn new(database_url: &str) -> Result<Self, sqlx::Error> {
381 let pool = PgPool::connect(database_url).await?;
382 Ok(Self { pool })
383 }
384
385 pub async fn with_config(database_url: &str, config: PoolConfig) -> Result<Self, sqlx::Error> {
387 let pool = sqlx::postgres::PgPoolOptions::new()
388 .max_connections(config.max_connections)
389 .min_connections(config.min_connections)
390 .acquire_timeout(config.acquire_timeout)
391 .idle_timeout(config.idle_timeout)
392 .max_lifetime(config.max_lifetime)
393 .connect(database_url)
394 .await?;
395 Ok(Self { pool })
396 }
397
398 pub fn from_pool(pool: PgPool) -> Self {
399 Self { pool }
400 }
401
402 pub async fn test_connection(&self) -> Result<(), sqlx::Error> {
404 sqlx::query("SELECT 1")
405 .execute(&self.pool)
406 .await?;
407 Ok(())
408 }
409
410 pub fn pool_stats(&self) -> PoolStats {
412 PoolStats {
413 size: self.pool.size(),
414 idle: self.pool.num_idle(),
415 }
416 }
417
418 pub async fn close(&self) {
420 self.pool.close().await;
421 }
422 }
423
424 #[derive(Debug, Clone)]
426 pub struct PoolConfig {
427 pub max_connections: u32,
428 pub min_connections: u32,
429 pub acquire_timeout: std::time::Duration,
430 pub idle_timeout: Option<std::time::Duration>,
431 pub max_lifetime: Option<std::time::Duration>,
432 }
433
434 impl Default for PoolConfig {
435 fn default() -> Self {
436 Self {
437 max_connections: 10,
438 min_connections: 0,
439 acquire_timeout: std::time::Duration::from_secs(30),
440 idle_timeout: Some(std::time::Duration::from_secs(600)), max_lifetime: Some(std::time::Duration::from_secs(1800)), }
443 }
444 }
445
446 #[derive(Debug, Clone)]
448 pub struct PoolStats {
449 pub size: u32,
450 pub idle: usize,
451 }
452
453 #[async_trait]
454 impl DatabaseAdapter for SqlxAdapter {
455 async fn create_user(&self, create_user: CreateUser) -> AuthResult<User> {
456 let id = create_user.id.unwrap_or_else(|| Uuid::new_v4().to_string());
457 let now = Utc::now();
458
459 let user = sqlx::query_as::<_, User>(
460 r#"
461 INSERT INTO users (id, email, name, image, email_verified, created_at, updated_at, metadata)
462 VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
463 RETURNING id, email, name, image, email_verified, created_at, updated_at, metadata
464 "#
465 )
466 .bind(&id)
467 .bind(&create_user.email)
468 .bind(&create_user.name)
469 .bind(&create_user.image)
470 .bind(false)
471 .bind(&now)
472 .bind(&now)
473 .bind(sqlx::types::Json(create_user.metadata.unwrap_or_default()))
474 .fetch_one(&self.pool)
475 .await?;
476
477 Ok(user)
478 }
479
480 async fn get_user_by_id(&self, id: &str) -> AuthResult<Option<User>> {
481 let user = sqlx::query_as::<_, User>(
482 r#"
483 SELECT id, email, name, image, email_verified, created_at, updated_at, metadata
484 FROM users WHERE id = $1
485 "#
486 )
487 .bind(id)
488 .fetch_optional(&self.pool)
489 .await?;
490
491 Ok(user)
492 }
493
494 async fn get_user_by_email(&self, email: &str) -> AuthResult<Option<User>> {
495 let user = sqlx::query_as::<_, User>(
496 r#"
497 SELECT id, email, name, image, email_verified, created_at, updated_at, metadata
498 FROM users WHERE email = $1
499 "#
500 )
501 .bind(email)
502 .fetch_optional(&self.pool)
503 .await?;
504
505 Ok(user)
506 }
507
508 async fn update_user(&self, id: &str, update: UpdateUser) -> AuthResult<User> {
509 let mut query = sqlx::QueryBuilder::new("UPDATE users SET updated_at = NOW()");
510 let mut has_updates = false;
511
512 if let Some(email) = &update.email {
513 query.push(", email = ");
514 query.push_bind(email);
515 has_updates = true;
516 }
517
518 if let Some(name) = &update.name {
519 query.push(", name = ");
520 query.push_bind(name);
521 has_updates = true;
522 }
523
524 if let Some(image) = &update.image {
525 query.push(", image = ");
526 query.push_bind(image);
527 has_updates = true;
528 }
529
530 if let Some(email_verified) = update.email_verified {
531 query.push(", email_verified = ");
532 query.push_bind(email_verified);
533 has_updates = true;
534 }
535
536 if let Some(metadata) = &update.metadata {
537 query.push(", metadata = ");
538 query.push_bind(sqlx::types::Json(metadata.clone()));
539 has_updates = true;
540 }
541
542 if !has_updates {
543 return self.get_user_by_id(id).await?.ok_or(AuthError::UserNotFound);
545 }
546
547 query.push(" WHERE id = ");
548 query.push_bind(id);
549 query.push(" RETURNING id, email, name, image, email_verified, created_at, updated_at, metadata");
550
551 let user = query
552 .build_query_as::<User>()
553 .fetch_one(&self.pool)
554 .await?;
555
556 Ok(user)
557 }
558
559 async fn delete_user(&self, id: &str) -> AuthResult<()> {
560 sqlx::query("DELETE FROM users WHERE id = $1")
561 .bind(id)
562 .execute(&self.pool)
563 .await?;
564
565 Ok(())
566 }
567
568 async fn create_session(&self, create_session: CreateSession) -> AuthResult<Session> {
569 let id = Uuid::new_v4().to_string();
570 let token = format!("session_{}", Uuid::new_v4());
571 let now = Utc::now();
572
573 let session = sqlx::query_as::<_, Session>(
574 r#"
575 INSERT INTO sessions (id, user_id, token, expires_at, created_at, ip_address, user_agent, active)
576 VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
577 RETURNING id, user_id, token, expires_at, created_at, ip_address, user_agent, active
578 "#
579 )
580 .bind(&id)
581 .bind(&create_session.user_id)
582 .bind(&token)
583 .bind(&create_session.expires_at)
584 .bind(&now)
585 .bind(&create_session.ip_address)
586 .bind(&create_session.user_agent)
587 .bind(true)
588 .fetch_one(&self.pool)
589 .await?;
590
591 Ok(session)
592 }
593
594 async fn get_session(&self, token: &str) -> AuthResult<Option<Session>> {
595 let session = sqlx::query_as::<_, Session>(
596 r#"
597 SELECT id, user_id, token, expires_at, created_at, updated_at, ip_address, user_agent, active, impersonated_by, active_organization_id
598 FROM sessions
599 WHERE token = $1 AND active = true
600 "#
601 )
602 .bind(token)
603 .fetch_optional(&self.pool)
604 .await?;
605
606 Ok(session)
607 }
608
609 async fn get_user_sessions(&self, user_id: &str) -> AuthResult<Vec<Session>> {
610 let sessions = sqlx::query_as::<_, Session>(
611 r#"
612 SELECT id, user_id, token, expires_at, created_at, updated_at, ip_address, user_agent, active, impersonated_by, active_organization_id
613 FROM sessions
614 WHERE user_id = $1 AND active = true
615 ORDER BY created_at DESC
616 "#
617 )
618 .bind(user_id)
619 .fetch_all(&self.pool)
620 .await?;
621
622 Ok(sessions)
623 }
624
625 async fn update_session_expiry(&self, token: &str, expires_at: DateTime<Utc>) -> AuthResult<()> {
626 sqlx::query(
627 r#"
628 UPDATE sessions
629 SET expires_at = $1
630 WHERE token = $2 AND active = true
631 "#
632 )
633 .bind(&expires_at)
634 .bind(token)
635 .execute(&self.pool)
636 .await?;
637
638 Ok(())
639 }
640
641 async fn delete_session(&self, token: &str) -> AuthResult<()> {
642 sqlx::query("DELETE FROM sessions WHERE token = $1")
643 .bind(token)
644 .execute(&self.pool)
645 .await?;
646
647 Ok(())
648 }
649
650 async fn delete_user_sessions(&self, user_id: &str) -> AuthResult<()> {
651 sqlx::query("DELETE FROM sessions WHERE user_id = $1")
652 .bind(user_id)
653 .execute(&self.pool)
654 .await?;
655
656 Ok(())
657 }
658
659 async fn delete_expired_sessions(&self) -> AuthResult<usize> {
660 let result = sqlx::query("DELETE FROM sessions WHERE expires_at < NOW() OR active = false")
661 .execute(&self.pool)
662 .await?;
663
664 Ok(result.rows_affected() as usize)
665 }
666
667 async fn create_account(&self, create_account: CreateAccount) -> AuthResult<Account> {
668 let id = Uuid::new_v4().to_string();
669 let now = Utc::now();
670
671 let account = sqlx::query_as::<_, Account>(
672 r#"
673 INSERT INTO accounts (id, account_id, provider_id, user_id, access_token, refresh_token, id_token, access_token_expires_at, refresh_token_expires_at, scope, password, created_at, updated_at)
674 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
675 RETURNING *
676 "#
677 )
678 .bind(&id)
679 .bind(&create_account.account_id)
680 .bind(&create_account.provider_id)
681 .bind(&create_account.user_id)
682 .bind(&create_account.access_token)
683 .bind(&create_account.refresh_token)
684 .bind(&create_account.id_token)
685 .bind(&create_account.access_token_expires_at)
686 .bind(&create_account.refresh_token_expires_at)
687 .bind(&create_account.scope)
688 .bind(&create_account.password)
689 .bind(&now)
690 .bind(&now)
691 .fetch_one(&self.pool)
692 .await?;
693
694 Ok(account)
695 }
696
697 async fn get_account(&self, provider: &str, provider_account_id: &str) -> AuthResult<Option<Account>> {
698 let account = sqlx::query_as::<_, Account>(
699 r#"
700 SELECT *
701 FROM accounts
702 WHERE provider_id = $1 AND account_id = $2
703 "#
704 )
705 .bind(provider)
706 .bind(provider_account_id)
707 .fetch_optional(&self.pool)
708 .await?;
709
710 Ok(account)
711 }
712
713 async fn get_user_accounts(&self, user_id: &str) -> AuthResult<Vec<Account>> {
714 let accounts = sqlx::query_as::<_, Account>(
715 r#"
716 SELECT id, user_id, provider, provider_account_id, access_token, refresh_token, expires_at, token_type, scope, created_at
717 FROM accounts
718 WHERE user_id = $1
719 ORDER BY created_at DESC
720 "#
721 )
722 .bind(user_id)
723 .fetch_all(&self.pool)
724 .await?;
725
726 Ok(accounts)
727 }
728
729 async fn delete_account(&self, id: &str) -> AuthResult<()> {
730 sqlx::query("DELETE FROM accounts WHERE id = $1")
731 .bind(id)
732 .execute(&self.pool)
733 .await?;
734
735 Ok(())
736 }
737
738 async fn create_verification(&self, create_verification: CreateVerification) -> AuthResult<Verification> {
739 let id = Uuid::new_v4().to_string();
740 let now = Utc::now();
741
742 let verification = sqlx::query_as::<_, Verification>(
743 r#"
744 INSERT INTO verifications (id, identifier, value, expires_at, created_at, updated_at)
745 VALUES ($1, $2, $3, $4, $5, $6)
746 RETURNING *
747 "#
748 )
749 .bind(&id)
750 .bind(&create_verification.identifier)
751 .bind(&create_verification.value)
752 .bind(&create_verification.expires_at)
753 .bind(&now)
754 .bind(&now)
755 .fetch_one(&self.pool)
756 .await?;
757
758 Ok(verification)
759 }
760
761 async fn get_verification(&self, identifier: &str, value: &str) -> AuthResult<Option<Verification>> {
762 let verification = sqlx::query_as::<_, Verification>(
763 r#"
764 SELECT *
765 FROM verifications
766 WHERE identifier = $1 AND value = $2 AND expires_at > NOW()
767 "#
768 )
769 .bind(identifier)
770 .bind(value)
771 .fetch_optional(&self.pool)
772 .await?;
773
774 Ok(verification)
775 }
776
777 async fn get_verification_by_value(&self, value: &str) -> AuthResult<Option<Verification>> {
778 let verification = sqlx::query_as::<_, Verification>(
779 r#"
780 SELECT *
781 FROM verifications
782 WHERE value = $1 AND expires_at > NOW()
783 "#
784 )
785 .bind(value)
786 .fetch_optional(&self.pool)
787 .await?;
788
789 Ok(verification)
790 }
791
792 async fn delete_verification(&self, id: &str) -> AuthResult<()> {
793 sqlx::query("DELETE FROM verifications WHERE id = $1")
794 .bind(id)
795 .execute(&self.pool)
796 .await?;
797
798 Ok(())
799 }
800
801 async fn delete_expired_verifications(&self) -> AuthResult<usize> {
802 let result = sqlx::query("DELETE FROM verifications WHERE expires_at < NOW()")
803 .execute(&self.pool)
804 .await?;
805
806 Ok(result.rows_affected() as usize)
807 }
808 }
809}
810
811#[cfg(feature = "sqlx-postgres")]
812pub use sqlx_adapter::SqlxAdapter;