From b223dff4ba6734c692236cdd3d029590859704c7 Mon Sep 17 00:00:00 2001 From: Radical Date: Tue, 3 Jun 2025 11:01:33 +0000 Subject: [PATCH] feat: move email tokens to valkey No need to have them in permanent DB storage when they are temporary --- .../down.sql | 7 +++ .../up.sql | 2 + src/api/v1/auth/verify_email.rs | 13 ++--- src/objects/email_token.rs | 47 ++++++------------- src/schema.rs | 11 ----- 5 files changed, 27 insertions(+), 53 deletions(-) create mode 100644 migrations/2025-06-03-103311_remove_email_tokens/down.sql create mode 100644 migrations/2025-06-03-103311_remove_email_tokens/up.sql diff --git a/migrations/2025-06-03-103311_remove_email_tokens/down.sql b/migrations/2025-06-03-103311_remove_email_tokens/down.sql new file mode 100644 index 0000000..e8f0350 --- /dev/null +++ b/migrations/2025-06-03-103311_remove_email_tokens/down.sql @@ -0,0 +1,7 @@ +-- This file should undo anything in `up.sql` +CREATE TABLE email_tokens ( + token VARCHAR(64) NOT NULL, + user_uuid uuid UNIQUE NOT NULL REFERENCES users(uuid), + created_at TIMESTAMPTZ NOT NULL, + PRIMARY KEY (token, user_uuid) +); diff --git a/migrations/2025-06-03-103311_remove_email_tokens/up.sql b/migrations/2025-06-03-103311_remove_email_tokens/up.sql new file mode 100644 index 0000000..b41afe5 --- /dev/null +++ b/migrations/2025-06-03-103311_remove_email_tokens/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +DROP TABLE email_tokens; diff --git a/src/api/v1/auth/verify_email.rs b/src/api/v1/auth/verify_email.rs index 0f23649..e596500 100644 --- a/src/api/v1/auth/verify_email.rs +++ b/src/api/v1/auth/verify_email.rs @@ -46,20 +46,15 @@ pub async fn get( let me = Me::get(&mut conn, uuid).await?; - let email_token = EmailToken::get(&mut conn, me.uuid).await?; + let email_token = EmailToken::get(&data, me.uuid).await?; if query.token != email_token.token { return Ok(HttpResponse::Unauthorized().finish()); } - if Utc::now().signed_duration_since(email_token.created_at) > Duration::hours(24) { - email_token.delete(&mut conn).await?; - return Ok(HttpResponse::Gone().finish()); - } - me.verify_email(&mut conn).await?; - email_token.delete(&mut conn).await?; + email_token.delete(&data).await?; Ok(HttpResponse::Ok().finish()) } @@ -90,9 +85,9 @@ pub async fn post(req: HttpRequest, data: web::Data) -> Result Duration::hours(1) { - email_token.delete(&mut conn).await?; + email_token.delete(&data).await?; } else { return Err(Error::TooManyRequests( "Please allow 1 hour before sending a new email".to_string(), diff --git a/src/objects/email_token.rs b/src/objects/email_token.rs index e458cf7..f55de8c 100644 --- a/src/objects/email_token.rs +++ b/src/objects/email_token.rs @@ -1,19 +1,13 @@ use chrono::Utc; -use diesel::{ - ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper, delete, dsl::now, - insert_into, -}; -use diesel_async::RunQueryDsl; use lettre::message::MultiPart; +use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::{Conn, Data, error::Error, schema::email_tokens, utils::generate_refresh_token}; +use crate::{Data, error::Error, utils::generate_refresh_token}; use super::Me; -#[derive(Selectable, Queryable)] -#[diesel(table_name = email_tokens)] -#[diesel(check_for_backend(diesel::pg::Pg))] +#[derive(Serialize, Deserialize)] pub struct EmailToken { user_uuid: Uuid, pub token: String, @@ -21,13 +15,8 @@ pub struct EmailToken { } impl EmailToken { - pub async fn get(conn: &mut Conn, user_uuid: Uuid) -> Result { - use email_tokens::dsl; - let email_token = dsl::email_tokens - .filter(dsl::user_uuid.eq(user_uuid)) - .select(EmailToken::as_select()) - .get_result(conn) - .await?; + pub async fn get(data: &Data, user_uuid: Uuid) -> Result { + let email_token = serde_json::from_str(&data.get_cache_key(format!("{}_email_verify", user_uuid)).await?)?; Ok(email_token) } @@ -36,17 +25,14 @@ impl EmailToken { pub async fn new(data: &Data, me: Me) -> Result<(), Error> { let token = generate_refresh_token()?; - let mut conn = data.pool.get().await?; + let email_token = EmailToken { + user_uuid: me.uuid, + token: token.clone(), + // TODO: Check if this can be replaced with something built into valkey + created_at: Utc::now() + }; - use email_tokens::dsl; - insert_into(email_tokens::table) - .values(( - dsl::user_uuid.eq(me.uuid), - dsl::token.eq(&token), - dsl::created_at.eq(now), - )) - .execute(&mut conn) - .await?; + data.set_cache_key(format!("{}_email_verify", me.uuid), email_token, 86400).await?; let mut verify_endpoint = data.config.web.frontend_url.join("verify-email")?; @@ -67,13 +53,8 @@ impl EmailToken { Ok(()) } - pub async fn delete(&self, conn: &mut Conn) -> Result<(), Error> { - use email_tokens::dsl; - delete(email_tokens::table) - .filter(dsl::user_uuid.eq(self.user_uuid)) - .filter(dsl::token.eq(&self.token)) - .execute(conn) - .await?; + pub async fn delete(&self, data: &Data) -> Result<(), Error> { + data.del_cache_key(format!("{}_email_verify", self.user_uuid)).await?; Ok(()) } diff --git a/src/schema.rs b/src/schema.rs index 09ea7a3..09fa08a 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -31,15 +31,6 @@ diesel::table! { } } -diesel::table! { - email_tokens (token, user_uuid) { - #[max_length = 64] - token -> Varchar, - user_uuid -> Uuid, - created_at -> Timestamptz, - } -} - diesel::table! { guild_members (uuid) { uuid -> Uuid, @@ -155,7 +146,6 @@ diesel::joinable!(access_tokens -> refresh_tokens (refresh_token)); diesel::joinable!(access_tokens -> users (uuid)); diesel::joinable!(channel_permissions -> channels (channel_uuid)); diesel::joinable!(channels -> guilds (guild_uuid)); -diesel::joinable!(email_tokens -> users (user_uuid)); diesel::joinable!(guild_members -> guilds (guild_uuid)); diesel::joinable!(guild_members -> users (user_uuid)); diesel::joinable!(guilds -> users (owner_uuid)); @@ -173,7 +163,6 @@ diesel::allow_tables_to_appear_in_same_query!( access_tokens, channel_permissions, channels, - email_tokens, guild_members, guilds, instance_permissions,