feat: move email tokens to valkey
No need to have them in permanent DB storage when they are temporary
This commit is contained in:
parent
4cbe551061
commit
b223dff4ba
5 changed files with 27 additions and 53 deletions
|
@ -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)
|
||||||
|
);
|
2
migrations/2025-06-03-103311_remove_email_tokens/up.sql
Normal file
2
migrations/2025-06-03-103311_remove_email_tokens/up.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
DROP TABLE email_tokens;
|
|
@ -46,20 +46,15 @@ pub async fn get(
|
||||||
|
|
||||||
let me = Me::get(&mut conn, uuid).await?;
|
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 {
|
if query.token != email_token.token {
|
||||||
return Ok(HttpResponse::Unauthorized().finish());
|
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?;
|
me.verify_email(&mut conn).await?;
|
||||||
|
|
||||||
email_token.delete(&mut conn).await?;
|
email_token.delete(&data).await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
@ -90,9 +85,9 @@ pub async fn post(req: HttpRequest, data: web::Data<Data>) -> Result<HttpRespons
|
||||||
return Ok(HttpResponse::NoContent().finish());
|
return Ok(HttpResponse::NoContent().finish());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(email_token) = EmailToken::get(&mut conn, me.uuid).await {
|
if let Ok(email_token) = EmailToken::get(&data, me.uuid).await {
|
||||||
if Utc::now().signed_duration_since(email_token.created_at) > Duration::hours(1) {
|
if Utc::now().signed_duration_since(email_token.created_at) > Duration::hours(1) {
|
||||||
email_token.delete(&mut conn).await?;
|
email_token.delete(&data).await?;
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::TooManyRequests(
|
return Err(Error::TooManyRequests(
|
||||||
"Please allow 1 hour before sending a new email".to_string(),
|
"Please allow 1 hour before sending a new email".to_string(),
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use diesel::{
|
|
||||||
ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper, delete, dsl::now,
|
|
||||||
insert_into,
|
|
||||||
};
|
|
||||||
use diesel_async::RunQueryDsl;
|
|
||||||
use lettre::message::MultiPart;
|
use lettre::message::MultiPart;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
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;
|
use super::Me;
|
||||||
|
|
||||||
#[derive(Selectable, Queryable)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[diesel(table_name = email_tokens)]
|
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
|
||||||
pub struct EmailToken {
|
pub struct EmailToken {
|
||||||
user_uuid: Uuid,
|
user_uuid: Uuid,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
|
@ -21,13 +15,8 @@ pub struct EmailToken {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmailToken {
|
impl EmailToken {
|
||||||
pub async fn get(conn: &mut Conn, user_uuid: Uuid) -> Result<EmailToken, Error> {
|
pub async fn get(data: &Data, user_uuid: Uuid) -> Result<EmailToken, Error> {
|
||||||
use email_tokens::dsl;
|
let email_token = serde_json::from_str(&data.get_cache_key(format!("{}_email_verify", user_uuid)).await?)?;
|
||||||
let email_token = dsl::email_tokens
|
|
||||||
.filter(dsl::user_uuid.eq(user_uuid))
|
|
||||||
.select(EmailToken::as_select())
|
|
||||||
.get_result(conn)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(email_token)
|
Ok(email_token)
|
||||||
}
|
}
|
||||||
|
@ -36,17 +25,14 @@ impl EmailToken {
|
||||||
pub async fn new(data: &Data, me: Me) -> Result<(), Error> {
|
pub async fn new(data: &Data, me: Me) -> Result<(), Error> {
|
||||||
let token = generate_refresh_token()?;
|
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;
|
data.set_cache_key(format!("{}_email_verify", me.uuid), email_token, 86400).await?;
|
||||||
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?;
|
|
||||||
|
|
||||||
let mut verify_endpoint = data.config.web.frontend_url.join("verify-email")?;
|
let mut verify_endpoint = data.config.web.frontend_url.join("verify-email")?;
|
||||||
|
|
||||||
|
@ -67,13 +53,8 @@ impl EmailToken {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(&self, conn: &mut Conn) -> Result<(), Error> {
|
pub async fn delete(&self, data: &Data) -> Result<(), Error> {
|
||||||
use email_tokens::dsl;
|
data.del_cache_key(format!("{}_email_verify", self.user_uuid)).await?;
|
||||||
delete(email_tokens::table)
|
|
||||||
.filter(dsl::user_uuid.eq(self.user_uuid))
|
|
||||||
.filter(dsl::token.eq(&self.token))
|
|
||||||
.execute(conn)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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! {
|
diesel::table! {
|
||||||
guild_members (uuid) {
|
guild_members (uuid) {
|
||||||
uuid -> Uuid,
|
uuid -> Uuid,
|
||||||
|
@ -155,7 +146,6 @@ diesel::joinable!(access_tokens -> refresh_tokens (refresh_token));
|
||||||
diesel::joinable!(access_tokens -> users (uuid));
|
diesel::joinable!(access_tokens -> users (uuid));
|
||||||
diesel::joinable!(channel_permissions -> channels (channel_uuid));
|
diesel::joinable!(channel_permissions -> channels (channel_uuid));
|
||||||
diesel::joinable!(channels -> guilds (guild_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 -> guilds (guild_uuid));
|
||||||
diesel::joinable!(guild_members -> users (user_uuid));
|
diesel::joinable!(guild_members -> users (user_uuid));
|
||||||
diesel::joinable!(guilds -> users (owner_uuid));
|
diesel::joinable!(guilds -> users (owner_uuid));
|
||||||
|
@ -173,7 +163,6 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
access_tokens,
|
access_tokens,
|
||||||
channel_permissions,
|
channel_permissions,
|
||||||
channels,
|
channels,
|
||||||
email_tokens,
|
|
||||||
guild_members,
|
guild_members,
|
||||||
guilds,
|
guilds,
|
||||||
instance_permissions,
|
instance_permissions,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue