From 057dac5b09e6e25a2125139b1a37ebf317ac2358 Mon Sep 17 00:00:00 2001 From: N08I40K Date: Tue, 15 Apr 2025 13:08:46 +0400 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=84=D1=83=D0=BD=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D1=81=D1=83?= =?UTF-8?q?=D1=89=D0=B5=D1=81=D1=82=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B9=20=D0=B2=20?= =?UTF-8?q?=D0=B1=D0=B0=D0=B7=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= =?UTF-8?q?=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20=D1=80=D1=83=D1=87?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BC=D1=8C=D1=8E=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- .idea/schedule-parser-rusted.iml | 1 + src/app_state.rs | 20 +--- src/database/driver.rs | 164 ++++++++++++++---------------- src/extractors/authorized_user.rs | 4 +- src/routes/auth/sign_in.rs | 36 ++++--- src/routes/auth/sign_up.rs | 10 +- src/routes/fcm/update_callback.rs | 6 +- src/utility/mod.rs | 3 +- src/utility/mutex.rs | 21 ++++ 10 files changed, 138 insertions(+), 130 deletions(-) create mode 100644 src/utility/mutex.rs diff --git a/.gitignore b/.gitignore index b6bf0fa..e5fa733 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ schedule.json teachers.json -.env* \ No newline at end of file +.env* +/*-firebase-adminsdk-*.json diff --git a/.idea/schedule-parser-rusted.iml b/.idea/schedule-parser-rusted.iml index 7bd07ad..9084b1e 100644 --- a/.idea/schedule-parser-rusted.iml +++ b/.idea/schedule-parser-rusted.iml @@ -10,6 +10,7 @@ + diff --git a/src/app_state.rs b/src/app_state.rs index 733575e..28a7892 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -7,8 +7,7 @@ use diesel::{Connection, PgConnection}; use sha1::{Digest, Sha1}; use std::env; use std::hash::Hash; -use std::ops::DerefMut; -use std::sync::{Mutex, MutexGuard}; +use std::sync::Mutex; #[derive(Clone)] pub struct Schedule { @@ -74,23 +73,6 @@ impl AppState { } } -impl AppState { - /// Получение объекта соединения с базой данных PostgreSQL - pub fn connection(&self) -> MutexGuard { - self.database.lock().unwrap() - } - - pub fn lock_connection(&self, f: F) -> T - where - F: FnOnce(&mut PgConnection) -> T, - { - let mut lock = self.connection(); - let conn = lock.deref_mut(); - - f(conn) - } -} - /// Создание нового объекта web::Data pub fn app_state() -> web::Data { web::Data::new(AppState::new()) diff --git a/src/database/driver.rs b/src/database/driver.rs index 07464e7..bfc8c4a 100644 --- a/src/database/driver.rs +++ b/src/database/driver.rs @@ -1,118 +1,112 @@ pub mod users { + use crate::app_state::AppState; use crate::database::models::User; use crate::database::schema::users::dsl::users; use crate::database::schema::users::dsl::*; + use actix_web::web; + use diesel::SelectableHelper; use diesel::{insert_into, ExpressionMethods, QueryResult}; - use diesel::{PgConnection, SelectableHelper}; use diesel::{QueryDsl, RunQueryDsl}; - use std::ops::DerefMut; - use std::sync::Mutex; + use crate::utility::mutex::MutexScope; - pub fn get(connection: &Mutex, _id: &String) -> QueryResult { - let mut lock = connection.lock().unwrap(); - let con = lock.deref_mut(); - - users - .filter(id.eq(_id)) - .select(User::as_select()) - .first(con) + pub fn get(state: &web::Data, _id: &String) -> QueryResult { + state.database.scope(|conn| { + users + .filter(id.eq(_id)) + .select(User::as_select()) + .first(conn) + }) } - pub fn get_by_username( - connection: &Mutex, - _username: &String, - ) -> QueryResult { - let mut lock = connection.lock().unwrap(); - let con = lock.deref_mut(); - - users - .filter(username.eq(_username)) - .select(User::as_select()) - .first(con) + pub fn get_by_username(state: &web::Data, _username: &String) -> QueryResult { + state.database.scope(|conn| { + users + .filter(username.eq(_username)) + .select(User::as_select()) + .first(conn) + }) } - pub fn get_by_vk_id(connection: &Mutex, _vk_id: i32) -> QueryResult { - let mut lock = connection.lock().unwrap(); - let con = lock.deref_mut(); - - users - .filter(vk_id.eq(_vk_id)) - .select(User::as_select()) - .first(con) + //noinspection RsTraitObligations + pub fn get_by_vk_id(state: &web::Data, _vk_id: i32) -> QueryResult { + state.database.scope(|conn| { + users + .filter(vk_id.eq(_vk_id)) + .select(User::as_select()) + .first(conn) + }) } - pub fn contains_by_username(connection: &Mutex, _username: &String) -> bool { - let mut lock = connection.lock().unwrap(); - let con = lock.deref_mut(); - - match users - .filter(username.eq(_username)) - .count() - .get_result::(con) - { - Ok(count) => count > 0, - Err(_) => false, - } + //noinspection DuplicatedCode + pub fn contains_by_username(state: &web::Data, _username: &String) -> bool { + // и как это нахуй сократить блять примеров нихуя нет, нихуя не работает + // как меня этот раст заебал уже + state.database.scope(|conn| { + match users + .filter(username.eq(_username)) + .count() + .get_result::(conn) + { + Ok(count) => count > 0, + Err(_) => false, + } + }) } - pub fn contains_by_vk_id(connection: &Mutex, _vk_id: i32) -> bool { - let mut lock = connection.lock().unwrap(); - let con = lock.deref_mut(); - - match users - .filter(vk_id.eq(_vk_id)) - .count() - .get_result::(con) - { - Ok(count) => count > 0, - Err(_) => false, - } + //noinspection DuplicatedCode + //noinspection RsTraitObligations + pub fn contains_by_vk_id(state: &web::Data, _vk_id: i32) -> bool { + state.database.scope(|conn| { + match users + .filter(vk_id.eq(_vk_id)) + .count() + .get_result::(conn) + { + Ok(count) => count > 0, + Err(_) => false, + } + }) } - pub fn insert(connection: &Mutex, user: &User) -> QueryResult { - let mut lock = connection.lock().unwrap(); - let con = lock.deref_mut(); - - insert_into(users).values(user).execute(con) + pub fn insert(state: &web::Data, user: &User) -> QueryResult { + state.database.scope(|conn| insert_into(users).values(user).execute(conn)) } #[cfg(test)] - pub fn delete_by_username(connection: &Mutex, _username: &String) -> bool { - let mut lock = connection.lock().unwrap(); - let con = lock.deref_mut(); - - match diesel::delete(users.filter(username.eq(_username))).execute(con) { - Ok(count) => count > 0, - Err(_) => false, - } + pub fn delete_by_username(state: &web::Data, _username: &String) -> bool { + state.database.scope(|conn| { + match diesel::delete(users.filter(username.eq(_username))).execute(conn) { + Ok(count) => count > 0, + Err(_) => false, + } + }) } #[cfg(test)] - pub fn insert_or_ignore(connection: &Mutex, user: &User) -> QueryResult { - let mut lock = connection.lock().unwrap(); - let con = lock.deref_mut(); - - insert_into(users) - .values(user) - .on_conflict_do_nothing() - .execute(con) + pub fn insert_or_ignore(state: &web::Data, user: &User) -> QueryResult { + state.database.scope(|conn| { + insert_into(users) + .values(user) + .on_conflict_do_nothing() + .execute(conn) + }) } } pub mod fcm { + use crate::app_state::AppState; use crate::database::models::{User, FCM}; + use actix_web::web; use diesel::QueryDsl; use diesel::RunQueryDsl; - use diesel::{BelongingToDsl, PgConnection, QueryResult, SelectableHelper}; - use std::ops::DerefMut; - use std::sync::Mutex; + use diesel::{BelongingToDsl, QueryResult, SelectableHelper}; + use crate::utility::mutex::MutexScope; - pub fn from_user(connection: &Mutex, user: &User) -> QueryResult { - let mut lock = connection.lock().unwrap(); - let con = lock.deref_mut(); - - FCM::belonging_to(&user) - .select(FCM::as_select()) - .get_result(con) + pub fn from_user(state: &web::Data, user: &User) -> QueryResult { + state.database.scope(|conn| { + FCM::belonging_to(&user) + .select(FCM::as_select()) + .get_result(conn) + }) } } diff --git a/src/extractors/authorized_user.rs b/src/extractors/authorized_user.rs index abb32e3..152000b 100644 --- a/src/extractors/authorized_user.rs +++ b/src/extractors/authorized_user.rs @@ -63,7 +63,7 @@ impl FromRequestSync for User { let app_state = req.app_data::>().unwrap(); - driver::users::get(&app_state.database, &user_id).map_err(|_| Error::NoUser.into()) + driver::users::get(&app_state, &user_id).map_err(|_| Error::NoUser.into()) } } @@ -100,7 +100,7 @@ impl FromRequestSync for UserExtractor<{ FCM }> { Ok(Self { fcm: if FCM { - driver::fcm::from_user(&app_state.database, &user).ok() + driver::fcm::from_user(&app_state, &user).ok() } else { None }, diff --git a/src/routes/auth/sign_in.rs b/src/routes/auth/sign_in.rs index b5a71d1..66d8c91 100644 --- a/src/routes/auth/sign_in.rs +++ b/src/routes/auth/sign_in.rs @@ -5,10 +5,10 @@ use crate::routes::auth::shared::parse_vk_id; use crate::routes::auth::sign_in::schema::SignInData::{Default, Vk}; use crate::routes::schema::user::UserResponse; use crate::routes::schema::{IntoResponseAsError, ResponseError}; -use crate::{utility, AppState}; +use crate::utility::mutex::MutexScope; +use crate::{AppState, utility}; use actix_web::{post, web}; use diesel::SaveChangesDsl; -use std::ops::DerefMut; use web::Json; async fn sign_in_combined( @@ -16,8 +16,8 @@ async fn sign_in_combined( app_state: &web::Data, ) -> Result { let user = match &data { - Default(data) => driver::users::get_by_username(&app_state.database, &data.username), - Vk(id) => driver::users::get_by_vk_id(&app_state.database, *id), + Default(data) => driver::users::get_by_username(&app_state, &data.username), + Vk(id) => driver::users::get_by_vk_id(&app_state, *id), }; match user { @@ -35,13 +35,12 @@ async fn sign_in_combined( } } - let mut lock = app_state.connection(); - let conn = lock.deref_mut(); - user.access_token = utility::jwt::encode(&user.id); - user.save_changes::(conn) - .expect("Failed to update user"); + app_state.database.scope(|conn| { + user.save_changes::(conn) + .expect("Failed to update user") + }); Ok(user.into()) } @@ -56,7 +55,9 @@ async fn sign_in_combined( ))] #[post("/sign-in")] pub async fn sign_in(data: Json, app_state: web::Data) -> ServiceResponse { - sign_in_combined(Default(data.into_inner()), &app_state).await.into() + sign_in_combined(Default(data.into_inner()), &app_state) + .await + .into() } #[utoipa::path(responses( @@ -64,7 +65,10 @@ pub async fn sign_in(data: Json, app_state: web::Data) -> Ser (status = NOT_ACCEPTABLE, body = ResponseError) ))] #[post("/sign-in-vk")] -pub async fn sign_in_vk(data_json: Json, app_state: web::Data) -> ServiceResponse { +pub async fn sign_in_vk( + data_json: Json, + app_state: web::Data, +) -> ServiceResponse { let data = data_json.into_inner(); match parse_vk_id(&data.access_token) { @@ -85,7 +89,7 @@ mod schema { /// Имя пользователя #[schema(examples("n08i40k"))] pub username: String, - + /// Пароль pub password: String, } @@ -112,7 +116,7 @@ mod schema { pub enum ErrorCode { /// Некорректное имя пользователя или пароль IncorrectCredentials, - + /// Недействительный токен VK ID InvalidVkAccessToken, } @@ -123,8 +127,8 @@ mod schema { pub enum SignInData { /// Имя пользователя и пароль Default(Request), - - /// Идентификатор привязанного аккаунта VK + + /// Идентификатор привязанного аккаунта VK Vk(i32), } } @@ -176,7 +180,7 @@ mod tests { let app_state = static_app_state(); driver::users::insert_or_ignore( - &app_state.database, + &app_state, &User { id: id.clone(), username, diff --git a/src/routes/auth/sign_up.rs b/src/routes/auth/sign_up.rs index b329fa1..b0d7bcb 100644 --- a/src/routes/auth/sign_up.rs +++ b/src/routes/auth/sign_up.rs @@ -28,19 +28,19 @@ async fn sign_up_combined( } // If user with specified username already exists. - if driver::users::contains_by_username(&app_state.database, &data.username) { + if driver::users::contains_by_username(&app_state, &data.username) { return Err(ErrorCode::UsernameAlreadyExists); } // If user with specified VKID already exists. if let Some(id) = data.vk_id { - if driver::users::contains_by_vk_id(&app_state.database, id) { + if driver::users::contains_by_vk_id(&app_state, id) { return Err(ErrorCode::VkAlreadyExists); } } let user = data.into(); - driver::users::insert(&app_state.database, &user).unwrap(); + driver::users::insert(&app_state, &user).unwrap(); Ok(UserResponse::from(&user)).into() } @@ -281,7 +281,7 @@ mod tests { test_env(); let app_state = static_app_state(); - driver::users::delete_by_username(&app_state.database, &"test::sign_up_valid".to_string()); + driver::users::delete_by_username(&app_state, &"test::sign_up_valid".to_string()); // test @@ -303,7 +303,7 @@ mod tests { let app_state = static_app_state(); driver::users::delete_by_username( - &app_state.database, + &app_state, &"test::sign_up_multiple".to_string(), ); diff --git a/src/routes/fcm/update_callback.rs b/src/routes/fcm/update_callback.rs index 72e126f..4d29770 100644 --- a/src/routes/fcm/update_callback.rs +++ b/src/routes/fcm/update_callback.rs @@ -1,6 +1,7 @@ use crate::app_state::AppState; use crate::database::models::User; use crate::extractors::base::SyncExtractor; +use crate::utility::mutex::MutexScope; use actix_web::{HttpResponse, Responder, post, web}; use diesel::SaveChangesDsl; @@ -18,7 +19,10 @@ async fn update_callback( user.version = version.into_inner(); - match app_state.lock_connection(|con| user.save_changes::(con)) { + match app_state + .database + .scope(|conn| user.save_changes::(conn)) + { Ok(_) => HttpResponse::Ok(), Err(e) => { eprintln!("Failed to update user: {}", e); diff --git a/src/utility/mod.rs b/src/utility/mod.rs index 22ace8a..ca71730 100644 --- a/src/utility/mod.rs +++ b/src/utility/mod.rs @@ -1,3 +1,4 @@ pub mod jwt; pub mod error; -pub mod hasher; \ No newline at end of file +pub mod hasher; +pub mod mutex; \ No newline at end of file diff --git a/src/utility/mutex.rs b/src/utility/mutex.rs new file mode 100644 index 0000000..c0988ad --- /dev/null +++ b/src/utility/mutex.rs @@ -0,0 +1,21 @@ +use std::ops::DerefMut; +use std::sync::Mutex; + +pub trait MutexScope +where + ScopeFn: FnOnce(&mut T) -> ScopeFnOutput, +{ + fn scope(&self, f: ScopeFn) -> ScopeFnOutput; +} + +impl MutexScope for Mutex +where + ScopeFn: FnOnce(&mut T) -> ScopeFnOutput, +{ + fn scope(&self, f: ScopeFn) -> ScopeFnOutput { + let mut lock = self.lock().unwrap(); + let inner = lock.deref_mut(); + + f(inner) + } +} \ No newline at end of file