diff --git a/src/routes/channels/message_send.rs b/src/routes/channels/message_send.rs new file mode 100644 index 0000000000000000000000000000000000000000..472112d8fd5e8bd0717bf84623a613c256a0942d --- /dev/null +++ b/src/routes/channels/message_send.rs @@ -0,0 +1,57 @@ +use crate::database::*; +use crate::util::result::{Error, Result}; + +use serde::{Serialize, Deserialize}; +use rocket_contrib::json::Json; +use validator::Validate; +use mongodb::bson::doc; +use ulid::Ulid; + +#[derive(Validate, Serialize, Deserialize)] +pub struct Data { + #[validate(length(min = 1, max = 2000))] + content: String, + // Maximum length of 36 allows both ULIDs and UUIDs. + #[validate(length(min = 1, max = 36))] + nonce: String, +} + +#[post("/<target>/messages", data = "<message>")] +pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<()> { + message.validate() + .map_err(|error| Error::FailedValidation { error })?; + + let target = target.fetch_channel().await?; + + let perm = permissions::channel::calculate(&user, &target).await; + if !perm.get_send_message() { + Err(Error::LabelMe)? + } + + if get_collection("messages") + .find_one( + doc! { + "nonce": &message.nonce + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { operation: "find_one", with: "message" })? + .is_some() { + Err(Error::AlreadySentMessage)? + } + + Message { + id: Ulid::new().to_string(), + channel: target.id().to_string(), + author: user.id, + + content: message.content.clone(), + nonce: Some(message.nonce.clone()), + edited: None, + } + .send() + .await?; + + Ok(()) +} diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs index 2f2d3b985e729fccc3a7ab8e1d33b51d6f6a1bf7..1b7135e96def0548023fd2ceacdba7e370fa03d4 100644 --- a/src/routes/channels/mod.rs +++ b/src/routes/channels/mod.rs @@ -2,10 +2,12 @@ use rocket::Route; mod fetch_channel; mod delete_channel; +mod message_send; pub fn routes() -> Vec<Route> { routes![ fetch_channel::req, - delete_channel::req + delete_channel::req, + message_send::req ] } diff --git a/src/util/result.rs b/src/util/result.rs index e707f609ffad0b9f00426a2d1067b844184260d9..2afc5460d9a53c3478c40482030be55b74c11d7a 100644 --- a/src/util/result.rs +++ b/src/util/result.rs @@ -31,6 +31,10 @@ pub enum Error { #[snafu(display("You have been blocked by this user."))] BlockedByOther, + // ? Channel related errors. + #[snafu(display("Already sent a message with this nonce."))] + AlreadySentMessage, + // ? General errors. #[snafu(display("Failed to validate fields."))] FailedValidation { error: ValidationErrors }, @@ -62,6 +66,8 @@ impl<'r> Responder<'r, 'static> for Error { Error::Blocked => Status::Conflict, Error::BlockedByOther => Status::Forbidden, + Error::AlreadySentMessage => Status::Conflict, + Error::FailedValidation { .. } => Status::UnprocessableEntity, Error::DatabaseError { .. } => Status::InternalServerError, Error::InternalError => Status::InternalServerError,