Compare commits

...
Author SHA1 Message Date
Brandon Thomas c81e68f7a0 update transactions 2026-05-13 18:04:42 -04:00
Brandon Thomas 2a3cc35df0 cleanup that mess 2026-05-13 17:56:40 -04:00
Brandon Thomas 324d07c4f3 wip 2026-05-13 17:46:50 -04:00
13 changed files with 123 additions and 60 deletions
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO user_sessions (\n token,\n user_token,\n ip_address_creation,\n expires_at\n)\nVALUES ( ?, ?, ?, NOW() + interval 1 year )\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "ff9690b62bcc93212ecf9698d9ae1d12e6284fd4a8259986f6967bf5c29462f1"
}
@@ -54,20 +54,26 @@ async fn seed_user(
let email_gravatar_hash = email_to_gravatar_hash(&email_address);
let password_hash = bcrypt_password_hash(password)?;
create_account_from_email_and_password(mysql_pool, CreateAccountFromEmailPasswordArgs {
username: &username,
display_name,
email_address: &email_address,
email_gravatar_hash: &email_gravatar_hash,
password_hash: &password_hash,
ip_address: "127.0.0.1",
maybe_source: None,
maybe_referral_url: None,
maybe_landing_url: None,
maybe_referral_partner: None,
maybe_referral_user_token: None,
maybe_user_token: maybe_user_token.as_ref(),
}).await.map_err(|err| anyhow!("err: {:?}", err))?;
let mut mysql_connection = mysql_pool.acquire().await
.map_err(|err| anyhow!("pool acquire error: {:?}", err))?;
create_account_from_email_and_password(
CreateAccountFromEmailPasswordArgs {
username: &username,
display_name,
email_address: &email_address,
email_gravatar_hash: &email_gravatar_hash,
password_hash: &password_hash,
ip_address: "127.0.0.1",
maybe_source: None,
maybe_referral_url: None,
maybe_landing_url: None,
maybe_referral_partner: None,
maybe_referral_user_token: None,
maybe_user_token: maybe_user_token.as_ref(),
},
&mut mysql_connection,
).await.map_err(|err| anyhow!("err: {:?}", err))?;
Ok(())
}
@@ -5,10 +5,10 @@
use crate::queries::users::user::create::create_account_error::CreateAccountError;
use crate::queries::users::user::create::create_account_generic::{create_account_generic, GenericCreateAccountArgs};
use crate::utils::transactor::Transactor;
use enums::by_table::users::user_signup_method::UserSignupMethod;
use enums::by_table::users::user_signup_source::UserSignupSource;
use sqlx::MySqlPool;
use sqlx::pool::PoolConnection;
use sqlx::MySql;
use tokens::tokens::users::UserToken;
pub struct CreateAccountFromEmailPasswordArgs<'a> {
@@ -48,8 +48,8 @@ pub struct CreateAccountSuccessResult {
pub async fn create_account_from_email_and_password(
mysql_pool: &MySqlPool,
args: CreateAccountFromEmailPasswordArgs<'_>,
mysql_connection: &mut PoolConnection<MySql>,
) -> Result<CreateAccountSuccessResult, CreateAccountError>
{
let result= create_account_generic(
@@ -90,7 +90,7 @@ pub async fn create_account_from_email_and_password(
email_is_synthetic: false,
was_eagerly_provisioned: false,
},
Transactor::for_pool(mysql_pool),
&mut **mysql_connection,
).await?;
Ok(CreateAccountSuccessResult {
@@ -5,9 +5,9 @@
use crate::queries::users::user::create::create_account_error::CreateAccountError;
use crate::queries::users::user::create::create_account_generic::{create_account_generic, GenericCreateAccountArgs};
use crate::utils::transactor::Transactor;
use enums::by_table::users::user_signup_method::UserSignupMethod;
use enums::by_table::users::user_signup_source::UserSignupSource;
use sqlx::{Executor, MySql};
use tokens::tokens::users::UserToken;
/// SSO accounts do not have passwords at account creation
@@ -38,10 +38,12 @@ pub struct CreateAccountFromGoogleSsoArgs<'a> {
pub maybe_referral_user_token: Option<&'a UserToken>,
}
pub async fn create_account_from_google_sso(
pub async fn create_account_from_google_sso<'e, 'c: 'e, E>(
args: CreateAccountFromGoogleSsoArgs<'_>,
transactor: Transactor<'_, '_>,
mysql_executor: E,
) -> Result<UserToken, CreateAccountError>
where
E: 'e + Executor<'c, Database = MySql>,
{
let result= create_account_generic(
GenericCreateAccountArgs {
@@ -79,7 +81,7 @@ pub async fn create_account_from_google_sso(
email_is_synthetic: false,
was_eagerly_provisioned: false,
},
transactor,
mysql_executor,
).await?;
Ok(result.user_token)
@@ -5,9 +5,9 @@
use crate::queries::users::user::create::create_account_error::CreateAccountError;
use crate::queries::users::user::create::create_account_generic::{create_account_generic, GenericCreateAccountArgs};
use crate::utils::transactor::Transactor;
use enums::by_table::users::user_signup_method::UserSignupMethod;
use enums::by_table::users::user_signup_source::UserSignupSource;
use sqlx::{Executor, MySql};
use tokens::tokens::users::UserToken;
/// Stripe checkout sessions do not have a real email address or password.
@@ -39,10 +39,12 @@ pub struct CreateAccountFromStripeCheckoutArgs<'a> {
pub maybe_referral_user_token: Option<&'a UserToken>,
}
pub async fn create_account_from_stripe_checkout(
pub async fn create_account_from_stripe_checkout<'e, 'c: 'e, E>(
args: CreateAccountFromStripeCheckoutArgs<'_>,
transactor: Transactor<'_, '_>,
mysql_executor: E,
) -> Result<UserToken, CreateAccountError>
where
E: 'e + Executor<'c, Database = MySql>,
{
let result= create_account_generic(
GenericCreateAccountArgs {
@@ -84,7 +86,7 @@ pub async fn create_account_from_stripe_checkout(
// NB: This is just for testing.
maybe_user_token: None,
},
transactor,
mysql_executor,
).await?;
Ok(result.user_token)
@@ -4,11 +4,11 @@
#![forbid(unused_variables)]
use crate::queries::users::user::create::create_account_error::CreateAccountError;
use crate::utils::transactor::Transactor;
use enums::by_table::users::user_signup_method::UserSignupMethod;
use enums::by_table::users::user_signup_source::UserSignupSource;
use log::warn;
use sqlx::error::Error::Database;
use sqlx::{Executor, MySql};
use tokens::tokens::users::UserToken;
pub struct GenericCreateAccountArgs<'a> {
@@ -88,10 +88,12 @@ pub struct CreateAccountSuccessResult {
pub user_id: u64,
}
pub async fn create_account_generic(
pub async fn create_account_generic<'e, 'c: 'e, E>(
args: GenericCreateAccountArgs<'_>,
transactor: Transactor<'_, '_>,
mysql_executor: E,
) -> Result<CreateAccountSuccessResult, CreateAccountError>
where
E: 'e + Executor<'c, Database = MySql>,
{
const INITIAL_PROFILE_MARKDOWN : &str = "";
const INITIAL_PROFILE_RENDERED_HTML : &str = "";
@@ -193,7 +195,7 @@ SET
);
let query_result = transactor.execute(query).await;
let query_result = query.execute(mysql_executor).await;
let record_id = match query_result {
Ok(res) => {
@@ -0,0 +1,36 @@
use log::info;
use sqlx::{Executor, MySql};
use tokens::tokens::user_sessions::UserSessionToken;
use tokens::tokens::users::UserToken;
pub async fn create_user_session_with_executor<'e, 'c: 'e, E>(
user_token: &UserToken,
ip_address: &str,
mysql_executor: E,
) -> Result<UserSessionToken, sqlx::Error>
where
E: 'e + Executor<'c, Database = MySql>,
{
let session_token = UserSessionToken::generate();
let result = sqlx::query!(
r#"
INSERT INTO user_sessions (
token,
user_token,
ip_address_creation,
expires_at
)
VALUES ( ?, ?, ?, NOW() + interval 1 year )
"#,
session_token.as_str(),
user_token.as_str(),
ip_address,
)
.execute(mysql_executor)
.await?;
info!("Created session id: {}", result.last_insert_id());
Ok(session_token)
}
@@ -1,5 +1,6 @@
pub mod create_impersonated_user_session;
pub mod create_user_session;
pub mod create_user_session_with_executor;
pub mod create_user_session_with_transactor;
pub mod delete_user_session;
pub mod get_user_session_by_token;
@@ -12,8 +12,7 @@ use log::{error, info, warn};
use mysql_queries::queries::users::user::create::create_account_error::CreateAccountError;
use mysql_queries::queries::users::user::create::create_account_from_google_sso::{create_account_from_google_sso, CreateAccountFromGoogleSsoArgs};
use mysql_queries::queries::users::user::create::create_account_from_stripe_checkout::{create_account_from_stripe_checkout, CreateAccountFromStripeCheckoutArgs};
use mysql_queries::queries::users::user_sessions::create_user_session_with_transactor::create_user_session_with_transactor;
use mysql_queries::utils::transactor::Transactor;
use mysql_queries::queries::users::user_sessions::create_user_session_with_executor::create_user_session_with_executor;
use sqlx::pool::PoolConnection;
use sqlx::{Acquire, MySql};
use std::collections::HashMap;
@@ -92,7 +91,7 @@ pub (super) async fn user_creation_case(
maybe_referral_partner: maybe_referral_partner.clone(),
maybe_referral_user_token: maybe_referral_user_token.as_ref(),
},
Transactor::for_transaction(&mut transaction),
&mut *transaction,
).await;
match result {
@@ -140,10 +139,10 @@ pub (super) async fn user_creation_case(
CommonWebError::ServerError
})?;
let session_token = create_user_session_with_transactor(
let session_token = create_user_session_with_executor(
&final_user_token,
&ip_address,
Transactor::for_connection(mysql_connection))
&mut **mysql_connection)
.await
.map_err(|e| {
warn!("error creating user session: {:?}", e);
@@ -25,10 +25,9 @@ use log::{info, warn};
use mysql_queries::mediators::firehose_publisher::FirehosePublisher;
use mysql_queries::queries::users::user::create::create_account_error::CreateAccountError;
use mysql_queries::queries::users::user::create::create_account_from_email_and_password::{create_account_from_email_and_password, CreateAccountFromEmailPasswordArgs};
use mysql_queries::queries::users::user_sessions::create_user_session::create_user_session;
use mysql_queries::queries::users::user_sessions::create_user_session_with_executor::create_user_session_with_executor;
use password::bcrypt_hash_password::bcrypt_hash_password;
use sqlx::MySqlPool;
use tokens::tokens::user_sessions::UserSessionToken;
use user_input_common::check_for_slurs::contains_slurs;
use users::email::email_to_gravatar_hash::email_to_gravatar_hash;
use utoipa::ToSchema;
@@ -218,6 +217,13 @@ pub async fn create_account_handler(
let maybe_landing_url = request.maybe_landing_url.clone();
// Acquire a single connection for pre-creation lookups.
let mut mysql_connection = mysql_pool.acquire().await
.map_err(|err| {
warn!("MySql pool error: {:?}", err);
CreateAccountErrorResponse::server_error()
})?;
// Look up referring user by username (optional, fail-open).
let maybe_referral_user_token = match request.maybe_referral_username.as_deref() {
Some(raw) => {
@@ -225,7 +231,7 @@ pub async fn create_account_handler(
if lookup_username.is_empty() {
None
} else {
match get_user_token_by_username_with_executor(&lookup_username, &**mysql_pool).await {
match get_user_token_by_username_with_executor(&lookup_username, &mut *mysql_connection).await {
Ok(token) => token,
Err(err) => {
warn!("Referral user lookup failed (continuing): {:?}", err);
@@ -238,7 +244,6 @@ pub async fn create_account_handler(
};
let create_account_result = create_account_from_email_and_password(
&mysql_pool,
CreateAccountFromEmailPasswordArgs {
username: &username,
display_name: &display_name,
@@ -252,7 +257,8 @@ pub async fn create_account_handler(
maybe_referral_partner: request.maybe_referral_username.as_deref().and_then(sanitize_referral_username),
maybe_referral_user_token: maybe_referral_user_token.as_ref(),
maybe_user_token: None, // NB: This parameter is for internal testing only
}
},
&mut mysql_connection,
).await;
let new_user_data = match create_account_result {
@@ -285,19 +291,15 @@ pub async fn create_account_handler(
info!("new user id: {}", new_user_data.user_id);
let create_session_result = create_user_session(
new_user_data.user_token.as_str(),
let session_token = create_user_session_with_executor(
&new_user_data.user_token,
&ip_address,
&mysql_pool
).await;
let session_token = match create_session_result {
Ok(token) => token,
Err(e) => {
&mut *mysql_connection,
).await
.map_err(|e| {
warn!("create account session creation error : {:?}", e);
return Err(CreateAccountErrorResponse::server_error());
}
};
CreateAccountErrorResponse::server_error()
})?;
info!("new user session created");
@@ -309,13 +311,11 @@ pub async fn create_account_handler(
})?;
// NB: Enroll new users in studio for a while.
enroll_in_studio(&new_user_data.user_token, &ip_address, &mysql_pool, None).await
enroll_in_studio(&new_user_data.user_token, &ip_address, &mut mysql_connection, None).await
.map_err(|e| {
warn!("error enrolling in studio: {:?}", e);
}).ok();
let session_token = UserSessionToken::new_from_str(&session_token);
let session_cookie = match session_cookie_manager.create_cookie(&session_token, &new_user_data.user_token) {
Ok(cookie) => cookie,
Err(_) => return Err(CreateAccountErrorResponse::server_error()),
@@ -10,7 +10,6 @@ use log::{error, info, warn};
use mysql_queries::queries::google_sign_in_accounts::insert_google_sign_in_account::{insert_google_sign_in_account, InsertGoogleSignInArgs};
use mysql_queries::queries::users::user::create::create_account_error::CreateAccountError;
use mysql_queries::queries::users::user::create::create_account_from_google_sso::{create_account_from_google_sso, CreateAccountFromGoogleSsoArgs};
use mysql_queries::utils::transactor::Transactor;
use sqlx::pool::PoolConnection;
use sqlx::{Acquire, MySql};
use users::email::email_to_gravatar_hash::email_to_gravatar_hash;
@@ -74,7 +73,7 @@ pub async fn handle_new_sso_account_for_new_user(
maybe_landing_url: args.maybe_landing_url.clone(),
maybe_referral_user_token: args.maybe_referral_user_token.as_ref(),
},
Transactor::for_transaction(&mut transaction),
&mut *transaction,
).await;
match result {
@@ -179,10 +179,13 @@ pub async fn login_handler(
UserSessionFeatureFlags::new(user.maybe_feature_flags.as_deref());
// NB: Enroll new users in studio for a while.
enroll_in_studio(&user.token, &ip_address, &mysql_pool, Some(&user_feature_flags)).await
// TODO: Refactor login handler to use a connection throughout instead of the pool.
if let Ok(mut conn) = mysql_pool.acquire().await {
enroll_in_studio(&user.token, &ip_address, &mut conn, Some(&user_feature_flags)).await
.map_err(|e| {
warn!("error enrolling in studio: {:?}", e);
}).ok();
}
UserSessionToken::new_from_str(&token)
} else {
@@ -1,4 +1,5 @@
use sqlx::MySqlPool;
use sqlx::pool::PoolConnection;
use sqlx::{Acquire, MySql};
use enums::by_table::users::user_feature_flag::UserFeatureFlag;
use errors::AnyhowResult;
@@ -11,7 +12,7 @@ use crate::http_server::session::lookup::user_session_feature_flags::UserSession
pub async fn enroll_in_studio(
user_token: &UserToken,
ip_address: &str,
mysql_pool: &MySqlPool,
mysql_connection: &mut PoolConnection<MySql>,
maybe_existing_feature_flags: Option<&UserSessionFeatureFlags>
) -> AnyhowResult<()> {
@@ -29,7 +30,7 @@ pub async fn enroll_in_studio(
UserFeatureFlag::VideoStyleTransfer,
]);
let mut transaction = mysql_pool.begin().await?;
let mut transaction = mysql_connection.begin().await?;
set_user_feature_flags_transactional(SetUserFeatureFlagTransactionalArgs {
subject_user_token: user_token,