switched from using Mutexes with MySqlConnection to Arcs with MySqlPools

This commit is contained in:
franklinblanco 2022-07-17 09:38:41 -04:00
parent 04d60bd3ef
commit e8c169f325
7 changed files with 39 additions and 33 deletions

View File

@ -0,0 +1,3 @@
SELECT *
FROM user
WHERE user.id = ?

View File

@ -1,8 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use sqlx::{MySqlConnection, Connection}; use sqlx::{MySqlPool};
pub async fn start_database_connection(env_vars: &HashMap<String, String>) -> Result<MySqlConnection, sqlx::Error>{ pub async fn start_database_connection(env_vars: &HashMap<String, String>) -> Result<MySqlPool, sqlx::Error>{
let db_user = match env_vars.get("DB_USER") { let db_user = match env_vars.get("DB_USER") {
Some(str) => str, Some(str) => str,
None => panic!("DB_USER env var not found") None => panic!("DB_USER env var not found")
@ -20,9 +20,9 @@ pub async fn start_database_connection(env_vars: &HashMap<String, String>) -> Re
None => panic!("DB_DATABASE_NAME env var not found") None => panic!("DB_DATABASE_NAME env var not found")
}; };
let formatted_db_url = &format!("mysql://{db_user}:{db_pass}@{db_host}/{db_database_name}"); let formatted_db_url = &format!("mysql://{db_user}:{db_pass}@{db_host}/{db_database_name}");
sqlx::MySqlConnection::connect(&formatted_db_url).await sqlx::MySqlPool::connect(&formatted_db_url).await
} }
pub async fn run_all_migrations(conn: &mut MySqlConnection){ pub async fn run_all_migrations(conn: &MySqlPool){
match sqlx::migrate!("./migrations").run(conn).await { match sqlx::migrate!("./migrations").run(conn).await {
Ok(()) => {println!("{}", "Successfully ran migrations.")}, Ok(()) => {println!("{}", "Successfully ran migrations.")},
Err(error) => {panic!("{error}")} Err(error) => {panic!("{error}")}

View File

@ -1,18 +1,19 @@
use sqlx::{MySqlConnection, mysql::MySqlQueryResult}; use sqlx::MySqlPool;
use sqlx::{mysql::MySqlQueryResult};
use crate::r#do::token::Token; use crate::r#do::token::Token;
use crate::r#do::token::{AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS}; use crate::r#do::token::{AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS};
pub async fn insert_token(conn: &mut MySqlConnection, token: &Token) -> Result<MySqlQueryResult, sqlx::Error> { pub async fn insert_token(conn: &MySqlPool, token: &Token) -> Result<MySqlQueryResult, sqlx::Error> {
sqlx::query_file!("sql/schema/token/insert.sql", token.user_id, token.auth_token, token.refresh_token).execute(conn).await sqlx::query_file!("sql/schema/token/insert.sql", token.user_id, token.auth_token, token.refresh_token).execute(conn).await
} }
pub async fn get_tokens_with_user_id(conn: &mut MySqlConnection, user_id: &i32) -> Result<Vec<Token>, sqlx::Error> { pub async fn get_tokens_with_user_id(conn: &MySqlPool, user_id: &i32) -> Result<Vec<Token>, sqlx::Error> {
sqlx::query_file_as!(Token, "sql/schema/token/find_with_user_id.sql", user_id).fetch_all(conn).await sqlx::query_file_as!(Token, "sql/schema/token/find_with_user_id.sql", user_id).fetch_all(conn).await
} }
pub async fn update_token_with_id(conn: &mut MySqlConnection, token: &Token ) -> Result<MySqlQueryResult, sqlx::Error> { pub async fn update_token_with_id(conn: &MySqlPool, token: &Token ) -> Result<MySqlQueryResult, sqlx::Error> {
sqlx::query_file!("sql/schema/token/update.sql", token.auth_token, token.refresh_token, token.id).execute(conn).await sqlx::query_file!("sql/schema/token/update.sql", token.auth_token, token.refresh_token, token.id).execute(conn).await
} }
pub async fn delete_expired_tokens(conn: &mut MySqlConnection) -> Result<MySqlQueryResult, sqlx::Error>{ pub async fn delete_expired_tokens(conn: &MySqlPool) -> Result<MySqlQueryResult, sqlx::Error>{
sqlx::query_file!("sql/schema/token/delete_expired_tokens.sql", sqlx::query_file!("sql/schema/token/delete_expired_tokens.sql",
AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS) AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS)
.execute(conn).await .execute(conn).await

View File

@ -1,15 +1,18 @@
use sqlx::{MySqlConnection, mysql::MySqlQueryResult}; use sqlx::{mysql::MySqlQueryResult, MySqlPool};
use crate::r#do::user::User; use crate::r#do::user::User;
pub async fn insert_user(conn: &mut MySqlConnection, user_to_insert: &User) -> Result<MySqlQueryResult, sqlx::Error>{ pub async fn insert_user(conn: &MySqlPool, user_to_insert: &User) -> Result<MySqlQueryResult, sqlx::Error>{
sqlx::query_file!("sql/schema/user/insert.sql", sqlx::query_file!("sql/schema/user/insert.sql",
user_to_insert.app, user_to_insert.email, user_to_insert.name, user_to_insert.password, user_to_insert.salt) user_to_insert.app, user_to_insert.email, user_to_insert.name, user_to_insert.password, user_to_insert.salt)
.execute(conn).await .execute(conn).await
} }
pub async fn find_user_by_email(conn: &mut MySqlConnection, email: &String, app: &String) -> Result<User, sqlx::Error>{ pub async fn find_user_by_email(conn: &MySqlPool, email: &String, app: &String) -> Result<User, sqlx::Error>{
sqlx::query_file_as!(User, "sql/schema/user/find_with_email.sql", email, app).fetch_one(conn).await sqlx::query_file_as!(User, "sql/schema/user/find_with_email.sql", email, app).fetch_one(conn).await
} }
pub async fn _find_user_by_id(conn: &MySqlPool, id: &i32) -> Result<User, sqlx::Error> {
sqlx::query_file_as!(User, "sql/schema/user/find_with_id.sql", id).fetch_one(conn).await
}
//pub async fn _update_user(conn: &mut MySqlConnection, user_to_modify: &User) -> Result<(), sqlx::Error>{ //pub async fn _update_user(conn: &mut MySqlConnection, user_to_modify: &User) -> Result<(), sqlx::Error>{
// Ok(()) // Ok(())
//} //}

View File

@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use sqlx::MySqlConnection; use sqlx::{MySqlPool};
pub struct SharedStateObj{ pub struct SharedStateObj{
pub db_conn: MySqlConnection, pub db_conn: MySqlPool,
pub env_vars: HashMap<String, String>, pub env_vars: HashMap<String, String>,
} }

View File

@ -1,4 +1,4 @@
use std::sync::Mutex; use std::sync::{Mutex, Arc};
use actix_web::{HttpServer, App, web}; use actix_web::{HttpServer, App, web};
use crate::r#do::shared_state::SharedStateObj; use crate::r#do::shared_state::SharedStateObj;
use super::user_routes; use super::user_routes;
@ -29,9 +29,8 @@ pub async fn start_all_routes(after_startup_fn_call: &dyn Fn(), state: SharedSta
// Extract variables to be put into shared app state & clone them // Extract variables to be put into shared app state & clone them
let db_conn_state = web::Data::new(Mutex::new(state.db_conn)); let db_conn_state = web::Data::new(Arc::new(state.db_conn));
let env_vars_state = web::Data::new(Mutex::new(state.env_vars.clone())); let env_vars_state = web::Data::new(Mutex::new(state.env_vars.clone()));
// Start server code that turns into a future to be executed below // Start server code that turns into a future to be executed below
let server_future = HttpServer::new( move || { let server_future = HttpServer::new( move || {
App::new() App::new()

View File

@ -1,14 +1,14 @@
use core::panic; use core::panic;
use std::{sync::Mutex}; use std::{sync::{Arc}};
use actix_web::{web::{self, Data}, HttpResponse, post, patch, HttpRequest}; use actix_web::{web::{self, Data}, HttpResponse, post, patch, HttpRequest};
use chrono::{Utc}; use chrono::{Utc};
use sqlx::MySqlConnection; use sqlx::{MySqlPool};
use crate::{r#do::user::User, dao::{user_dao::{insert_user, find_user_by_email}, token_dao::{insert_token, self, update_token_with_id}}, dto::{user_dtos::{UserForCreationDto, UserForLoginDto}, message_resources_dtos::MessageResourceDto}, validation::user_validator, util::hasher::{self, generate_multiple_random_token_with_rng}, r#do::token::Token, resources::error_messages::{ERROR_USER_ALREADY_EXISTS, ERROR_USER_DOES_NOT_EXIST, ERROR_PASSWORD_INCORRECT, ERROR_INVALID_TOKEN, ERROR_MISSING_TOKEN, ERROR_INCORRECT_TOKEN, ERROR_EXPIRED_TOKEN}, r#do::token::AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, r#do::token::REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS}; use crate::{r#do::user::User, dao::{user_dao::{insert_user, find_user_by_email}, token_dao::{insert_token, self, update_token_with_id}}, dto::{user_dtos::{UserForCreationDto, UserForLoginDto}, message_resources_dtos::MessageResourceDto}, validation::user_validator, util::hasher::{self, generate_multiple_random_token_with_rng}, r#do::token::Token, resources::error_messages::{ERROR_USER_ALREADY_EXISTS, ERROR_USER_DOES_NOT_EXIST, ERROR_PASSWORD_INCORRECT, ERROR_INVALID_TOKEN, ERROR_MISSING_TOKEN, ERROR_INCORRECT_TOKEN, ERROR_EXPIRED_TOKEN}, r#do::token::AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, r#do::token::REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS};
#[post("/user")] #[post("/user")]
pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn: Data<Mutex<MySqlConnection>>) -> HttpResponse { pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse {
let mut message_resources: Vec<MessageResourceDto> = Vec::new(); let mut message_resources: Vec<MessageResourceDto> = Vec::new();
// Get user object from json // Get user object from json
@ -23,7 +23,7 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
user_validator::validate_user_for_creation(incoming_user_obj, &mut message_resources); user_validator::validate_user_for_creation(incoming_user_obj, &mut message_resources);
// Find if user exists // Find if user exists
match find_user_by_email(&mut db_conn.lock().unwrap(), &user_to_insert.email, &user_to_insert.app).await{ match find_user_by_email(&db_conn, &user_to_insert.email, &user_to_insert.app).await{
Ok(_usr) => { Ok(_usr) => {
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_ALREADY_EXISTS)); message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_ALREADY_EXISTS));
return HttpResponse::BadRequest().json(web::Json(message_resources)); return HttpResponse::BadRequest().json(web::Json(message_resources));
@ -40,7 +40,7 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
user_to_insert.salt = hash_result.salt; user_to_insert.salt = hash_result.salt;
// Insert user in DB // Insert user in DB
match insert_user(&mut db_conn.lock().unwrap(), &user_to_insert).await{ match insert_user(&db_conn, &user_to_insert).await{
Ok(resultrs) => { Ok(resultrs) => {
user_to_insert.id = resultrs.last_insert_id() as i32; user_to_insert.id = resultrs.last_insert_id() as i32;
}, },
@ -58,7 +58,7 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
); );
// Insert token in DB // Insert token in DB
match insert_token(&mut db_conn.lock().unwrap(), &token_to_insert).await{ match insert_token(&db_conn, &token_to_insert).await{
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32}, Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32},
Err(e) => {panic!("{e}")} Err(e) => {panic!("{e}")}
} }
@ -68,7 +68,7 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
} }
#[post("/user/auth/password")] #[post("/user/auth/password")]
pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLoginDto>, db_conn: Data<Mutex<MySqlConnection>>) -> HttpResponse { pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLoginDto>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse {
let mut message_resources: Vec<MessageResourceDto> = Vec::new(); let mut message_resources: Vec<MessageResourceDto> = Vec::new();
let incoming_user_obj = incoming_user.0; let incoming_user_obj = incoming_user.0;
@ -79,7 +79,7 @@ pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLog
if message_resources.len() > 0 { return HttpResponse::BadRequest().json(web::Json(message_resources)); } if message_resources.len() > 0 { return HttpResponse::BadRequest().json(web::Json(message_resources)); }
// If user exists get it, if it doesn't blow up to the client // If user exists get it, if it doesn't blow up to the client
let persisted_user = match find_user_by_email(&mut db_conn.lock().unwrap(), &incoming_user_obj.email, &incoming_user_obj.app).await { let persisted_user = match find_user_by_email(&db_conn, &incoming_user_obj.email, &incoming_user_obj.app).await {
Ok(rs) => {rs}, Ok(rs) => {rs},
Err(_e) => { Err(_e) => {
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_DOES_NOT_EXIST)); message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_DOES_NOT_EXIST));
@ -104,7 +104,7 @@ pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLog
); );
// Insert token in DB // Insert token in DB
match insert_token(&mut db_conn.lock().unwrap(), &token_to_insert).await{ match insert_token(&db_conn, &token_to_insert).await{
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32}, Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32},
Err(e) => {panic!("{e}")} Err(e) => {panic!("{e}")}
}; };
@ -113,7 +113,7 @@ pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLog
} }
#[post("/user/auth/token/{user_id}")] #[post("/user/auth/token/{user_id}")]
pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: web::Path<i32>, db_conn: Data<Mutex<MySqlConnection>>) -> HttpResponse{ pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: web::Path<i32>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse{
let mut message_resources: Vec<MessageResourceDto> = Vec::new(); let mut message_resources: Vec<MessageResourceDto> = Vec::new();
let headers = request.headers(); let headers = request.headers();
let auth_token = match headers.get("auth-token") { let auth_token = match headers.get("auth-token") {
@ -130,11 +130,11 @@ pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: we
return HttpResponse::BadRequest().json(web::Json(message_resources)); return HttpResponse::BadRequest().json(web::Json(message_resources));
}}; }};
match token_dao::delete_expired_tokens(&mut db_conn.lock().unwrap()).await match token_dao::delete_expired_tokens(&db_conn).await
{ Ok(_res) => {}, Err(_err) => {} }; { Ok(_res) => {}, Err(_err) => {} };
let mut _persisted_token = let mut _persisted_token =
match token_dao::get_tokens_with_user_id(&mut db_conn.lock().unwrap(), &user_id).await { match token_dao::get_tokens_with_user_id(&db_conn, &user_id).await {
Ok(tokens) => { Ok(tokens) => {
for token in tokens{ for token in tokens{
let now = Utc::now().naive_utc(); let now = Utc::now().naive_utc();
@ -161,7 +161,7 @@ pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: we
} }
#[patch("/user/refresh/{user_id}")] #[patch("/user/refresh/{user_id}")]
pub async fn refresh_auth_token(request: HttpRequest, user_id: web::Path<i32>, db_conn: Data<Mutex<MySqlConnection>>) -> HttpResponse{ pub async fn refresh_auth_token(request: HttpRequest, user_id: web::Path<i32>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse{
let mut message_resources: Vec<MessageResourceDto> = Vec::new(); let mut message_resources: Vec<MessageResourceDto> = Vec::new();
let headers = request.headers(); let headers = request.headers();
let refresh_token = match headers.get("refresh-token") { let refresh_token = match headers.get("refresh-token") {
@ -178,11 +178,11 @@ pub async fn refresh_auth_token(request: HttpRequest, user_id: web::Path<i32>, d
return HttpResponse::BadRequest().json(web::Json(message_resources)); return HttpResponse::BadRequest().json(web::Json(message_resources));
}}; }};
match token_dao::delete_expired_tokens(&mut db_conn.lock().unwrap()).await match token_dao::delete_expired_tokens(&db_conn).await
{ Ok(_res) => {}, Err(_err) => {} }; { Ok(_res) => {}, Err(_err) => {} };
let mut persisted_token = let mut persisted_token =
match token_dao::get_tokens_with_user_id(&mut db_conn.lock().unwrap(), &user_id).await { match token_dao::get_tokens_with_user_id(&db_conn, &user_id).await {
Ok(tokens) => { Ok(tokens) => {
let mut matched_token: Option<Token> = None; let mut matched_token: Option<Token> = None;
for token in tokens{ for token in tokens{
@ -209,7 +209,7 @@ pub async fn refresh_auth_token(request: HttpRequest, user_id: web::Path<i32>, d
return HttpResponse::InternalServerError().json(web::Json(message_resources)); return HttpResponse::InternalServerError().json(web::Json(message_resources));
}}; }};
persisted_token.auth_token = generate_multiple_random_token_with_rng(1).await.expect("msg").get(0).expect("msg").to_string(); persisted_token.auth_token = generate_multiple_random_token_with_rng(1).await.expect("msg").get(0).expect("msg").to_string();
match update_token_with_id(&mut db_conn.lock().unwrap(), &persisted_token).await { match update_token_with_id(&db_conn, &persisted_token).await {
Ok(_rs) => { Ok(_rs) => {
return HttpResponse::Ok().json(web::Json(persisted_token)); return HttpResponse::Ok().json(web::Json(persisted_token));
}, },