From a7ea29d8210fafd5fdfbe9f55a6d59e52c4f0807 Mon Sep 17 00:00:00 2001 From: Paul Makles <paulmakles@gmail.com> Date: Tue, 16 Feb 2021 15:25:33 +0000 Subject: [PATCH] Add a way to send messages with attachments. --- .dockerignore | 1 + src/database/entities/autumn.rs | 21 +++++++++++ src/database/entities/message.rs | 3 ++ src/database/entities/mod.rs | 2 ++ src/routes/channels/message_send.rs | 54 +++++++++++++++++++++++++++-- src/routes/root.rs | 8 +++-- src/util/result.rs | 3 ++ src/util/variables.rs | 7 ++-- 8 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 src/database/entities/autumn.rs diff --git a/.dockerignore b/.dockerignore index 796603f..8f7866a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,3 @@ target +.mongo .env \ No newline at end of file diff --git a/src/database/entities/autumn.rs b/src/database/entities/autumn.rs new file mode 100644 index 0000000..7ae1102 --- /dev/null +++ b/src/database/entities/autumn.rs @@ -0,0 +1,21 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "type")] +enum Metadata { + File, + Image { width: isize, height: isize }, + Video { width: isize, height: isize }, + Audio, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct File { + #[serde(rename = "_id")] + pub id: String, + filename: String, + metadata: Metadata, + content_type: String, + + message_id: Option<String> +} diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs index 8594f40..ee26c40 100644 --- a/src/database/entities/message.rs +++ b/src/database/entities/message.rs @@ -19,6 +19,8 @@ pub struct Message { pub content: String, #[serde(skip_serializing_if = "Option::is_none")] + pub attachment: Option<File>, + #[serde(skip_serializing_if = "Option::is_none")] pub edited: Option<DateTime>, } @@ -31,6 +33,7 @@ impl Message { author, content, + attachment: None, edited: None, } } diff --git a/src/database/entities/mod.rs b/src/database/entities/mod.rs index d64512a..864ee9c 100644 --- a/src/database/entities/mod.rs +++ b/src/database/entities/mod.rs @@ -2,8 +2,10 @@ mod channel; mod guild; mod message; mod user; +mod autumn; pub use channel::*; pub use guild::*; pub use message::*; pub use user::*; +pub use autumn::*; diff --git a/src/routes/channels/message_send.rs b/src/routes/channels/message_send.rs index 1a7df55..66c9e94 100644 --- a/src/routes/channels/message_send.rs +++ b/src/routes/channels/message_send.rs @@ -1,7 +1,7 @@ use crate::database::*; use crate::util::result::{Error, Result}; -use mongodb::bson::doc; +use mongodb::{bson::{doc, from_document}, options::FindOneOptions}; use rocket_contrib::json::{Json, JsonValue}; use serde::{Deserialize, Serialize}; use ulid::Ulid; @@ -14,6 +14,8 @@ pub struct Data { // Maximum length of 36 allows both ULIDs and UUIDs. #[validate(length(min = 1, max = 36))] nonce: String, + #[validate(length(min = 1, max = 128))] + attachment: Option<String>, } #[post("/<target>/messages", data = "<message>")] @@ -37,7 +39,9 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal doc! { "nonce": &message.nonce }, - None, + FindOneOptions::builder() + .projection(doc! { "_id": 1 }) + .build() ) .await .map_err(|_| Error::DatabaseError { @@ -49,12 +53,56 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal Err(Error::DuplicateNonce)? } + let id = Ulid::new().to_string(); + let attachments = get_collection("attachments"); + let attachment = if let Some(attachment_id) = &message.attachment { + if let Some(doc) = attachments + .find_one( + doc! { + "_id": attachment_id, + "message_id": { + "$exists": false + } + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "find_one", + with: "attachment", + })? { + let attachment = from_document::<File>(doc) + .map_err(|_| Error::DatabaseError { operation: "from_document", with: "attachment" })?; + + attachments.update_one( + doc! { + "_id": &attachment.id + }, + doc! { + "$set": { + "message_id": &id + } + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { operation: "update_one", with: "attachment" })?; + + Some(attachment) + } else { + return Err(Error::UnknownAttachment) + } + } else { + None + }; + let msg = Message { - id: Ulid::new().to_string(), + id, channel: target.id().to_string(), author: user.id, content: message.content.clone(), + attachment, nonce: Some(message.nonce.clone()), edited: None, }; diff --git a/src/routes/root.rs b/src/routes/root.rs index 70ff4a5..9242caf 100644 --- a/src/routes/root.rs +++ b/src/routes/root.rs @@ -1,5 +1,5 @@ use crate::util::variables::{ - DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY, USE_EMAIL, USE_HCAPTCHA, + DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY, USE_EMAIL, USE_HCAPTCHA, USE_AUTUMN, AUTUMN_URL }; use mongodb::bson::doc; @@ -16,7 +16,11 @@ pub async fn root() -> JsonValue { "key": HCAPTCHA_SITEKEY.to_string() }, "email": *USE_EMAIL, - "invite_only": *INVITE_ONLY + "invite_only": *INVITE_ONLY, + "autumn": { + "enabled": *USE_AUTUMN, + "url": *AUTUMN_URL + } }, "ws": *EXTERNAL_WS_URL, }) diff --git a/src/util/result.rs b/src/util/result.rs index 4a54803..e69c48a 100644 --- a/src/util/result.rs +++ b/src/util/result.rs @@ -36,6 +36,8 @@ pub enum Error { // ? Channel related errors. #[snafu(display("This channel does not exist!"))] UnknownChannel, + #[snafu(display("Attachment does not exist!"))] + UnknownAttachment, #[snafu(display("Cannot edit someone else's message."))] CannotEditMessage, #[snafu(display("Cannot remove yourself from a group, use delete channel instead."))] @@ -84,6 +86,7 @@ impl<'r> Responder<'r, 'static> for Error { Error::NotFriends => Status::Forbidden, Error::UnknownChannel => Status::NotFound, + Error::UnknownAttachment => Status::BadRequest, Error::CannotEditMessage => Status::Forbidden, Error::CannotRemoveYourself => Status::BadRequest, Error::GroupTooLarge { .. } => Status::Forbidden, diff --git a/src/util/variables.rs b/src/util/variables.rs index 00f3d02..17a5084 100644 --- a/src/util/variables.rs +++ b/src/util/variables.rs @@ -7,18 +7,20 @@ lazy_static! { // Application Settings pub static ref MONGO_URI: String = env::var("REVOLT_MONGO_URI").expect("Missing REVOLT_MONGO_URI environment variable."); + pub static ref WS_HOST: String = + env::var("REVOLT_WS_HOST").unwrap_or_else(|_| "0.0.0.0:9000".to_string()); pub static ref PUBLIC_URL: String = env::var("REVOLT_PUBLIC_URL").expect("Missing REVOLT_PUBLIC_URL environment variable."); pub static ref APP_URL: String = env::var("REVOLT_APP_URL").unwrap_or_else(|_| "https://app.revolt.chat".to_string()); pub static ref EXTERNAL_WS_URL: String = env::var("REVOLT_EXTERNAL_WS_URL").expect("Missing REVOLT_EXTERNAL_WS_URL environment variable."); + pub static ref AUTUMN_URL: String = + env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string()); pub static ref HCAPTCHA_KEY: String = env::var("REVOLT_HCAPTCHA_KEY").unwrap_or_else(|_| "0x0000000000000000000000000000000000000000".to_string()); pub static ref HCAPTCHA_SITEKEY: String = env::var("REVOLT_HCAPTCHA_SITEKEY").unwrap_or_else(|_| "10000000-ffff-ffff-ffff-000000000001".to_string()); - pub static ref WS_HOST: String = - env::var("REVOLT_WS_HOST").unwrap_or_else(|_| "0.0.0.0:9000".to_string()); // Application Flags pub static ref DISABLE_REGISTRATION: bool = env::var("REVOLT_DISABLE_REGISTRATION").map_or(false, |v| v == "1"); @@ -32,6 +34,7 @@ lazy_static! { ); pub static ref USE_HCAPTCHA: bool = env::var("REVOLT_HCAPTCHA_KEY").is_ok(); pub static ref USE_PROMETHEUS: bool = env::var("REVOLT_ENABLE_PROMETHEUS").map_or(false, |v| v == "1"); + pub static ref USE_AUTUMN: bool = env::var("AUTUMN_PUBLIC_URL").is_ok(); // SMTP Settings pub static ref SMTP_HOST: String = -- GitLab