feat: make database access more uniform and stop locking as many pool connections
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful

This commit is contained in:
Radical 2025-07-21 04:15:04 +02:00
parent f5d4211fad
commit fa52412b43
30 changed files with 516 additions and 373 deletions

View file

@ -10,10 +10,10 @@ use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
AppState,
AppState, Conn,
error::Error,
schema::users,
utils::{PASSWORD_REGEX, generate_token, global_checks, user_uuid_from_identifier},
utils::{CacheFns, PASSWORD_REGEX, generate_token, global_checks, user_uuid_from_identifier},
};
#[derive(Serialize, Deserialize)]
@ -24,50 +24,49 @@ pub struct PasswordResetToken {
}
impl PasswordResetToken {
pub async fn get(app_state: &AppState, token: String) -> Result<PasswordResetToken, Error> {
let user_uuid: Uuid =
serde_json::from_str(&app_state.get_cache_key(token.to_string()).await?)?;
let password_reset_token = serde_json::from_str(
&app_state
.get_cache_key(format!("{user_uuid}_password_reset"))
.await?,
)?;
pub async fn get(
cache_pool: &redis::Client,
token: String,
) -> Result<PasswordResetToken, Error> {
let user_uuid: Uuid = cache_pool.get_cache_key(token.to_string()).await?;
let password_reset_token = cache_pool
.get_cache_key(format!("{user_uuid}_password_reset"))
.await?;
Ok(password_reset_token)
}
pub async fn get_with_identifier(
app_state: &AppState,
conn: &mut Conn,
cache_pool: &redis::Client,
identifier: String,
) -> Result<PasswordResetToken, Error> {
let mut conn = app_state.pool.get().await?;
let user_uuid = user_uuid_from_identifier(conn, &identifier).await?;
let user_uuid = user_uuid_from_identifier(&mut conn, &identifier).await?;
let password_reset_token = serde_json::from_str(
&app_state
.get_cache_key(format!("{user_uuid}_password_reset"))
.await?,
)?;
let password_reset_token = cache_pool
.get_cache_key(format!("{user_uuid}_password_reset"))
.await?;
Ok(password_reset_token)
}
#[allow(clippy::new_ret_no_self)]
pub async fn new(app_state: &AppState, identifier: String) -> Result<(), Error> {
pub async fn new(
conn: &mut Conn,
app_state: &AppState,
identifier: String,
) -> Result<(), Error> {
let token = generate_token::<32>()?;
let mut conn = app_state.pool.get().await?;
let user_uuid = user_uuid_from_identifier(conn, &identifier).await?;
let user_uuid = user_uuid_from_identifier(&mut conn, &identifier).await?;
global_checks(app_state, user_uuid).await?;
global_checks(conn, &app_state.config, user_uuid).await?;
use users::dsl as udsl;
let (username, email_address): (String, String) = udsl::users
.filter(udsl::uuid.eq(user_uuid))
.select((udsl::username, udsl::email))
.get_result(&mut conn)
.get_result(conn)
.await?;
let password_reset_token = PasswordResetToken {
@ -77,6 +76,7 @@ impl PasswordResetToken {
};
app_state
.cache_pool
.set_cache_key(
format!("{user_uuid}_password_reset"),
password_reset_token,
@ -84,6 +84,7 @@ impl PasswordResetToken {
)
.await?;
app_state
.cache_pool
.set_cache_key(token.clone(), user_uuid, 86400)
.await?;
@ -106,7 +107,12 @@ impl PasswordResetToken {
Ok(())
}
pub async fn set_password(&self, app_state: &AppState, password: String) -> Result<(), Error> {
pub async fn set_password(
&self,
conn: &mut Conn,
app_state: &AppState,
password: String,
) -> Result<(), Error> {
if !PASSWORD_REGEX.is_match(&password) {
return Err(Error::BadRequest(
"Please provide a valid password".to_string(),
@ -120,19 +126,17 @@ impl PasswordResetToken {
.hash_password(password.as_bytes(), &salt)
.map_err(|e| Error::PasswordHashError(e.to_string()))?;
let mut conn = app_state.pool.get().await?;
use users::dsl;
update(users::table)
.filter(dsl::uuid.eq(self.user_uuid))
.set(dsl::password.eq(hashed_password.to_string()))
.execute(&mut conn)
.execute(conn)
.await?;
let (username, email_address): (String, String) = dsl::users
.filter(dsl::uuid.eq(self.user_uuid))
.select((dsl::username, dsl::email))
.get_result(&mut conn)
.get_result(conn)
.await?;
let login_page = app_state.config.web.frontend_url.join("login")?;
@ -149,14 +153,14 @@ impl PasswordResetToken {
app_state.mail_client.send_mail(email).await?;
self.delete(app_state).await
self.delete(&app_state.cache_pool).await
}
pub async fn delete(&self, app_state: &AppState) -> Result<(), Error> {
app_state
pub async fn delete(&self, cache_pool: &redis::Client) -> Result<(), Error> {
cache_pool
.del_cache_key(format!("{}_password_reset", &self.user_uuid))
.await?;
app_state.del_cache_key(self.token.to_string()).await?;
cache_pool.del_cache_key(self.token.to_string()).await?;
Ok(())
}