better_auth/adapters/
database.rs

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/// Database adapter trait for persistence
11#[async_trait]
12pub trait DatabaseAdapter: Send + Sync {
13    // User operations
14    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    // Session operations
21    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    // Account operations (for OAuth)
30    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    // Verification token operations
36    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
43/// In-memory database adapter for testing and development
44pub 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>>>, // email -> user_id
50}
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        // Check if email already exists
79        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        // Update email index if email changed
136        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        /// Create adapter with custom pool configuration
386        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        /// Test database connection
403        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        /// Get connection pool statistics
411        pub fn pool_stats(&self) -> PoolStats {
412            PoolStats {
413                size: self.pool.size(),
414                idle: self.pool.num_idle(),
415            }
416        }
417        
418        /// Close the connection pool
419        pub async fn close(&self) {
420            self.pool.close().await;
421        }
422    }
423    
424    /// Database connection pool configuration
425    #[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)), // 10 minutes
441                max_lifetime: Some(std::time::Duration::from_secs(1800)), // 30 minutes
442            }
443        }
444    }
445    
446    /// Connection pool statistics
447    #[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                // If no updates, just return the current user
544                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;