use anyhow::Result; use chrono::{NaiveDateTime, TimeDelta, Utc}; use rand::{distributions::Alphanumeric, rngs::OsRng, Rng as _}; use sqlx::{query_as, PgPool}; #[derive(Debug)] pub struct PasswordReset { pub id: i32, pub token: String, pub userid: i32, pub expires: NaiveDateTime } impl PasswordReset { pub async fn insert_new_for_user(pool: &PgPool, user_id: i32) -> Result{ let value = std::iter::repeat(()) .map(|()| OsRng.sample(Alphanumeric)) .take(64) .collect::>(); let token = String::from_utf8(value).unwrap().try_into().unwrap(); let expires = Utc::now().naive_utc() + TimeDelta::hours(24); let inserted = query_as!( PasswordReset, "INSERT INTO passwordReset (token, userId, expires) VALUES ($1, $2, $3) RETURNING *;", token, user_id, expires ) .fetch_one(pool) .await?; Ok(inserted) } pub async fn does_token_exist(pool: &PgPool, token: &str) -> Result { let result = query_as!(PasswordReset, "SELECT * FROM passwordReset WHERE token = $1 AND expires > NOW();", token ) .fetch_one(pool) .await?; Ok(result) } pub async fn delete(pool: &PgPool, token: &str) -> anyhow::Result<()> { sqlx::query!("DELETE FROM passwordReset WHERE token = $1;", token) .execute(pool) .await?; Ok(()) } }