From accd6d7789639d2cf250904e6658d6639c3a5cbf Mon Sep 17 00:00:00 2001
From: Paul Makles <paulmakles@gmail.com>
Date: Mon, 18 Jan 2021 20:54:08 +0000
Subject: [PATCH] Add edit message route.

---
 src/database/entities/message.rs    | 20 ++++++++++
 src/database/guards/reference.rs    |  2 +-
 src/database/guards/user.rs         |  2 +-
 src/database/migrations/scripts.rs  |  2 +-
 src/notifications/events.rs         |  2 +
 src/notifications/payload.rs        |  2 +-
 src/routes/channels/message_edit.rs | 58 +++++++++++++++++++++++++++++
 src/routes/channels/mod.rs          |  4 +-
 src/util/result.rs                  |  3 ++
 9 files changed, 90 insertions(+), 5 deletions(-)
 create mode 100644 src/routes/channels/message_edit.rs

diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs
index 8cec6ce..13975de 100644
--- a/src/database/entities/message.rs
+++ b/src/database/entities/message.rs
@@ -58,4 +58,24 @@ impl Message {
 
         Ok(())
     }
+
+    pub async fn publish_edit(self) -> Result<()> {
+        let channel = self.channel.clone();
+        ClientboundNotification::MessageEdit(self)
+            .publish(channel)
+            .await
+            .ok();
+
+        Ok(())
+    }
+
+    pub async fn publish_delete(self) -> Result<()> {
+        let channel = self.channel.clone();
+        ClientboundNotification::MessageDelete(self.id)
+            .publish(channel)
+            .await
+            .ok();
+
+        Ok(())
+    }
 }
diff --git a/src/database/guards/reference.rs b/src/database/guards/reference.rs
index a1262c3..829ff24 100644
--- a/src/database/guards/reference.rs
+++ b/src/database/guards/reference.rs
@@ -1,7 +1,7 @@
 use crate::database::*;
 use crate::util::result::{Error, Result};
 
-use mongodb::bson::{doc, from_bson, from_document, Bson};
+use mongodb::bson::{doc, from_document};
 use rocket::http::RawStr;
 use rocket::request::FromParam;
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
diff --git a/src/database/guards/user.rs b/src/database/guards/user.rs
index c7363d1..fb8e53d 100644
--- a/src/database/guards/user.rs
+++ b/src/database/guards/user.rs
@@ -1,6 +1,6 @@
 use crate::database::*;
 
-use mongodb::bson::{doc, from_bson, from_document, Bson};
+use mongodb::bson::{doc, from_document};
 use rauth::auth::Session;
 use rocket::http::Status;
 use rocket::request::{self, FromRequest, Outcome, Request};
diff --git a/src/database/migrations/scripts.rs b/src/database/migrations/scripts.rs
index 64866c0..4f57000 100644
--- a/src/database/migrations/scripts.rs
+++ b/src/database/migrations/scripts.rs
@@ -2,7 +2,7 @@ use super::super::{get_collection, get_db};
 
 use crate::rocket::futures::StreamExt;
 use log::info;
-use mongodb::bson::{doc, from_bson, from_document, Bson};
+use mongodb::bson::{doc, from_document};
 use mongodb::options::FindOptions;
 use serde::{Deserialize, Serialize};
 
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index eeb03a9..cde3946 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -37,6 +37,8 @@ pub enum ClientboundNotification {
     },
 
     Message(Message),
+    MessageEdit(Message),
+    MessageDelete(String),
 
     /*MessageCreate {
         id: String,
diff --git a/src/notifications/payload.rs b/src/notifications/payload.rs
index c3b2559..32ae010 100644
--- a/src/notifications/payload.rs
+++ b/src/notifications/payload.rs
@@ -5,7 +5,7 @@ use crate::{
 };
 use futures::StreamExt;
 use mongodb::{
-    bson::{doc, from_bson, from_document, Bson},
+    bson::{doc, from_document},
     options::FindOptions,
 };
 
diff --git a/src/routes/channels/message_edit.rs b/src/routes/channels/message_edit.rs
new file mode 100644
index 0000000..ccc2f8a
--- /dev/null
+++ b/src/routes/channels/message_edit.rs
@@ -0,0 +1,58 @@
+use crate::database::*;
+use crate::util::result::{Error, Result};
+
+use chrono::Utc;
+use mongodb::bson::{Bson, DateTime, doc};
+use rocket_contrib::json::Json;
+use serde::{Deserialize, Serialize};
+use validator::Validate;
+
+#[derive(Validate, Serialize, Deserialize)]
+pub struct Data {
+    #[validate(length(min = 1, max = 2000))]
+    content: String,
+}
+
+#[patch("/<target>/messages/<msg>", data = "<edit>")]
+pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<()> {
+    edit.validate()
+        .map_err(|error| Error::FailedValidation { error })?;
+
+    let channel = target.fetch_channel().await?;
+
+    let perm = permissions::channel::calculate(&user, &channel).await;
+    if !perm.get_view() {
+        Err(Error::LabelMe)?
+    }
+
+    let mut message = msg.fetch_message().await?;
+    if message.author != user.id {
+        Err(Error::CannotEditMessage)?
+    }
+
+    let edited = Utc::now();
+    get_collection("messages")
+        .update_one(
+            doc! {
+                "_id": &message.id
+            },
+            doc! {
+                "$set": {
+                    "content": &edit.content,
+                    "edited": Bson::DateTime(edited)
+                }
+            },
+            None,
+        )
+        .await
+        .map_err(|_| Error::DatabaseError {
+            operation: "update_one",
+            with: "message",
+        })?;
+
+    message.content = edit.content.clone();
+    message.edited = Some(DateTime(edited));
+    message.publish_edit().await?;
+
+    Ok(())
+}
diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs
index fbd9e7d..f888557 100644
--- a/src/routes/channels/mod.rs
+++ b/src/routes/channels/mod.rs
@@ -2,6 +2,7 @@ use rocket::Route;
 
 mod delete_channel;
 mod fetch_channel;
+mod message_edit;
 mod message_fetch;
 mod message_query;
 mod message_send;
@@ -12,6 +13,7 @@ pub fn routes() -> Vec<Route> {
         delete_channel::req,
         message_send::req,
         message_query::req,
-        message_fetch::req
+        message_fetch::req,
+        message_edit::req
     ]
 }
diff --git a/src/util/result.rs b/src/util/result.rs
index 2afc546..0f2dec3 100644
--- a/src/util/result.rs
+++ b/src/util/result.rs
@@ -34,6 +34,8 @@ pub enum Error {
     // ? Channel related errors.
     #[snafu(display("Already sent a message with this nonce."))]
     AlreadySentMessage,
+    #[snafu(display("Cannot edit someone else's message."))]
+    CannotEditMessage,
 
     // ? General errors.
     #[snafu(display("Failed to validate fields."))]
@@ -67,6 +69,7 @@ impl<'r> Responder<'r, 'static> for Error {
             Error::BlockedByOther => Status::Forbidden,
 
             Error::AlreadySentMessage => Status::Conflict,
+            Error::CannotEditMessage => Status::Forbidden,
 
             Error::FailedValidation { .. } => Status::UnprocessableEntity,
             Error::DatabaseError { .. } => Status::InternalServerError,
-- 
GitLab