From c21d7c46207787398a3939cacaf4fc5151164d3d Mon Sep 17 00:00:00 2001 From: Paul Makles <paulmakles@gmail.com> Date: Tue, 19 Jan 2021 09:02:18 +0000 Subject: [PATCH] Group member add and remove routes. --- src/database/entities/message.rs | 13 ++++ src/database/permissions/channel.rs | 8 ++- src/notifications/events.rs | 52 +--------------- src/routes/channels/group_add_member.rs | 70 ++++++++++++++++++++++ src/routes/channels/group_create.rs | 12 ++-- src/routes/channels/group_remove_member.rs | 69 +++++++++++++++++++++ src/routes/channels/mod.rs | 6 +- src/util/result.rs | 12 ++++ 8 files changed, 184 insertions(+), 58 deletions(-) create mode 100644 src/routes/channels/group_add_member.rs create mode 100644 src/routes/channels/group_remove_member.rs diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs index 6aa692a..ed3bd0f 100644 --- a/src/database/entities/message.rs +++ b/src/database/entities/message.rs @@ -5,6 +5,7 @@ use crate::{ }; use mongodb::bson::{to_bson, DateTime}; use serde::{Deserialize, Serialize}; +use ulid::Ulid; /*#[derive(Serialize, Deserialize, Debug)] pub struct PreviousEntry { @@ -41,6 +42,18 @@ pub struct Message { } impl Message { + pub fn create(author: String, channel: String, content: String) -> Message { + Message { + id: Ulid::new().to_string(), + nonce: None, + channel, + author, + + content, + edited: None, + } + } + pub async fn publish(self) -> Result<()> { get_collection("messages") .insert_one(to_bson(&self).unwrap().as_document().unwrap().clone(), None) diff --git a/src/database/permissions/channel.rs b/src/database/permissions/channel.rs index 41656bd..7c216b0 100644 --- a/src/database/permissions/channel.rs +++ b/src/database/permissions/channel.rs @@ -49,6 +49,12 @@ pub async fn calculate(user: &User, target: &Channel) -> ChannelPermissions<[u32 ChannelPermissions([0]) } - _ => unreachable!(), + Channel::Group { recipients, .. } => { + if recipients.iter().find(|x| *x == &user.id).is_some() { + ChannelPermissions([ChannelPermission::View + ChannelPermission::SendMessage]) + } else { + ChannelPermissions([0]) + } + } } } diff --git a/src/notifications/events.rs b/src/notifications/events.rs index 2d22d85..b408d62 100644 --- a/src/notifications/events.rs +++ b/src/notifications/events.rs @@ -43,68 +43,20 @@ pub enum ClientboundNotification { }, ChannelCreate(Channel), - - /*MessageCreate { - id: String, - nonce: Option<String>, - channel: String, - author: String, - content: String, - }, - - MessageEdit { - id: String, - channel: String, - author: String, - content: String, - }, - - MessageDelete { - id: String, - }, - - GroupUserJoin { - id: String, - user: String, - }, - - GroupUserLeave { + ChannelGroupJoin { id: String, user: String, }, - - GuildUserJoin { + ChannelGroupLeave { id: String, user: String, }, - GuildUserLeave { - id: String, - user: String, - banned: bool, - }, - - GuildChannelCreate { - id: String, - channel: String, - name: String, - description: String, - }, - - GuildChannelDelete { - id: String, - channel: String, - }, - - GuildDelete { - id: String, - },*/ UserRelationship { id: String, user: String, status: RelationshipStatus, }, - UserPresence { id: String, online: bool, diff --git a/src/routes/channels/group_add_member.rs b/src/routes/channels/group_add_member.rs new file mode 100644 index 0000000..677fdd8 --- /dev/null +++ b/src/routes/channels/group_add_member.rs @@ -0,0 +1,70 @@ +use crate::util::result::{Error, Result}; +use crate::util::variables::MAX_GROUP_SIZE; +use crate::{database::*, notifications::events::ClientboundNotification}; + +use mongodb::bson::doc; + +#[put("/<target>/recipients/<member>")] +pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> { + if get_relationship(&user, &member.id) != RelationshipStatus::Friend { + Err(Error::NotFriends)? + } + + let channel = target.fetch_channel().await?; + + let perm = permissions::channel::calculate(&user, &channel).await; + if !perm.get_view() { + Err(Error::LabelMe)? + } + + if let Channel::Group { id, recipients, .. } = channel { + if recipients.len() >= *MAX_GROUP_SIZE { + Err(Error::GroupTooLarge { + max: *MAX_GROUP_SIZE, + })? + } + + if recipients.iter().find(|x| *x == &member.id).is_some() { + Err(Error::AlreadyInGroup)? + } + + get_collection("channels") + .update_one( + doc! { + "_id": &id + }, + doc! { + "$push": { + "recipients": &member.id + } + }, + None, + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "update_one", + with: "channel", + })?; + + ClientboundNotification::ChannelGroupJoin { + id: id.clone(), + user: member.id.clone(), + } + .publish(id.clone()) + .await + .ok(); + + Message::create( + "00000000000000000000000000".to_string(), + id, + format!("<@{}> added <@{}> to the group.", user.id, member.id), + ) + .publish() + .await + .ok(); + + Ok(()) + } else { + Err(Error::InvalidOperation) + } +} diff --git a/src/routes/channels/group_create.rs b/src/routes/channels/group_create.rs index 5dbe89d..54075a6 100644 --- a/src/routes/channels/group_create.rs +++ b/src/routes/channels/group_create.rs @@ -36,6 +36,12 @@ pub async fn req(user: User, info: Json<Data>) -> Result<JsonValue> { })? } + for target in &set { + if get_relationship(&user, target) != RelationshipStatus::Friend { + Err(Error::NotFriends)? + } + } + if get_collection("channels") .find_one( doc! { @@ -53,12 +59,6 @@ pub async fn req(user: User, info: Json<Data>) -> Result<JsonValue> { Err(Error::DuplicateNonce)? } - for target in &set { - if get_relationship(&user, target) != RelationshipStatus::Friend { - Err(Error::NotFriends)? - } - } - let id = Ulid::new().to_string(); let channel = Channel::Group { id, diff --git a/src/routes/channels/group_remove_member.rs b/src/routes/channels/group_remove_member.rs new file mode 100644 index 0000000..438756d --- /dev/null +++ b/src/routes/channels/group_remove_member.rs @@ -0,0 +1,69 @@ +use crate::util::result::{Error, Result}; +use crate::{database::*, notifications::events::ClientboundNotification}; + +use mongodb::bson::doc; + +#[delete("/<target>/recipients/<member>")] +pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> { + if &user.id == &member.id { + Err(Error::CannotRemoveYourself)? + } + + let channel = target.fetch_channel().await?; + + if let Channel::Group { + id, + owner, + recipients, + .. + } = channel + { + if &user.id != &owner { + // figure out if we want to use perm system here + Err(Error::LabelMe)? + } + + if recipients.iter().find(|x| *x == &member.id).is_none() { + Err(Error::NotInGroup)? + } + + get_collection("channels") + .update_one( + doc! { + "_id": &id + }, + doc! { + "$pull": { + "recipients": &member.id + } + }, + None, + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "update_one", + with: "channel", + })?; + + ClientboundNotification::ChannelGroupLeave { + id: id.clone(), + user: member.id.clone(), + } + .publish(id.clone()) + .await + .ok(); + + Message::create( + "00000000000000000000000000".to_string(), + id, + format!("<@{}> removed <@{}> from the group.", user.id, member.id), + ) + .publish() + .await + .ok(); + + Ok(()) + } else { + Err(Error::InvalidOperation) + } +} diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs index 4c9d5a8..ccc1389 100644 --- a/src/routes/channels/mod.rs +++ b/src/routes/channels/mod.rs @@ -2,7 +2,9 @@ use rocket::Route; mod delete_channel; mod fetch_channel; +mod group_add_member; mod group_create; +mod group_remove_member; mod message_delete; mod message_edit; mod message_fetch; @@ -18,6 +20,8 @@ pub fn routes() -> Vec<Route> { message_fetch::req, message_edit::req, message_delete::req, - group_create::req + group_create::req, + group_add_member::req, + group_remove_member::req ] } diff --git a/src/util/result.rs b/src/util/result.rs index 0de30d8..7aa3e03 100644 --- a/src/util/result.rs +++ b/src/util/result.rs @@ -36,8 +36,14 @@ pub enum Error { // ? Channel related errors. #[snafu(display("Cannot edit someone else's message."))] CannotEditMessage, + #[snafu(display("Cannot remove yourself from a group, use delete channel instead."))] + CannotRemoveYourself, #[snafu(display("Group size is too large."))] GroupTooLarge { max: usize }, + #[snafu(display("User already part of group."))] + AlreadyInGroup, + #[snafu(display("User is not part of the group."))] + NotInGroup, // ? General errors. #[snafu(display("Failed to validate fields."))] @@ -49,6 +55,8 @@ pub enum Error { }, #[snafu(display("Internal server error."))] InternalError, + #[snafu(display("Operation cannot be performed on this object."))] + InvalidOperation, #[snafu(display("Already created an object with this nonce."))] DuplicateNonce, #[snafu(display("This request had no effect."))] @@ -74,11 +82,15 @@ impl<'r> Responder<'r, 'static> for Error { Error::NotFriends => Status::Forbidden, Error::CannotEditMessage => Status::Forbidden, + Error::CannotRemoveYourself => Status::BadRequest, Error::GroupTooLarge { .. } => Status::Forbidden, + Error::AlreadyInGroup => Status::Conflict, + Error::NotInGroup => Status::NotFound, Error::FailedValidation { .. } => Status::UnprocessableEntity, Error::DatabaseError { .. } => Status::InternalServerError, Error::InternalError => Status::InternalServerError, + Error::InvalidOperation => Status::BadRequest, Error::DuplicateNonce => Status::Conflict, Error::NoEffect => Status::Ok, }; -- GitLab