diff --git a/src/controllers/user/update.rs b/src/controllers/user/update.rs index 947fadef552..0d86e1df02a 100644 --- a/src/controllers/user/update.rs +++ b/src/controllers/user/update.rs @@ -3,13 +3,11 @@ use crate::auth::AuthCheck; use crate::controllers::helpers::ok_true; use crate::models::NewEmail; use crate::schema::{emails, users}; -use crate::tasks::spawn_blocking; use crate::util::diesel::prelude::*; use crate::util::errors::{bad_request, server_error, AppResult}; use axum::extract::Path; use axum::response::Response; use axum::Json; -use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; use lettre::Address; use secrecy::{ExposeSecret, SecretString}; @@ -32,85 +30,83 @@ pub async fn update_user( req: Parts, Json(user_update): Json, ) -> AppResult { + use diesel_async::RunQueryDsl; + let mut conn = state.db_write().await?; let auth = AuthCheck::default().check(&req, &mut conn).await?; - spawn_blocking(move || { - use diesel::RunQueryDsl; - - let conn: &mut AsyncConnectionWrapper<_> = &mut conn.into(); - let user = auth.user(); + let user = auth.user(); - // need to check if current user matches user to be updated - if user.id != param_user_id { - return Err(bad_request("current user does not match requested user")); - } + // need to check if current user matches user to be updated + if user.id != param_user_id { + return Err(bad_request("current user does not match requested user")); + } - if let Some(publish_notifications) = &user_update.user.publish_notifications { - if user.publish_notifications != *publish_notifications { - diesel::update(user) - .set(users::publish_notifications.eq(*publish_notifications)) - .execute(conn)?; + if let Some(publish_notifications) = &user_update.user.publish_notifications { + if user.publish_notifications != *publish_notifications { + diesel::update(user) + .set(users::publish_notifications.eq(*publish_notifications)) + .execute(&mut conn) + .await?; - if !publish_notifications { - let email_address = user.verified_email(conn)?; + if !publish_notifications { + let email_address = user.async_verified_email(&mut conn).await?; - if let Some(email_address) = email_address { - let email = PublishNotificationsUnsubscribeEmail { - user_name: &user.gh_login, - domain: &state.emails.domain, - }; + if let Some(email_address) = email_address { + let email = PublishNotificationsUnsubscribeEmail { + user_name: &user.gh_login, + domain: &state.emails.domain, + }; - if let Err(error) = state.emails.send(&email_address, email) { - warn!("Failed to send publish notifications unsubscribe email to {email_address}: {error}"); - } + if let Err(error) = state.emails.async_send(&email_address, email).await { + warn!("Failed to send publish notifications unsubscribe email to {email_address}: {error}"); } } } } + } - if let Some(user_email) = &user_update.user.email { - let user_email = user_email.trim(); - - if user_email.is_empty() { - return Err(bad_request("empty email rejected")); - } + if let Some(user_email) = &user_update.user.email { + let user_email = user_email.trim(); - user_email - .parse::
() - .map_err(|_| bad_request("invalid email address"))?; - - let new_email = NewEmail { - user_id: user.id, - email: user_email, - }; - - let token = diesel::insert_into(emails::table) - .values(&new_email) - .on_conflict(emails::user_id) - .do_update() - .set(&new_email) - .returning(emails::token) - .get_result::(conn) - .map(SecretString::from) - .map_err(|_| server_error("Error in creating token"))?; - - // This swallows any errors that occur while attempting to send the email. Some users have - // an invalid email set in their GitHub profile, and we should let them sign in even though - // we're trying to silently use their invalid address during signup and can't send them an - // email. They'll then have to provide a valid email address. - let email = UserConfirmEmail { - user_name: &user.gh_login, - domain: &state.emails.domain, - token, - }; - - let _ = state.emails.send(user_email, email); + if user_email.is_empty() { + return Err(bad_request("empty email rejected")); } - ok_true() - }) - .await + user_email + .parse::
() + .map_err(|_| bad_request("invalid email address"))?; + + let new_email = NewEmail { + user_id: user.id, + email: user_email, + }; + + let token = diesel::insert_into(emails::table) + .values(&new_email) + .on_conflict(emails::user_id) + .do_update() + .set(&new_email) + .returning(emails::token) + .get_result::(&mut conn) + .await + .map(SecretString::from) + .map_err(|_| server_error("Error in creating token"))?; + + // This swallows any errors that occur while attempting to send the email. Some users have + // an invalid email set in their GitHub profile, and we should let them sign in even though + // we're trying to silently use their invalid address during signup and can't send them an + // email. They'll then have to provide a valid email address. + let email = UserConfirmEmail { + user_name: &user.gh_login, + domain: &state.emails.domain, + token, + }; + + let _ = state.emails.async_send(user_email, email).await; + } + + ok_true() } pub struct UserConfirmEmail<'a> { diff --git a/src/models/user.rs b/src/models/user.rs index 88a7307debe..6d988a3b98a 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -104,6 +104,22 @@ impl User { .optional() } + /// Queries the database for the verified emails + /// belonging to a given user + pub async fn async_verified_email( + &self, + conn: &mut AsyncPgConnection, + ) -> QueryResult> { + use diesel_async::RunQueryDsl; + + Email::belonging_to(self) + .select(emails::email) + .filter(emails::verified.eq(true)) + .first(conn) + .await + .optional() + } + /// Queries for the email belonging to a particular user pub fn email(&self, conn: &mut impl Conn) -> QueryResult> { use diesel::RunQueryDsl;