Skip to main content

rs_auth_postgres/
user.rs

1use async_trait::async_trait;
2use sqlx::Row;
3
4use rs_auth_core::error::AuthError;
5use rs_auth_core::store::UserStore;
6use rs_auth_core::types::User;
7
8use crate::db::AuthDb;
9
10#[async_trait]
11impl UserStore for AuthDb {
12    async fn create_user(
13        &self,
14        email: &str,
15        name: Option<&str>,
16        password_hash: Option<&str>,
17    ) -> Result<User, AuthError> {
18        sqlx::query(
19            r#"
20            INSERT INTO users (email, name, password_hash)
21            VALUES (LOWER($1), $2, $3)
22            RETURNING id, email, name, password_hash, email_verified_at, image, created_at, updated_at
23            "#,
24        )
25        .bind(email)
26        .bind(name)
27        .bind(password_hash)
28        .fetch_one(&self.pool)
29        .await
30        .map(|row| User {
31            id: row.get("id"),
32            email: row.get("email"),
33            name: row.get("name"),
34            password_hash: row.get("password_hash"),
35            email_verified_at: row.get("email_verified_at"),
36            image: row.get("image"),
37            created_at: row.get("created_at"),
38            updated_at: row.get("updated_at"),
39        })
40        .map_err(|error| match error {
41            sqlx::Error::Database(database_error) if database_error.is_unique_violation() => {
42                AuthError::EmailTaken
43            }
44            other => AuthError::Store(other.to_string()),
45        })
46    }
47
48    async fn find_by_email(&self, email: &str) -> Result<Option<User>, AuthError> {
49        sqlx::query(
50            r#"
51            SELECT id, email, name, password_hash, email_verified_at, image, created_at, updated_at
52            FROM users
53            WHERE LOWER(email) = LOWER($1)
54            "#,
55        )
56        .bind(email)
57        .fetch_optional(&self.pool)
58        .await
59        .map(|row| {
60            row.map(|row| User {
61                id: row.get("id"),
62                email: row.get("email"),
63                name: row.get("name"),
64                password_hash: row.get("password_hash"),
65                email_verified_at: row.get("email_verified_at"),
66                image: row.get("image"),
67                created_at: row.get("created_at"),
68                updated_at: row.get("updated_at"),
69            })
70        })
71        .map_err(|error| AuthError::Store(error.to_string()))
72    }
73
74    async fn find_by_id(&self, id: i64) -> Result<Option<User>, AuthError> {
75        sqlx::query(
76            r#"
77            SELECT id, email, name, password_hash, email_verified_at, image, created_at, updated_at
78            FROM users
79            WHERE id = $1
80            "#,
81        )
82        .bind(id)
83        .fetch_optional(&self.pool)
84        .await
85        .map(|row| {
86            row.map(|row| User {
87                id: row.get("id"),
88                email: row.get("email"),
89                name: row.get("name"),
90                password_hash: row.get("password_hash"),
91                email_verified_at: row.get("email_verified_at"),
92                image: row.get("image"),
93                created_at: row.get("created_at"),
94                updated_at: row.get("updated_at"),
95            })
96        })
97        .map_err(|error| AuthError::Store(error.to_string()))
98    }
99
100    async fn set_email_verified(&self, user_id: i64) -> Result<(), AuthError> {
101        sqlx::query(
102            r#"UPDATE users SET email_verified_at = now(), updated_at = now() WHERE id = $1"#,
103        )
104        .bind(user_id)
105        .execute(&self.pool)
106        .await
107        .map(|_| ())
108        .map_err(|error| AuthError::Store(error.to_string()))
109    }
110
111    async fn update_password(&self, user_id: i64, password_hash: &str) -> Result<(), AuthError> {
112        sqlx::query(r#"UPDATE users SET password_hash = $1, updated_at = now() WHERE id = $2"#)
113            .bind(password_hash)
114            .bind(user_id)
115            .execute(&self.pool)
116            .await
117            .map(|_| ())
118            .map_err(|error| AuthError::Store(error.to_string()))
119    }
120
121    async fn delete_user(&self, user_id: i64) -> Result<(), AuthError> {
122        sqlx::query(r#"DELETE FROM users WHERE id = $1"#)
123            .bind(user_id)
124            .execute(&self.pool)
125            .await
126            .map(|_| ())
127            .map_err(|error| AuthError::Store(error.to_string()))
128    }
129}