From 02744444346fd0ad535e00f1dd6fb1cfbe37dba6 Mon Sep 17 00:00:00 2001 From: Awiteb Date: Sun, 28 Jul 2024 10:36:05 +0300 Subject: [PATCH 1/7] refactor: Rename `in_chat_requests` table to `incoming_chat` Signed-off-by: Awiteb --- .../{in_chat_requests.rs => incoming_chat.rs} | 19 ++++++-------- crates/oxidetalis/src/database/mod.rs | 4 +-- .../src/websocket/handlers/chat_request.rs | 2 +- ...ming_chat_requests.rs => incoming_chat.rs} | 4 +-- crates/oxidetalis_entities/src/lib.rs | 2 +- crates/oxidetalis_entities/src/prelude.rs | 10 +++---- crates/oxidetalis_entities/src/users.rs | 8 +++--- ...table.rs => create_incoming_chat_table.rs} | 26 +++++++++---------- crates/oxidetalis_migrations/src/lib.rs | 4 +-- 9 files changed, 38 insertions(+), 41 deletions(-) rename crates/oxidetalis/src/database/{in_chat_requests.rs => incoming_chat.rs} (77%) rename crates/oxidetalis_entities/src/{incoming_chat_requests.rs => incoming_chat.rs} (96%) rename crates/oxidetalis_migrations/src/{create_incoming_chat_requests_table.rs => create_incoming_chat_table.rs} (78%) diff --git a/crates/oxidetalis/src/database/in_chat_requests.rs b/crates/oxidetalis/src/database/incoming_chat.rs similarity index 77% rename from crates/oxidetalis/src/database/in_chat_requests.rs rename to crates/oxidetalis/src/database/incoming_chat.rs index 10c150a..2a6dbba 100644 --- a/crates/oxidetalis/src/database/in_chat_requests.rs +++ b/crates/oxidetalis/src/database/incoming_chat.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//! Database extension for the `in_chat_requests` table. +//! Database extension for the `incoming_chat` table. use chrono::Utc; use oxidetalis_core::types::PublicKey; @@ -23,8 +23,8 @@ use sea_orm::{sea_query::OnConflict, DatabaseConnection}; use crate::errors::ServerResult; -/// Extension trait for the `in_chat_requests` table. -pub trait InChatRequestsExt { +/// Extension trait for the `incoming_chat` table. +pub trait IncomingChatExt { /// Save the chat request in the recipient table async fn save_in_chat_request( &self, @@ -33,26 +33,23 @@ pub trait InChatRequestsExt { ) -> ServerResult<()>; } -impl InChatRequestsExt for DatabaseConnection { +impl IncomingChatExt for DatabaseConnection { #[logcall::logcall] async fn save_in_chat_request( &self, sender: &PublicKey, recipient: &UserModel, ) -> ServerResult<()> { - InChatRequestsEntity::insert(InChatRequestsActiveModel { + IncomingChatEntity::insert(IncomingChatActiveModel { recipient_id: Set(recipient.id), sender: Set(*sender), in_on: Set(Utc::now()), ..Default::default() }) .on_conflict( - OnConflict::columns([ - InChatRequestsColumn::RecipientId, - InChatRequestsColumn::Sender, - ]) - .do_nothing() - .to_owned(), + OnConflict::columns([IncomingChatColumn::RecipientId, IncomingChatColumn::Sender]) + .do_nothing() + .to_owned(), ) .exec(self) .await?; diff --git a/crates/oxidetalis/src/database/mod.rs b/crates/oxidetalis/src/database/mod.rs index 8d51890..ec51210 100644 --- a/crates/oxidetalis/src/database/mod.rs +++ b/crates/oxidetalis/src/database/mod.rs @@ -16,12 +16,12 @@ //! Database trait extensions. -mod in_chat_requests; +mod incoming_chat; mod out_chat_requests; mod user; mod user_status; -pub use in_chat_requests::*; +pub use incoming_chat::*; pub use out_chat_requests::*; pub use user::*; pub use user_status::*; diff --git a/crates/oxidetalis/src/websocket/handlers/chat_request.rs b/crates/oxidetalis/src/websocket/handlers/chat_request.rs index 222137a..77d6ade 100644 --- a/crates/oxidetalis/src/websocket/handlers/chat_request.rs +++ b/crates/oxidetalis/src/websocket/handlers/chat_request.rs @@ -20,7 +20,7 @@ use oxidetalis_core::types::PublicKey; use oxidetalis_entities::prelude::*; use sea_orm::DatabaseConnection; -use crate::database::InChatRequestsExt; +use crate::database::IncomingChatExt; use crate::errors::ServerError; use crate::extensions::OnlineUsersExt; use crate::{ diff --git a/crates/oxidetalis_entities/src/incoming_chat_requests.rs b/crates/oxidetalis_entities/src/incoming_chat.rs similarity index 96% rename from crates/oxidetalis_entities/src/incoming_chat_requests.rs rename to crates/oxidetalis_entities/src/incoming_chat.rs index ed6ff3c..e93fd8a 100644 --- a/crates/oxidetalis_entities/src/incoming_chat_requests.rs +++ b/crates/oxidetalis_entities/src/incoming_chat.rs @@ -19,7 +19,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -//! Entity for `in_chat_requests` table +//! Entity for `incoming_chat` table use chrono::Utc; use oxidetalis_core::types::PublicKey; @@ -28,7 +28,7 @@ use sea_orm::entity::prelude::*; use crate::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "in_chat_requests")] +#[sea_orm(table_name = "incoming_chat")] pub struct Model { #[sea_orm(primary_key)] pub id: UserId, diff --git a/crates/oxidetalis_entities/src/lib.rs b/crates/oxidetalis_entities/src/lib.rs index ba83d45..2df1d8e 100644 --- a/crates/oxidetalis_entities/src/lib.rs +++ b/crates/oxidetalis_entities/src/lib.rs @@ -21,7 +21,7 @@ #![doc = include_str!("../README.md")] -pub mod incoming_chat_requests; +pub mod incoming_chat; pub mod outgoing_chat_requests; pub mod prelude; pub mod users; diff --git a/crates/oxidetalis_entities/src/prelude.rs b/crates/oxidetalis_entities/src/prelude.rs index 4bdd679..a4e5d64 100644 --- a/crates/oxidetalis_entities/src/prelude.rs +++ b/crates/oxidetalis_entities/src/prelude.rs @@ -40,11 +40,11 @@ pub use sea_orm::{ /// User ID type pub type UserId = i64; -pub use super::incoming_chat_requests::{ - ActiveModel as InChatRequestsActiveModel, - Column as InChatRequestsColumn, - Entity as InChatRequestsEntity, - Model as InChatRequestsModel, +pub use super::incoming_chat::{ + ActiveModel as IncomingChatActiveModel, + Column as IncomingChatColumn, + Entity as IncomingChatEntity, + Model as IncomingChatModel, }; pub use super::outgoing_chat_requests::{ ActiveModel as OutChatRequestsActiveModel, diff --git a/crates/oxidetalis_entities/src/users.rs b/crates/oxidetalis_entities/src/users.rs index 8489585..b3a0f81 100644 --- a/crates/oxidetalis_entities/src/users.rs +++ b/crates/oxidetalis_entities/src/users.rs @@ -39,17 +39,17 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(has_many = "InChatRequestsEntity")] - InChatRequests, + #[sea_orm(has_many = "IncomingChatEntity")] + IncomingChat, #[sea_orm(has_many = "OutChatRequestsEntity")] OutChatRequests, #[sea_orm(has_many = "UsersStatusEntity")] UsersStatus, } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::InChatRequests.def() + Relation::IncomingChat.def() } } diff --git a/crates/oxidetalis_migrations/src/create_incoming_chat_requests_table.rs b/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs similarity index 78% rename from crates/oxidetalis_migrations/src/create_incoming_chat_requests_table.rs rename to crates/oxidetalis_migrations/src/create_incoming_chat_table.rs index 3cd6fed..0dc03f5 100644 --- a/crates/oxidetalis_migrations/src/create_incoming_chat_requests_table.rs +++ b/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs @@ -19,8 +19,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -//! Migration to create the `in_chat_requests` table, a table for incoming chat -//! requests from other users +//! Migration to create the `incoming_chat` table, a table for incoming chat +//! requests and responses from other users use sea_orm_migration::prelude::*; @@ -35,31 +35,31 @@ impl MigrationTrait for Migration { manager .create_table( Table::create() - .table(InChatRequests::Table) + .table(IncomingChat::Table) .if_not_exists() .col( - ColumnDef::new(InChatRequests::Id) + ColumnDef::new(IncomingChat::Id) .big_integer() .not_null() .auto_increment() .primary_key(), ) .col( - ColumnDef::new(InChatRequests::RecipientId) + ColumnDef::new(IncomingChat::RecipientId) .big_integer() .not_null(), ) .foreign_key( ForeignKey::create() - .name("fk-in_chat_requests-users") - .from(InChatRequests::Table, InChatRequests::RecipientId) + .name("fk-incoming_chat-users") + .from(IncomingChat::Table, IncomingChat::RecipientId) .to(Users::Table, Users::Id) .on_update(ForeignKeyAction::NoAction) .on_delete(ForeignKeyAction::Cascade), ) - .col(ColumnDef::new(InChatRequests::Sender).binary().not_null()) + .col(ColumnDef::new(IncomingChat::Sender).binary().not_null()) .col( - ColumnDef::new(InChatRequests::InOn) + ColumnDef::new(IncomingChat::InOn) .timestamp_with_time_zone() .not_null(), ) @@ -71,9 +71,9 @@ impl MigrationTrait for Migration { Index::create() .if_not_exists() .name("sep_request") - .table(InChatRequests::Table) - .col(InChatRequests::RecipientId) - .col(InChatRequests::Sender) + .table(IncomingChat::Table) + .col(IncomingChat::RecipientId) + .col(IncomingChat::Sender) .unique() .to_owned(), ) @@ -82,7 +82,7 @@ impl MigrationTrait for Migration { } #[derive(DeriveIden)] -enum InChatRequests { +enum IncomingChat { Table, Id, RecipientId, diff --git a/crates/oxidetalis_migrations/src/lib.rs b/crates/oxidetalis_migrations/src/lib.rs index b36dd83..56603d3 100644 --- a/crates/oxidetalis_migrations/src/lib.rs +++ b/crates/oxidetalis_migrations/src/lib.rs @@ -24,7 +24,7 @@ use sea_orm_migration::prelude::*; pub use sea_orm_migration::MigratorTrait; -mod create_incoming_chat_requests_table; +mod create_incoming_chat_table; mod create_outgoing_chat_requests_table; mod create_users_status; mod create_users_table; @@ -36,7 +36,7 @@ impl MigratorTrait for Migrator { fn migrations() -> Vec> { vec![ Box::new(create_users_table::Migration), - Box::new(create_incoming_chat_requests_table::Migration), + Box::new(create_incoming_chat_table::Migration), Box::new(create_outgoing_chat_requests_table::Migration), Box::new(create_users_status::Migration), ] -- 2.45.2 From f0383296dee88533581e1453a59a6e612231a1ec Mon Sep 17 00:00:00 2001 From: Awiteb Date: Sun, 28 Jul 2024 10:40:04 +0300 Subject: [PATCH 2/7] change: Rename `UserId` to `IdCol` and make it private Signed-off-by: Awiteb --- crates/oxidetalis_entities/src/incoming_chat.rs | 4 ++-- crates/oxidetalis_entities/src/outgoing_chat_requests.rs | 4 ++-- crates/oxidetalis_entities/src/prelude.rs | 2 +- crates/oxidetalis_entities/src/users.rs | 2 +- crates/oxidetalis_entities/src/users_status.rs | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/oxidetalis_entities/src/incoming_chat.rs b/crates/oxidetalis_entities/src/incoming_chat.rs index e93fd8a..709bb1a 100644 --- a/crates/oxidetalis_entities/src/incoming_chat.rs +++ b/crates/oxidetalis_entities/src/incoming_chat.rs @@ -31,8 +31,8 @@ use crate::prelude::*; #[sea_orm(table_name = "incoming_chat")] pub struct Model { #[sea_orm(primary_key)] - pub id: UserId, - pub recipient_id: UserId, + pub id: IdCol, + pub recipient_id: IdCol, /// Public key of the sender pub sender: PublicKey, /// The timestamp of the request, when it was received diff --git a/crates/oxidetalis_entities/src/outgoing_chat_requests.rs b/crates/oxidetalis_entities/src/outgoing_chat_requests.rs index 2f5642c..8e00c3b 100644 --- a/crates/oxidetalis_entities/src/outgoing_chat_requests.rs +++ b/crates/oxidetalis_entities/src/outgoing_chat_requests.rs @@ -31,8 +31,8 @@ use crate::prelude::*; #[sea_orm(table_name = "out_chat_requests")] pub struct Model { #[sea_orm(primary_key)] - pub id: UserId, - pub sender_id: UserId, + pub id: IdCol, + pub sender_id: IdCol, /// Public key of the recipient pub recipient: PublicKey, /// The timestamp of the request, when it was sent diff --git a/crates/oxidetalis_entities/src/prelude.rs b/crates/oxidetalis_entities/src/prelude.rs index a4e5d64..d5cb22f 100644 --- a/crates/oxidetalis_entities/src/prelude.rs +++ b/crates/oxidetalis_entities/src/prelude.rs @@ -38,7 +38,7 @@ pub use sea_orm::{ }; /// User ID type -pub type UserId = i64; +pub(crate) type IdCol = i64; pub use super::incoming_chat::{ ActiveModel as IncomingChatActiveModel, diff --git a/crates/oxidetalis_entities/src/users.rs b/crates/oxidetalis_entities/src/users.rs index b3a0f81..1c59bb6 100644 --- a/crates/oxidetalis_entities/src/users.rs +++ b/crates/oxidetalis_entities/src/users.rs @@ -31,7 +31,7 @@ use crate::prelude::*; #[sea_orm(table_name = "users")] pub struct Model { #[sea_orm(primary_key)] - pub id: UserId, + pub id: IdCol, pub public_key: PublicKey, pub last_logout: chrono::DateTime, pub is_admin: bool, diff --git a/crates/oxidetalis_entities/src/users_status.rs b/crates/oxidetalis_entities/src/users_status.rs index 2f66883..0559a6e 100644 --- a/crates/oxidetalis_entities/src/users_status.rs +++ b/crates/oxidetalis_entities/src/users_status.rs @@ -40,8 +40,8 @@ pub enum AccessStatus { #[sea_orm(table_name = "users_status")] pub struct Model { #[sea_orm(primary_key)] - pub id: UserId, - pub user_id: UserId, + pub id: IdCol, + pub user_id: IdCol, pub target: PublicKey, pub status: AccessStatus, pub updated_at: chrono::DateTime, -- 2.45.2 From c8693eeb3ec11ca5fc11621b201c22e314e79b96 Mon Sep 17 00:00:00 2001 From: Awiteb Date: Sun, 28 Jul 2024 10:55:04 +0300 Subject: [PATCH 3/7] feat: Add `accepted_response` col to `incoming_chat` table Signed-off-by: Awiteb --- crates/oxidetalis/src/database/incoming_chat.rs | 10 +++++++--- crates/oxidetalis_entities/src/incoming_chat.rs | 11 +++++++---- .../src/create_incoming_chat_table.rs | 9 +++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/crates/oxidetalis/src/database/incoming_chat.rs b/crates/oxidetalis/src/database/incoming_chat.rs index 2a6dbba..e562199 100644 --- a/crates/oxidetalis/src/database/incoming_chat.rs +++ b/crates/oxidetalis/src/database/incoming_chat.rs @@ -47,9 +47,13 @@ impl IncomingChatExt for DatabaseConnection { ..Default::default() }) .on_conflict( - OnConflict::columns([IncomingChatColumn::RecipientId, IncomingChatColumn::Sender]) - .do_nothing() - .to_owned(), + OnConflict::columns([ + IncomingChatColumn::RecipientId, + IncomingChatColumn::Sender, + IncomingChatColumn::AcceptedResponse, + ]) + .do_nothing() + .to_owned(), ) .exec(self) .await?; diff --git a/crates/oxidetalis_entities/src/incoming_chat.rs b/crates/oxidetalis_entities/src/incoming_chat.rs index 709bb1a..c44393d 100644 --- a/crates/oxidetalis_entities/src/incoming_chat.rs +++ b/crates/oxidetalis_entities/src/incoming_chat.rs @@ -31,12 +31,15 @@ use crate::prelude::*; #[sea_orm(table_name = "incoming_chat")] pub struct Model { #[sea_orm(primary_key)] - pub id: IdCol, - pub recipient_id: IdCol, + pub id: IdCol, + pub recipient_id: IdCol, /// Public key of the sender - pub sender: PublicKey, + pub sender: PublicKey, + /// Whether the chat response accepted or not. + /// This will be `None` if it is chat request, otherwise `bool` + pub accepted_response: Option, /// The timestamp of the request, when it was received - pub in_on: chrono::DateTime, + pub in_on: chrono::DateTime, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs b/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs index 0dc03f5..87b7083 100644 --- a/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs +++ b/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs @@ -58,6 +58,12 @@ impl MigrationTrait for Migration { .on_delete(ForeignKeyAction::Cascade), ) .col(ColumnDef::new(IncomingChat::Sender).binary().not_null()) + .col( + ColumnDef::new(IncomingChat::AcceptedResponse) + .boolean() + .null() + .default(Option::::None), + ) .col( ColumnDef::new(IncomingChat::InOn) .timestamp_with_time_zone() @@ -74,6 +80,7 @@ impl MigrationTrait for Migration { .table(IncomingChat::Table) .col(IncomingChat::RecipientId) .col(IncomingChat::Sender) + .col(IncomingChat::AcceptedResponse) .unique() .to_owned(), ) @@ -88,5 +95,7 @@ enum IncomingChat { RecipientId, /// Public key of the sender Sender, + /// Whether the chat response accepted or not + AcceptedResponse, InOn, } -- 2.45.2 From d1ca5c0a481afe1e8d270d9eb0c9aa48908434c6 Mon Sep 17 00:00:00 2001 From: Awiteb Date: Sun, 28 Jul 2024 11:07:00 +0300 Subject: [PATCH 4/7] change: Rename `in_on` col of `incoming_chat` table to `received_timestamp` Signed-off-by: Awiteb --- crates/oxidetalis/src/database/incoming_chat.rs | 2 +- crates/oxidetalis_entities/src/incoming_chat.rs | 10 +++++----- .../src/create_incoming_chat_table.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/oxidetalis/src/database/incoming_chat.rs b/crates/oxidetalis/src/database/incoming_chat.rs index e562199..17f7a88 100644 --- a/crates/oxidetalis/src/database/incoming_chat.rs +++ b/crates/oxidetalis/src/database/incoming_chat.rs @@ -43,7 +43,7 @@ impl IncomingChatExt for DatabaseConnection { IncomingChatEntity::insert(IncomingChatActiveModel { recipient_id: Set(recipient.id), sender: Set(*sender), - in_on: Set(Utc::now()), + received_timestamp: Set(Utc::now()), ..Default::default() }) .on_conflict( diff --git a/crates/oxidetalis_entities/src/incoming_chat.rs b/crates/oxidetalis_entities/src/incoming_chat.rs index c44393d..ed2d810 100644 --- a/crates/oxidetalis_entities/src/incoming_chat.rs +++ b/crates/oxidetalis_entities/src/incoming_chat.rs @@ -31,15 +31,15 @@ use crate::prelude::*; #[sea_orm(table_name = "incoming_chat")] pub struct Model { #[sea_orm(primary_key)] - pub id: IdCol, - pub recipient_id: IdCol, + pub id: IdCol, + pub recipient_id: IdCol, /// Public key of the sender - pub sender: PublicKey, + pub sender: PublicKey, /// Whether the chat response accepted or not. /// This will be `None` if it is chat request, otherwise `bool` - pub accepted_response: Option, + pub accepted_response: Option, /// The timestamp of the request, when it was received - pub in_on: chrono::DateTime, + pub received_timestamp: chrono::DateTime, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs b/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs index 87b7083..cdab495 100644 --- a/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs +++ b/crates/oxidetalis_migrations/src/create_incoming_chat_table.rs @@ -65,7 +65,7 @@ impl MigrationTrait for Migration { .default(Option::::None), ) .col( - ColumnDef::new(IncomingChat::InOn) + ColumnDef::new(IncomingChat::ReceivedTimestamp) .timestamp_with_time_zone() .not_null(), ) @@ -97,5 +97,5 @@ enum IncomingChat { Sender, /// Whether the chat response accepted or not AcceptedResponse, - InOn, + ReceivedTimestamp, } -- 2.45.2 From 476bffb37b8ed2256512b017f6a5eba2740f5d81 Mon Sep 17 00:00:00 2001 From: Awiteb Date: Sun, 28 Jul 2024 12:03:57 +0300 Subject: [PATCH 5/7] refactor: Refactor chat request and response Signed-off-by: Awiteb --- .../oxidetalis/src/database/incoming_chat.rs | 42 +++++++++-- .../src/websocket/handlers/chat_request.rs | 70 ++++++++++++------- 2 files changed, 81 insertions(+), 31 deletions(-) diff --git a/crates/oxidetalis/src/database/incoming_chat.rs b/crates/oxidetalis/src/database/incoming_chat.rs index 17f7a88..60a3308 100644 --- a/crates/oxidetalis/src/database/incoming_chat.rs +++ b/crates/oxidetalis/src/database/incoming_chat.rs @@ -25,11 +25,18 @@ use crate::errors::ServerResult; /// Extension trait for the `incoming_chat` table. pub trait IncomingChatExt { - /// Save the chat request in the recipient table + /// Save the incoming chat request async fn save_in_chat_request( &self, - requester: &PublicKey, - recipient: &UserModel, + chat_request_recipient: &UserModel, + chat_request_sender: &PublicKey, + ) -> ServerResult<()>; + + /// Remove incoming chat request + async fn remove_in_chat_request( + &self, + chat_request_recipient: &UserModel, + chat_request_sender: &PublicKey, ) -> ServerResult<()>; } @@ -37,12 +44,12 @@ impl IncomingChatExt for DatabaseConnection { #[logcall::logcall] async fn save_in_chat_request( &self, - sender: &PublicKey, - recipient: &UserModel, + chat_request_recipient: &UserModel, + chat_request_sender: &PublicKey, ) -> ServerResult<()> { IncomingChatEntity::insert(IncomingChatActiveModel { - recipient_id: Set(recipient.id), - sender: Set(*sender), + recipient_id: Set(chat_request_recipient.id), + sender: Set(*chat_request_sender), received_timestamp: Set(Utc::now()), ..Default::default() }) @@ -59,4 +66,25 @@ impl IncomingChatExt for DatabaseConnection { .await?; Ok(()) } + + async fn remove_in_chat_request( + &self, + chat_request_recipient: &UserModel, + chat_request_sender: &PublicKey, + ) -> ServerResult<()> { + if let Some(user) = chat_request_recipient + .find_related(IncomingChatEntity) + .filter( + IncomingChatColumn::Sender + .eq(chat_request_sender) + .and(IncomingChatColumn::AcceptedResponse.eq(Option::::None)), + ) + .one(self) + .await? + .map(IntoActiveModel::into_active_model) + { + user.delete(self).await?; + } + Ok(()) + } } diff --git a/crates/oxidetalis/src/websocket/handlers/chat_request.rs b/crates/oxidetalis/src/websocket/handlers/chat_request.rs index 77d6ade..448f481 100644 --- a/crates/oxidetalis/src/websocket/handlers/chat_request.rs +++ b/crates/oxidetalis/src/websocket/handlers/chat_request.rs @@ -33,44 +33,58 @@ use crate::{ #[logcall::logcall] pub async fn handle_chat_request( db: &DatabaseConnection, - from: Option<&UserModel>, - to_public_key: &PublicKey, + chat_request_sender: Option<&UserModel>, + chat_request_recipient: &PublicKey, ) -> Option> { - let Some(from_user) = from else { + let Some(chat_request_sender) = chat_request_sender else { return Some(WsError::RegistredUserEvent.into()); }; - let Some(to_user) = try_ws!(Some db.get_user_by_pubk(to_public_key).await) else { + let Some(chat_request_recipient) = + try_ws!(Some db.get_user_by_pubk(chat_request_recipient).await) + else { return Some(WsError::UserNotFound.into()); }; - if from_user.id == to_user.id { + if chat_request_sender.id == chat_request_recipient.id { return Some(WsError::CannotSendChatRequestToSelf.into()); } - if try_ws!(Some db.get_chat_request_to(from_user, to_public_key).await).is_some() { + if try_ws!(Some db.get_chat_request_to(chat_request_sender, &chat_request_recipient.public_key).await).is_some() { return Some(WsError::AlreadySendChatRequest.into()); } - if try_ws!(Some db.is_blacklisted(&to_user, &from_user.public_key).await) { + if try_ws!(Some db.is_blacklisted(&chat_request_recipient, &chat_request_sender.public_key).await) + { return Some(WsError::RecipientBlacklist.into()); } // To ignore the error if the requester added the recipient to the whitelist // table before send a request to them - if let Err(ServerError::Internal(_)) = db.add_to_whitelist(from_user, to_public_key).await { + if let Err(ServerError::Internal(_)) = db + .add_to_whitelist(chat_request_sender, &chat_request_recipient.public_key) + .await + { return Some(WsError::InternalServerError.into()); } - if try_ws!(Some db.is_whitelisted(&to_user, &from_user.public_key).await) { + if try_ws!(Some db.is_whitelisted(&chat_request_recipient, &chat_request_sender.public_key).await) + { return Some(WsError::AlreadyInRecipientWhitelist.into()); } - try_ws!(Some db.save_out_chat_request(from_user, to_public_key).await); - if let Some(conn_id) = ONLINE_USERS.is_online(to_public_key).await { + try_ws!(Some db.save_out_chat_request(chat_request_sender, &chat_request_recipient.public_key).await); + + if let Some(conn_id) = ONLINE_USERS + .is_online(&chat_request_recipient.public_key) + .await + { ONLINE_USERS - .send(&conn_id, ServerEvent::chat_request(&from_user.public_key)) + .send( + &conn_id, + ServerEvent::chat_request(&chat_request_sender.public_key), + ) .await; } else { - try_ws!(Some db.save_in_chat_request(&from_user.public_key, &to_user).await); + try_ws!(Some db.save_in_chat_request(&chat_request_recipient, &chat_request_sender.public_key).await); } None } @@ -78,22 +92,25 @@ pub async fn handle_chat_request( #[logcall::logcall] pub async fn handle_chat_response( db: &DatabaseConnection, - recipient: Option<&UserModel>, - sender_public_key: &PublicKey, + response_sender: Option<&UserModel>, + response_recipient: &PublicKey, accepted: bool, ) -> Option> { - let Some(recipient_user) = recipient else { + let Some(response_sender) = response_sender else { return Some(WsError::RegistredUserEvent.into()); }; - let Some(sender_user) = try_ws!(Some db.get_user_by_pubk(sender_public_key).await) else { + + let Some(response_recipient) = try_ws!(Some db.get_user_by_pubk(response_recipient).await) + else { return Some(WsError::UserNotFound.into()); }; - if recipient_user.id == sender_user.id { + + if response_sender.id == response_recipient.id { return Some(WsError::CannotRespondToOwnChatRequest.into()); } if try_ws!(Some - db.get_chat_request_to(&sender_user, &recipient_user.public_key) + db.get_chat_request_to(&response_recipient, &response_sender.public_key) .await ) .is_none() @@ -104,21 +121,26 @@ pub async fn handle_chat_response( // We don't need to handle the case where the sender is blacklisted or // whitelisted already, just add it if it is not already there let _ = if accepted { - db.add_to_whitelist(recipient_user, sender_public_key).await + db.add_to_whitelist(response_sender, &response_recipient.public_key) + .await } else { - db.add_to_blacklist(recipient_user, sender_public_key).await + db.add_to_blacklist(response_sender, &response_recipient.public_key) + .await }; try_ws!(Some - db.remove_out_chat_request(&sender_user, &recipient_user.public_key) + db.remove_out_chat_request(&response_recipient, &response_sender.public_key) .await ); + try_ws!(Some + db.remove_in_chat_request(response_sender, &response_recipient.public_key).await + ); - if let Some(conn_id) = ONLINE_USERS.is_online(sender_public_key).await { + if let Some(conn_id) = ONLINE_USERS.is_online(&response_recipient.public_key).await { ONLINE_USERS .send( &conn_id, - ServerEvent::chat_request_response(recipient_user.public_key, accepted), + ServerEvent::chat_request_response(response_sender.public_key, accepted), ) .await; } else { -- 2.45.2 From 1249d9c8bf2b0de2c3ab0e04a9a43a981c5f5ef4 Mon Sep 17 00:00:00 2001 From: Awiteb Date: Mon, 29 Jul 2024 14:01:27 +0300 Subject: [PATCH 6/7] chore: Remove `remove_in_chat_request` trait function Signed-off-by: Awiteb --- .../oxidetalis/src/database/incoming_chat.rs | 27 ------------------- .../src/websocket/handlers/chat_request.rs | 3 --- 2 files changed, 30 deletions(-) diff --git a/crates/oxidetalis/src/database/incoming_chat.rs b/crates/oxidetalis/src/database/incoming_chat.rs index 60a3308..72ebaa0 100644 --- a/crates/oxidetalis/src/database/incoming_chat.rs +++ b/crates/oxidetalis/src/database/incoming_chat.rs @@ -32,12 +32,6 @@ pub trait IncomingChatExt { chat_request_sender: &PublicKey, ) -> ServerResult<()>; - /// Remove incoming chat request - async fn remove_in_chat_request( - &self, - chat_request_recipient: &UserModel, - chat_request_sender: &PublicKey, - ) -> ServerResult<()>; } impl IncomingChatExt for DatabaseConnection { @@ -67,24 +61,3 @@ impl IncomingChatExt for DatabaseConnection { Ok(()) } - async fn remove_in_chat_request( - &self, - chat_request_recipient: &UserModel, - chat_request_sender: &PublicKey, - ) -> ServerResult<()> { - if let Some(user) = chat_request_recipient - .find_related(IncomingChatEntity) - .filter( - IncomingChatColumn::Sender - .eq(chat_request_sender) - .and(IncomingChatColumn::AcceptedResponse.eq(Option::::None)), - ) - .one(self) - .await? - .map(IntoActiveModel::into_active_model) - { - user.delete(self).await?; - } - Ok(()) - } -} diff --git a/crates/oxidetalis/src/websocket/handlers/chat_request.rs b/crates/oxidetalis/src/websocket/handlers/chat_request.rs index 448f481..8a9914b 100644 --- a/crates/oxidetalis/src/websocket/handlers/chat_request.rs +++ b/crates/oxidetalis/src/websocket/handlers/chat_request.rs @@ -132,9 +132,6 @@ pub async fn handle_chat_response( db.remove_out_chat_request(&response_recipient, &response_sender.public_key) .await ); - try_ws!(Some - db.remove_in_chat_request(response_sender, &response_recipient.public_key).await - ); if let Some(conn_id) = ONLINE_USERS.is_online(&response_recipient.public_key).await { ONLINE_USERS -- 2.45.2 From 9218f205df5e55501fd29eb99b1f4ecf82675d18 Mon Sep 17 00:00:00 2001 From: Awiteb Date: Mon, 29 Jul 2024 14:02:58 +0300 Subject: [PATCH 7/7] feat: Send chat requests and responses when the user is online Signed-off-by: Awiteb --- .../oxidetalis/src/database/incoming_chat.rs | 115 +++++++++++++++--- .../src/websocket/handlers/chat_request.rs | 5 +- crates/oxidetalis/src/websocket/mod.rs | 57 ++++++++- 3 files changed, 154 insertions(+), 23 deletions(-) diff --git a/crates/oxidetalis/src/database/incoming_chat.rs b/crates/oxidetalis/src/database/incoming_chat.rs index 72ebaa0..fe0c3f5 100644 --- a/crates/oxidetalis/src/database/incoming_chat.rs +++ b/crates/oxidetalis/src/database/incoming_chat.rs @@ -32,6 +32,25 @@ pub trait IncomingChatExt { chat_request_sender: &PublicKey, ) -> ServerResult<()>; + /// Returns all incoming chat requests for the given recipient + async fn get_all_chat_requests( + &self, + chat_request_recipient: &UserModel, + ) -> ServerResult>; + + /// Save the incoming chat response + async fn save_in_chat_response( + &self, + chat_response_recipient: &UserModel, + chat_response_sender: &PublicKey, + accepted_response: bool, + ) -> ServerResult<()>; + + /// Returns all incoming chat responses for the given recipient + async fn get_all_chat_responses( + &self, + chat_response_recipient: &UserModel, + ) -> ServerResult>; } impl IncomingChatExt for DatabaseConnection { @@ -41,23 +60,83 @@ impl IncomingChatExt for DatabaseConnection { chat_request_recipient: &UserModel, chat_request_sender: &PublicKey, ) -> ServerResult<()> { - IncomingChatEntity::insert(IncomingChatActiveModel { - recipient_id: Set(chat_request_recipient.id), - sender: Set(*chat_request_sender), - received_timestamp: Set(Utc::now()), - ..Default::default() - }) - .on_conflict( - OnConflict::columns([ - IncomingChatColumn::RecipientId, - IncomingChatColumn::Sender, - IncomingChatColumn::AcceptedResponse, - ]) - .do_nothing() - .to_owned(), - ) - .exec(self) - .await?; - Ok(()) + save(self, chat_request_recipient, chat_request_sender, None).await } + async fn get_all_chat_requests( + &self, + chat_request_recipient: &UserModel, + ) -> ServerResult> { + get_all::(self, chat_request_recipient).await + } + + #[logcall::logcall] + async fn save_in_chat_response( + &self, + chat_response_recipient: &UserModel, + chat_response_sender: &PublicKey, + accepted_response: bool, + ) -> ServerResult<()> { + save( + self, + chat_response_recipient, + chat_response_sender, + Some(accepted_response), + ) + .await + } + + async fn get_all_chat_responses( + &self, + chat_response_recipient: &UserModel, + ) -> ServerResult> { + get_all::(self, chat_response_recipient).await + } +} + +/// Utility function to save incoming chat request or response +async fn save( + db: &DatabaseConnection, + recipient: &UserModel, + sender: &PublicKey, + accepted_response: Option, +) -> ServerResult<()> { + IncomingChatEntity::insert(IncomingChatActiveModel { + recipient_id: Set(recipient.id), + sender: Set(*sender), + received_timestamp: Set(Utc::now()), + accepted_response: Set(accepted_response), + ..Default::default() + }) + .on_conflict( + OnConflict::columns([ + IncomingChatColumn::RecipientId, + IncomingChatColumn::Sender, + IncomingChatColumn::AcceptedResponse, + ]) + .do_nothing() + .to_owned(), + ) + .exec(db) + .await?; + Ok(()) +} + +/// Utility function to get all incoming chat requests or responses +async fn get_all( + db: &DatabaseConnection, + recipient: &UserModel, +) -> ServerResult> { + recipient + .find_related(IncomingChatEntity) + .filter( + if IS_REQUEST { + IncomingChatColumn::AcceptedResponse.is_null() + } else { + IncomingChatColumn::AcceptedResponse.is_not_null() + }, + ) + .all(db) + .await + .map_err(Into::into) +} diff --git a/crates/oxidetalis/src/websocket/handlers/chat_request.rs b/crates/oxidetalis/src/websocket/handlers/chat_request.rs index 8a9914b..7957dcf 100644 --- a/crates/oxidetalis/src/websocket/handlers/chat_request.rs +++ b/crates/oxidetalis/src/websocket/handlers/chat_request.rs @@ -141,8 +141,9 @@ pub async fn handle_chat_response( ) .await; } else { - // TODO: Create a table for chat request responses, and send them when - // the user logs in + try_ws!(Some + db.save_in_chat_response(&response_recipient, &response_sender.public_key, accepted).await + ); } None diff --git a/crates/oxidetalis/src/websocket/mod.rs b/crates/oxidetalis/src/websocket/mod.rs index 84b3396..20ea8bb 100644 --- a/crates/oxidetalis/src/websocket/mod.rs +++ b/crates/oxidetalis/src/websocket/mod.rs @@ -45,7 +45,7 @@ pub use events::*; use uuid::Uuid; use crate::{ - database::UserTableExt, + database::{IncomingChatExt, UserTableExt}, extensions::{DepotExt, OnlineUsersExt}, middlewares, nonce::NonceCache, @@ -145,8 +145,9 @@ async fn handle_socket( .await; log::info!("New user connected: ConnId(={conn_id}) PublicKey(={user_public_key})"); - // TODO: Send the incoming chat request to the user, while they are offline. - // This after adding last_login col to the user table + if let Some(server_user) = &user { + send_chat_requests_and_responses(&db_conn, &user_shared_secret, &sender, server_user).await; + } while let Some(Ok(msg)) = user_ws_receiver.next().await { match handle_ws_msg(msg, &nonce_cache, &user_shared_secret).await { @@ -177,6 +178,56 @@ async fn handle_socket( user_disconnected(&db_conn, &conn_id, &user_public_key, user).await; } +/// Send the incoming chat requests and responses to the user while they were +/// offline +/// +/// ### Note +/// The errors are ignored, if there is an issue with user connection, the user +/// will be disconnected after this function is called. +/// +/// Also we make sure that the user receives the chat requests and responses +/// before we delete them from the database. +async fn send_chat_requests_and_responses( + db_conn: &DatabaseConnection, + user_shared_secret: &[u8; 32], + sender: &mpsc::UnboundedSender>, + server_user: &UserModel, +) { + let Ok(requests) = db_conn.get_all_chat_requests(server_user).await else { + return; + }; + let Ok(responses) = db_conn.get_all_chat_responses(server_user).await else { + return; + }; + + for request in requests { + if sender + .unbounded_send(Ok(ServerEvent::chat_request(&request.sender) + .sign(user_shared_secret) + .as_ref() + .into())) + .is_ok() + { + let _ = request.delete(db_conn).await; + } + } + + for response in responses { + if sender + .unbounded_send(Ok(ServerEvent::chat_request_response( + response.sender, + response.accepted_response.unwrap_or_default(), + ) + .sign(user_shared_secret) + .as_ref() + .into())) + .is_ok() + { + let _ = response.delete(db_conn).await; + } + } +} + /// Handle websocket msg async fn handle_ws_msg( msg: Message, -- 2.45.2