diff --git a/src/bin/dummy.rs b/src/bin/dummy.rs index e71fdf55421d043f171eba8c32329338498cad17..f328e4d9d04c31d0d70d16d21a07d1613be9d577 100644 --- a/src/bin/dummy.rs +++ b/src/bin/dummy.rs @@ -1 +1 @@ -fn main() {} \ No newline at end of file +fn main() {} diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs index 7f61b70c79b2f4f0830180f4711e99a07d61c806..704d743c67cca3b59f10ad342a080cdd516078de 100644 --- a/src/database/entities/channel.rs +++ b/src/database/entities/channel.rs @@ -44,7 +44,7 @@ pub enum Channel { owner: String, description: String, recipients: Vec<String>, - + #[serde(skip_serializing_if = "Option::is_none")] icon: Option<File>, #[serde(skip_serializing_if = "Option::is_none")] @@ -93,8 +93,7 @@ impl Channel { })?; let channel_id = self.id().to_string(); - ClientboundNotification::ChannelCreate(self) - .publish(channel_id); + ClientboundNotification::ChannelCreate(self).publish(channel_id); Ok(()) } @@ -104,7 +103,7 @@ impl Channel { ClientboundNotification::ChannelUpdate { id: id.clone(), data, - clear: None + clear: None, } .publish(id); @@ -188,9 +187,8 @@ impl Channel { with: "channel", })?; - ClientboundNotification::ChannelDelete { id: id.to_string() } - .publish(id.to_string()); - + ClientboundNotification::ChannelDelete { id: id.to_string() }.publish(id.to_string()); + if let Channel::Group { icon, .. } = self { if let Some(attachment) = icon { attachment.delete().await?; diff --git a/src/database/entities/january.rs b/src/database/entities/january.rs index c530b75e550a20f57bac9474f1ba57e2b6d00789..cb6f9ffb54a7d3953c501a4f3b340849cf5d737b 100644 --- a/src/database/entities/january.rs +++ b/src/database/entities/january.rs @@ -1,6 +1,9 @@ -use serde::{Serialize, Deserialize}; +use crate::util::{ + result::{Error, Result}, + variables::JANUARY_URL, +}; use linkify::{LinkFinder, LinkKind}; -use crate::util::{result::{Error, Result}, variables::JANUARY_URL}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] pub enum ImageSize { @@ -33,7 +36,7 @@ pub enum TwitchType { #[derive(Serialize, Deserialize, Debug, Clone)] pub enum BandcampType { Album, - Track + Track, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -54,8 +57,8 @@ pub enum Special { Soundcloud, Bandcamp { content_type: BandcampType, - id: String - } + id: String, + }, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -98,7 +101,7 @@ impl Embed { // Ignore quoted lines. if let Some(c) = v.chars().next() { if c == '>' { - return "" + return ""; } } @@ -131,12 +134,10 @@ impl Embed { Err(_) => return Err(Error::LabelMe), Ok(result) => match result.status() { reqwest::StatusCode::OK => { - let res: Embed = result.json() - .await - .map_err(|_| Error::InvalidOperation)?; + let res: Embed = result.json().await.map_err(|_| Error::InvalidOperation)?; - Ok(vec![ res ]) - }, + Ok(vec![res]) + } _ => return Err(Error::LabelMe), }, } diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs index 5e23d4154c1d3966117c558f032f7f6637402e87..cca5b28eeed9f04b2bfea24356864175c959aa64 100644 --- a/src/database/entities/message.rs +++ b/src/database/entities/message.rs @@ -135,9 +135,7 @@ impl Message { self.process_embed(); let enc = serde_json::to_string(&self).unwrap(); - ClientboundNotification::Message(self) - .publish(channel.id().to_string()); - + ClientboundNotification::Message(self).publish(channel.id().to_string()); /* Web Push Test Code @@ -202,9 +200,11 @@ impl Message { for subscription in subscriptions { let mut builder = WebPushMessageBuilder::new(&subscription).unwrap(); - let sig_builder = - VapidSignatureBuilder::from_pem(std::io::Cursor::new(&key), &subscription) - .unwrap(); + let sig_builder = VapidSignatureBuilder::from_pem( + std::io::Cursor::new(&key), + &subscription, + ) + .unwrap(); let signature = sig_builder.build().unwrap(); builder.set_vapid_signature(signature); builder.set_payload(ContentEncoding::AesGcm, enc.as_bytes()); @@ -250,12 +250,11 @@ impl Message { }, None, ) - .await { + .await + { ClientboundNotification::MessageUpdate { id, - data: json!({ - "embeds": embeds - }), + data: json!({ "embeds": embeds }), } .publish(channel); } diff --git a/src/database/entities/mod.rs b/src/database/entities/mod.rs index 49ea91b1583669c0ef55ee39d45b329172b33ac7..d384f5d08b0ea8c073e5ffcc7b1490e671bfa0a8 100644 --- a/src/database/entities/mod.rs +++ b/src/database/entities/mod.rs @@ -1,13 +1,13 @@ -mod server; mod autumn; -mod january; mod channel; +mod january; mod message; +mod server; mod user; -pub use january::*; pub use autumn::*; pub use channel::*; -pub use server::*; +pub use january::*; pub use message::*; +pub use server::*; pub use user::*; diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs index 96db17d3f1143077eae064a7bc2a0f359a62e28a..48befa4db17d28bf9e42e1b06f0d262e2a7c5b35 100644 --- a/src/database/entities/user.rs +++ b/src/database/entities/user.rs @@ -1,12 +1,16 @@ -use mongodb::bson::doc; +use futures::StreamExt; use mongodb::options::{Collation, FindOneOptions}; +use mongodb::{ + bson::{doc, from_document}, + options::FindOptions, +}; use serde::{Deserialize, Serialize}; +use validator::Validate; use crate::database::permissions::user::UserPermissions; use crate::database::*; use crate::notifications::websocket::is_online; use crate::util::result::{Error, Result}; -use validator::Validate; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub enum RelationshipStatus { @@ -160,4 +164,46 @@ impl User { Ok(false) } } + + /// Utility function for fetching multiple users from the perspective of one. + pub async fn fetch_multiple_users(&self, user_ids: Vec<String>) -> Result<Vec<User>> { + let mut users = vec![]; + let mut cursor = get_collection("users") + .find( + doc! { + "_id": { + "$in": user_ids + } + }, + FindOptions::builder() + .projection( + doc! { "_id": 1, "username": 1, "avatar": 1, "badges": 1, "status": 1 }, + ) + .build(), + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "find", + with: "users", + })?; + + while let Some(result) = cursor.next().await { + if let Ok(doc) = result { + let other: User = from_document(doc).map_err(|_| Error::DatabaseError { + operation: "from_document", + with: "user", + })?; + + let permissions = PermissionCalculator::new(&self) + .with_mutual_connection() + .with_user(&other) + .for_user_given() + .await?; + + users.push(other.from(&self).with(permissions)); + } + } + + Ok(users) + } } diff --git a/src/main.rs b/src/main.rs index 386e5316d989406f99b607bf8c960ade140d66f3..fdbe4dfadf11a709ffcc0127a017a4c04fc85c45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,9 +15,9 @@ extern crate ctrlc; pub mod database; pub mod notifications; -pub mod version; pub mod routes; pub mod util; +pub mod version; use async_std::task; use chrono::Duration; @@ -40,7 +40,10 @@ async fn main() { dotenv::dotenv().ok(); env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "info")); - info!("Starting REVOLT server [version {}].", crate::version::VERSION); + info!( + "Starting REVOLT server [version {}].", + crate::version::VERSION + ); util::variables::preflight_checks(); database::connect().await; diff --git a/src/notifications/events.rs b/src/notifications/events.rs index 9366f16f60a68582eaba3d5a8a75feaa7b292f12..327130b426021778710daf66c5671ba6a0beaf5d 100644 --- a/src/notifications/events.rs +++ b/src/notifications/events.rs @@ -35,12 +35,12 @@ pub enum RemoveUserField { ProfileContent, ProfileBackground, StatusText, - Avatar + Avatar, } #[derive(Serialize, Deserialize, Debug)] pub enum RemoveChannelField { - Icon + Icon, } #[derive(Serialize, Deserialize, Debug)] @@ -67,7 +67,7 @@ pub enum ClientboundNotification { id: String, data: JsonValue, #[serde(skip_serializing_if = "Option::is_none")] - clear: Option<RemoveChannelField> + clear: Option<RemoveChannelField>, }, ChannelGroupJoin { id: String, @@ -93,7 +93,7 @@ pub enum ClientboundNotification { id: String, data: JsonValue, #[serde(skip_serializing_if = "Option::is_none")] - clear: Option<RemoveUserField> + clear: Option<RemoveUserField>, }, UserRelationship { id: String, @@ -110,7 +110,9 @@ impl ClientboundNotification { pub fn publish(self, topic: String) { async_std::task::spawn(async move { prehandle_hook(&self); // ! TODO: this should be moved to pubsub - hive_pubsub::backend::mongo::publish(get_hive(), &topic, self).await.ok(); + hive_pubsub::backend::mongo::publish(get_hive(), &topic, self) + .await + .ok(); }); } } diff --git a/src/notifications/payload.rs b/src/notifications/payload.rs index bd36e7a8aee1d5ade8b9e41098ef47932a1f638d..12e49b59402ea0c9dd182b4699157c3a74fe7ee6 100644 --- a/src/notifications/payload.rs +++ b/src/notifications/payload.rs @@ -6,13 +6,9 @@ use crate::{ util::result::{Error, Result}, }; use futures::StreamExt; -use mongodb::{ - bson::{doc, from_document}, - options::FindOptions, -}; +use mongodb::bson::{doc, from_document}; pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> { - let mut users = vec![]; let mut user_ids: HashSet<String> = HashSet::new(); if let Some(relationships) = &user.relations { @@ -68,41 +64,12 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> { } user_ids.remove(&user.id); - if user_ids.len() > 0 { - let mut cursor = get_collection("users") - .find( - doc! { - "_id": { - "$in": user_ids.into_iter().collect::<Vec<String>>() - } - }, - FindOptions::builder() - .projection(doc! { "_id": 1, "username": 1, "avatar": 1, "badges": 1, "status": 1 }) - .build(), - ) - .await - .map_err(|_| Error::DatabaseError { - operation: "find", - with: "users", - })?; - - while let Some(result) = cursor.next().await { - if let Ok(doc) = result { - let other: User = from_document(doc).map_err(|_| Error::DatabaseError { - operation: "from_document", - with: "user", - })?; - - let permissions = PermissionCalculator::new(&user) - .with_mutual_connection() - .with_user(&other) - .for_user_given() - .await?; - - users.push(other.from(&user).with(permissions)); - } - } - } + let mut users = if user_ids.len() > 0 { + user.fetch_multiple_users(user_ids.into_iter().collect::<Vec<String>>()) + .await? + } else { + vec![] + }; user.relationship = Some(RelationshipStatus::User); user.online = Some(true); diff --git a/src/routes/channels/edit_channel.rs b/src/routes/channels/edit_channel.rs index d5f114ea03ee64a7273bd8d4fac17bad90a62fdd..7c4f037cca2d353d5d2b9e8af3fb85bb7bcae28a 100644 --- a/src/routes/channels/edit_channel.rs +++ b/src/routes/channels/edit_channel.rs @@ -1,6 +1,6 @@ -use crate::{database::*, notifications::events::RemoveChannelField}; use crate::notifications::events::ClientboundNotification; use crate::util::result::{Error, Result}; +use crate::{database::*, notifications::events::RemoveChannelField}; use mongodb::bson::{doc, to_document}; use rocket_contrib::json::Json; @@ -17,7 +17,7 @@ pub struct Data { description: Option<String>, #[validate(length(min = 1, max = 128))] icon: Option<String>, - remove: Option<RemoveChannelField> + remove: Option<RemoveChannelField>, } #[patch("/<target>", data = "<data>")] @@ -26,8 +26,12 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { data.validate() .map_err(|error| Error::FailedValidation { error })?; - if data.name.is_none() && data.description.is_none() && data.icon.is_none() && data.remove.is_none() { - return Ok(()) + if data.name.is_none() + && data.description.is_none() + && data.icon.is_none() + && data.remove.is_none() + { + return Ok(()); } let target = target.fetch_channel().await?; @@ -64,7 +68,8 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { } if let Some(attachment_id) = &data.icon { - let attachment = File::find_and_use(&attachment_id, "icons", "object", &user.id).await?; + let attachment = + File::find_and_use(&attachment_id, "icons", "object", &user.id).await?; set.insert( "icon", to_document(&attachment).map_err(|_| Error::DatabaseError { @@ -72,7 +77,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { with: "attachment", })?, ); - + remove_icon = true; } @@ -80,26 +85,25 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { if set.len() > 0 { operations.insert("$set", &set); } - + if unset.len() > 0 { operations.insert("$unset", unset); } if operations.len() > 0 { get_collection("channels") - .update_one( - doc! { "_id": &id }, - operations, - None - ) - .await - .map_err(|_| Error::DatabaseError { operation: "update_one", with: "channel" })?; + .update_one(doc! { "_id": &id }, operations, None) + .await + .map_err(|_| Error::DatabaseError { + operation: "update_one", + with: "channel", + })?; } ClientboundNotification::ChannelUpdate { id: id.clone(), data: json!(set), - clear: data.remove + clear: data.remove, } .publish(id.clone()); @@ -107,10 +111,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { Message::create( "00000000000000000000000000".to_string(), id.clone(), - Content::SystemMessage(SystemMessage::ChannelRenamed { - name, - by: user.id, - }), + Content::SystemMessage(SystemMessage::ChannelRenamed { name, by: user.id }), ) .publish(&target) .await diff --git a/src/routes/channels/fetch_members.rs b/src/routes/channels/fetch_members.rs new file mode 100644 index 0000000000000000000000000000000000000000..4f42e695095c097d0d60f89f64624921ca4b325c --- /dev/null +++ b/src/routes/channels/fetch_members.rs @@ -0,0 +1,23 @@ +use crate::database::*; +use crate::util::result::{Error, Result}; + +use rocket_contrib::json::JsonValue; + +#[get("/<target>/members")] +pub async fn req(user: User, target: Ref) -> Result<JsonValue> { + let target = target.fetch_channel().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_channel(&target) + .for_channel() + .await?; + if !perm.get_view() { + Err(Error::MissingPermission)? + } + + if let Channel::Group { recipients, .. } = target { + Ok(json!(user.fetch_multiple_users(recipients).await?)) + } else { + Err(Error::InvalidOperation) + } +} diff --git a/src/routes/channels/message_query.rs b/src/routes/channels/message_query.rs index bed171c0d4d6fed5f48817823d27c6d5ec4c8179..903d811e36cf8e065d5fc109154d64fa1f053b81 100644 --- a/src/routes/channels/message_query.rs +++ b/src/routes/channels/message_query.rs @@ -14,7 +14,7 @@ use validator::Validate; #[derive(Serialize, Deserialize, FromFormValue)] pub enum Sort { Latest, - Oldest + Oldest, } #[derive(Validate, Serialize, Deserialize, FromForm)] @@ -25,7 +25,7 @@ pub struct Options { before: Option<String>, #[validate(length(min = 26, max = 26))] after: Option<String>, - sort: Option<Sort> + sort: Option<Sort>, } #[get("/<target>/messages?<options..>")] @@ -54,7 +54,11 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json query.insert("_id", doc! { "$gt": after }); } - let sort = if let Sort::Latest = options.sort.as_ref().unwrap_or_else(|| &Sort::Latest) { -1 } else { 1 }; + let sort = if let Sort::Latest = options.sort.as_ref().unwrap_or_else(|| &Sort::Latest) { + -1 + } else { + 1 + }; let mut cursor = get_collection("messages") .find( query, diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs index 365474c334b185ce499deb43fed23d8d2929c44d..732f31d8a973b449917de3f36051cdbcefbcdf11 100644 --- a/src/routes/channels/mod.rs +++ b/src/routes/channels/mod.rs @@ -3,6 +3,7 @@ use rocket::Route; mod delete_channel; mod edit_channel; mod fetch_channel; +mod fetch_members; mod group_add_member; mod group_create; mod group_remove_member; @@ -17,6 +18,7 @@ mod message_send; pub fn routes() -> Vec<Route> { routes![ fetch_channel::req, + fetch_members::req, delete_channel::req, edit_channel::req, message_send::req, diff --git a/src/routes/users/add_friend.rs b/src/routes/users/add_friend.rs index ad0a494c27787d7375b1808c167d83b98d1c8a45..b2c8697096779251f5ab0fd89b910d19e08326df 100644 --- a/src/routes/users/add_friend.rs +++ b/src/routes/users/add_friend.rs @@ -77,14 +77,14 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> { ClientboundNotification::UserRelationship { id: user.id.clone(), user: target_user, - status: RelationshipStatus::Friend + status: RelationshipStatus::Friend, } .publish(user.id.clone()); ClientboundNotification::UserRelationship { id: target_id.to_string(), user, - status: RelationshipStatus::Friend + status: RelationshipStatus::Friend, } .publish(target_id.to_string()); @@ -134,18 +134,18 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> { let user = user .from_override(&target_user, RelationshipStatus::Incoming) .await?; - + ClientboundNotification::UserRelationship { id: user.id.clone(), user: target_user, - status: RelationshipStatus::Outgoing + status: RelationshipStatus::Outgoing, } .publish(user.id.clone()); - + ClientboundNotification::UserRelationship { id: target_id.to_string(), user, - status: RelationshipStatus::Incoming + status: RelationshipStatus::Incoming, } .publish(target_id.to_string()); diff --git a/src/routes/users/block_user.rs b/src/routes/users/block_user.rs index 6e06e0e127827a1eb9359dd41e1446253f57846d..a786eb6cc5132ac6d85854020d8e821e02b498ec 100644 --- a/src/routes/users/block_user.rs +++ b/src/routes/users/block_user.rs @@ -75,24 +75,24 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ) { Ok(_) => { let target = target - .from_override(&user, RelationshipStatus::Friend) + .from_override(&user, RelationshipStatus::Blocked) .await?; let user = user - .from_override(&target, RelationshipStatus::Friend) + .from_override(&target, RelationshipStatus::BlockedOther) .await?; let target_id = target.id.clone(); ClientboundNotification::UserRelationship { id: user.id.clone(), user: target, - status: RelationshipStatus::Blocked + status: RelationshipStatus::Blocked, } .publish(user.id.clone()); ClientboundNotification::UserRelationship { id: target_id.clone(), user, - status: RelationshipStatus::BlockedOther + status: RelationshipStatus::BlockedOther, } .publish(target_id); @@ -145,14 +145,14 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ClientboundNotification::UserRelationship { id: user.id.clone(), user: target, - status: RelationshipStatus::Blocked + status: RelationshipStatus::Blocked, } .publish(user.id.clone()); - + ClientboundNotification::UserRelationship { id: target_id.clone(), user, - status: RelationshipStatus::BlockedOther + status: RelationshipStatus::BlockedOther, } .publish(target_id); diff --git a/src/routes/users/change_username.rs b/src/routes/users/change_username.rs index 461818a3e49a8a9e1928b80699d6ed124417bb54..9d8d8604b93b92dd82c1ece1dbe8ba4f97551d4b 100644 --- a/src/routes/users/change_username.rs +++ b/src/routes/users/change_username.rs @@ -58,7 +58,7 @@ pub async fn req( ClientboundNotification::UserUpdate { id: user.id.clone(), data: json!(data.0), - clear: None + clear: None, } .publish(user.id.clone()); diff --git a/src/routes/users/edit_user.rs b/src/routes/users/edit_user.rs index 2f1197eda7bb4c0f93fe0524ac3369df2690981c..9c89b2d66a79e1c70da464fbc70a8e63cd14820d 100644 --- a/src/routes/users/edit_user.rs +++ b/src/routes/users/edit_user.rs @@ -1,6 +1,6 @@ -use crate::{database::*, notifications::events::RemoveUserField}; use crate::notifications::events::ClientboundNotification; use crate::util::result::{Error, Result}; +use crate::{database::*, notifications::events::RemoveUserField}; use mongodb::bson::{doc, to_document}; use rocket_contrib::json::Json; @@ -25,7 +25,7 @@ pub struct Data { profile: Option<UserProfileData>, #[validate(length(min = 1, max = 128))] avatar: Option<String>, - remove: Option<RemoveUserField> + remove: Option<RemoveUserField>, } #[patch("/<_ignore_id>", data = "<data>")] @@ -35,8 +35,12 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> data.validate() .map_err(|error| Error::FailedValidation { error })?; - if data.status.is_none() && data.profile.is_none() && data.avatar.is_none() && data.remove.is_none() { - return Ok(()) + if data.status.is_none() + && data.profile.is_none() + && data.avatar.is_none() + && data.remove.is_none() + { + return Ok(()); } let mut unset = doc! {}; @@ -49,14 +53,14 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> match remove { RemoveUserField::ProfileContent => { unset.insert("profile.content", 1); - }, + } RemoveUserField::ProfileBackground => { unset.insert("profile.background", 1); remove_background = true; } RemoveUserField::StatusText => { unset.insert("status.text", 1); - }, + } RemoveUserField::Avatar => { unset.insert("avatar", 1); remove_avatar = true; @@ -80,7 +84,8 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> } if let Some(attachment_id) = profile.background { - let attachment = File::find_and_use(&attachment_id, "backgrounds", "user", &user.id).await?; + let attachment = + File::find_and_use(&attachment_id, "backgrounds", "user", &user.id).await?; set.insert( "profile.background", to_document(&attachment).map_err(|_| Error::DatabaseError { @@ -133,7 +138,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> ClientboundNotification::UserUpdate { id: user.id.clone(), data: json!({ "status": status }), - clear: None + clear: None, } .publish(user.id.clone()); } @@ -142,7 +147,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> ClientboundNotification::UserUpdate { id: user.id.clone(), data: json!({ "avatar": avatar }), - clear: None + clear: None, } .publish(user.id.clone()); } @@ -151,7 +156,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> ClientboundNotification::UserUpdate { id: user.id.clone(), data: json!({}), - clear: Some(clear) + clear: Some(clear), } .publish(user.id.clone()); } diff --git a/src/routes/users/remove_friend.rs b/src/routes/users/remove_friend.rs index 5426cf74f4250d37d7a51ae81a85f1882923760d..847e99cec92bf34e4d2d6638a97a45a737b01e8b 100644 --- a/src/routes/users/remove_friend.rs +++ b/src/routes/users/remove_friend.rs @@ -56,14 +56,14 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ClientboundNotification::UserRelationship { id: user.id.clone(), user: target, - status: RelationshipStatus::None + status: RelationshipStatus::None, } .publish(user.id.clone()); ClientboundNotification::UserRelationship { id: target_id.clone(), user, - status: RelationshipStatus::None + status: RelationshipStatus::None, } .publish(target_id); diff --git a/src/routes/users/unblock_user.rs b/src/routes/users/unblock_user.rs index 4e2e59c0e5b61220b451cedc5994da7f64dd26e1..6f94b1819a2ebbae6f894a345269cb72bbb8ef53 100644 --- a/src/routes/users/unblock_user.rs +++ b/src/routes/users/unblock_user.rs @@ -85,14 +85,14 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ClientboundNotification::UserRelationship { id: user.id.clone(), user: target, - status: RelationshipStatus::None + status: RelationshipStatus::None, } .publish(user.id.clone()); - + ClientboundNotification::UserRelationship { id: target_id.clone(), user: user, - status: RelationshipStatus::None + status: RelationshipStatus::None, } .publish(target_id);