headless_lms_models/
user_email_codes.rs1use crate::prelude::*;
2
3#[derive(sqlx::FromRow, Debug, Clone)]
4pub struct UserEmailCode {
5 pub id: Uuid,
6 pub user_id: Uuid,
7 pub code: String,
8 pub expires_at: DateTime<Utc>,
9 pub used_at: Option<DateTime<Utc>>,
10 pub created_at: DateTime<Utc>,
11 pub updated_at: DateTime<Utc>,
12 pub deleted_at: Option<DateTime<Utc>>,
13}
14
15pub async fn insert_user_email_code(
16 conn: &mut PgConnection,
17 user_id: Uuid,
18 code: String,
19) -> ModelResult<String> {
20 let mut tx = conn.begin().await?;
21
22 let _ = sqlx::query!(
24 r#"
25 UPDATE user_email_codes
26SET deleted_at = NOW()
27WHERE user_id = $1
28 AND deleted_at IS NULL
29 "#,
30 user_id
31 )
32 .execute(&mut *tx)
33 .await?;
34
35 let record = sqlx::query!(
37 r#"
38 INSERT INTO user_email_codes (code, user_id)
39VALUES ($1, $2)
40RETURNING code
41 "#,
42 code,
43 user_id
44 )
45 .fetch_one(&mut *tx)
46 .await?;
47
48 tx.commit().await?;
49
50 Ok(record.code)
51}
52
53pub async fn get_unused_user_email_code_with_user_id(
54 conn: &mut PgConnection,
55 user_id: Uuid,
56) -> ModelResult<Option<UserEmailCode>> {
57 let now = Utc::now();
58 let record = sqlx::query_as!(
59 UserEmailCode,
60 r#"
61SELECT id,
62 code,
63 user_id,
64 created_at,
65 updated_at,
66 used_at,
67 deleted_at,
68 expires_at
69FROM user_email_codes
70WHERE user_id = $1
71 AND deleted_at IS NULL
72 AND used_at IS NULL
73 AND expires_at > $2
74 "#,
75 user_id,
76 now
77 )
78 .fetch_optional(conn)
79 .await?;
80
81 Ok(record)
82}
83
84pub async fn is_reset_user_email_code_valid(
85 conn: &mut PgConnection,
86 user_id: Uuid,
87 code: &String,
88) -> ModelResult<bool> {
89 let now = Utc::now();
90 let record = sqlx::query!(
91 r#"
92SELECT *
93FROM user_email_codes
94WHERE user_id = $1
95 AND code = $2
96 AND deleted_at IS NULL
97 AND used_at IS NULL
98 AND expires_at > $3
99 "#,
100 user_id,
101 code,
102 now
103 )
104 .fetch_optional(conn)
105 .await?;
106
107 Ok(record.is_some())
108}
109
110pub async fn mark_user_email_code_used(
111 conn: &mut PgConnection,
112 user_id: Uuid,
113 code: &String,
114) -> ModelResult<bool> {
115 let result = sqlx::query!(
116 r#"
117UPDATE user_email_codes
118SET used_at = NOW(),
119 deleted_at = NOW()
120WHERE user_id = $1
121 AND code = $2
122 AND deleted_at IS NULL
123 "#,
124 user_id,
125 code
126 )
127 .execute(conn)
128 .await?;
129
130 Ok(result.rows_affected() > 0)
131}