From 8956400e44a4217257d45610996ef39115671729 Mon Sep 17 00:00:00 2001 From: Paul Makles <paulmakles@gmail.com> Date: Mon, 10 Aug 2020 18:53:24 +0200 Subject: [PATCH] Add caching for members, rewrite part of perms. --- src/database/channel.rs | 4 +- src/database/guild.rs | 116 +++++++++++++++++++++++---- src/database/permissions.rs | 46 +++++------ src/guards/guild.rs | 153 ------------------------------------ src/notifications/mod.rs | 2 +- src/routes/guild.rs | 42 ++++++---- 6 files changed, 154 insertions(+), 209 deletions(-) delete mode 100644 src/guards/guild.rs diff --git a/src/database/channel.rs b/src/database/channel.rs index a7a8b3e..2ec7822 100644 --- a/src/database/channel.rs +++ b/src/database/channel.rs @@ -179,8 +179,8 @@ pub fn process_event(event: &Notification) { owner: None, guild: Some(ev.id.clone()), name: Some(ev.name.clone()), - description: Some(ev.description.clone()) - } + description: Some(ev.description.clone()), + }, ); } Notification::guild_channel_delete(ev) => { diff --git a/src/database/guild.rs b/src/database/guild.rs index b5968ad..7f1e763 100644 --- a/src/database/guild.rs +++ b/src/database/guild.rs @@ -48,9 +48,14 @@ pub struct Guild { pub default_permissions: u32, } +#[derive(Hash, Eq, PartialEq)] +pub struct MemberKey(pub String, pub String); + lazy_static! { static ref CACHE: Arc<Mutex<LruCache<String, Guild>>> = Arc::new(Mutex::new(LruCache::new(4_000_000))); + static ref MEMBER_CACHE: Arc<Mutex<LruCache<MemberKey, Member>>> = + Arc::new(Mutex::new(LruCache::new(4_000_000))); } pub fn fetch_guild(id: &str) -> Result<Option<Guild>, String> { @@ -85,37 +90,57 @@ pub fn fetch_guild(id: &str) -> Result<Option<Guild>, String> { } } -impl<'r> FromParam<'r> for Guild { - type Error = &'r RawStr; +pub fn fetch_member(key: MemberKey) -> Result<Option<Member>, String> { + { + if let Ok(mut cache) = MEMBER_CACHE.lock() { + let existing = cache.get(&key); - fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { - if let Ok(result) = fetch_guild(param) { - if let Some(channel) = result { - Ok(channel) - } else { - Err(param) + if let Some(member) = existing { + return Ok(Some((*member).clone())); } } else { - Err(param) + return Err("Failed to lock cache.".to_string()); } } -} -pub fn get_member(guild_id: &String, member: &String) -> Option<Member> { - if let Ok(result) = get_collection("members").find_one( + let col = get_collection("members"); + if let Ok(result) = col.find_one( doc! { - "_id.guild": &guild_id, - "_id.user": &member, + "_id.guild": &key.0, + "_id.user": &key.1, }, None, ) { if let Some(doc) = result { - Some(from_bson(Bson::Document(doc)).expect("Failed to unwrap member.")) + if let Ok(member) = from_bson(Bson::Document(doc)) as Result<Member, _> { + let mut cache = MEMBER_CACHE.lock().unwrap(); + cache.put(key, member.clone()); + + Ok(Some(member)) + } else { + Err("Failed to deserialize member!".to_string()) + } } else { - None + Ok(None) } } else { - None + Err("Failed to fetch member from database.".to_string()) + } +} + +impl<'r> FromParam<'r> for Guild { + type Error = &'r RawStr; + + fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { + if let Ok(result) = fetch_guild(param) { + if let Some(channel) = result { + Ok(channel) + } else { + Err(param) + } + } else { + Err(param) + } } } @@ -176,3 +201,60 @@ pub fn get_invite<U: Into<Option<String>>>( None } } + +use crate::notifications::events::Notification; + +pub fn process_event(event: &Notification) { + match event { + Notification::guild_channel_create(ev) => {} // ? for later use + Notification::guild_channel_create(ev) => {} // ? for later use + Notification::guild_delete(ev) => {} + Notification::guild_user_join(ev) => {} + Notification::guild_user_leave(ev) => {} + /*Notification::group_user_join(ev) => { + let mut cache = CACHE.lock().unwrap(); + let entry = cache.pop(&ev.id); + + if entry.is_some() { + let mut channel = entry.unwrap(); + channel.recipients.as_mut().unwrap().push(ev.user.clone()); + cache.put(ev.id.clone(), channel); + } + } + Notification::group_user_leave(ev) => { + let mut cache = CACHE.lock().unwrap(); + let entry = cache.pop(&ev.id); + + if entry.is_some() { + let mut channel = entry.unwrap(); + let recipients = channel.recipients.as_mut().unwrap(); + if let Some(pos) = recipients.iter().position(|x| *x == ev.user) { + recipients.remove(pos); + } + cache.put(ev.id.clone(), channel); + } + } + Notification::guild_channel_create(ev) => { + let mut cache = CACHE.lock().unwrap(); + cache.put( + ev.id.clone(), + Channel { + id: ev.channel.clone(), + channel_type: 2, + active: None, + last_message: None, + recipients: None, + owner: None, + guild: Some(ev.id.clone()), + name: Some(ev.name.clone()), + description: Some(ev.description.clone()) + } + ); + } + Notification::guild_channel_delete(ev) => { + let mut cache = CACHE.lock().unwrap(); + cache.pop(&ev.channel); + }*/ + _ => {} + } +} diff --git a/src/database/permissions.rs b/src/database/permissions.rs index 7c553e0..37d35f7 100644 --- a/src/database/permissions.rs +++ b/src/database/permissions.rs @@ -1,6 +1,6 @@ use super::mutual::has_mutual_connection; use crate::database::channel::Channel; -use crate::database::guild::{fetch_guild, get_member, Guild, Member}; +use crate::database::guild::{fetch_guild, fetch_member, Guild, Member, MemberKey}; use crate::database::user::UserRelationship; use crate::guards::auth::UserRef; @@ -140,7 +140,9 @@ impl PermissionCalculator { }; if let Some(guild) = &guild { - self.member = get_member(&guild.id, &self.user.id); + if let Ok(result) = fetch_member(MemberKey(guild.id.clone(), self.user.id.clone())) { + self.member = result; + } } self.guild = guild; @@ -164,32 +166,32 @@ impl PermissionCalculator { match channel.channel_type { 0 => { if let Some(arr) = &channel.recipients { - let mut other_user = ""; + let mut other_user = None; for item in arr { if item != &self.user.id { - other_user = item; + other_user = Some(item); } } - // ? In this case, it is a "self DM". - if other_user == "" { - return 1024 + 128 + 32 + 16 + 1; - } - - let relationships = self.user.fetch_relationships(); - let relationship = - get_relationship_internal(&self.user.id, &other_user, &relationships); - - if relationship == Relationship::Friend { - permissions = 1024 + 128 + 32 + 16 + 1; - } else if relationship == Relationship::Blocked - || relationship == Relationship::BlockedOther - { - permissions = 1; - } else if has_mutual_connection(&self.user.id, other_user, true) { - permissions = 1024 + 128 + 32 + 16 + 1; + if let Some(other) = other_user { + let relationships = self.user.fetch_relationships(); + let relationship = + get_relationship_internal(&self.user.id, &other, &relationships); + + if relationship == Relationship::Friend { + permissions = 1024 + 128 + 32 + 16 + 1; + } else if relationship == Relationship::Blocked + || relationship == Relationship::BlockedOther + { + permissions = 1; + } else if has_mutual_connection(&self.user.id, other, true) { + permissions = 1024 + 128 + 32 + 16 + 1; + } else { + permissions = 1; + } } else { - permissions = 1; + // ? In this case, it is a "self DM". + return 1024 + 128 + 32 + 16 + 1; } } } diff --git a/src/guards/guild.rs b/src/guards/guild.rs deleted file mode 100644 index 51e3707..0000000 --- a/src/guards/guild.rs +++ /dev/null @@ -1,153 +0,0 @@ -use mongodb::bson::{doc, from_bson, Bson, Document}; -use mongodb::options::FindOneOptions; -use rocket::http::RawStr; -use rocket::request::FromParam; -use serde::{Deserialize, Serialize}; - -use crate::database; -use crate::database::guild::{Ban, Invite, Member}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct GuildRef { - #[serde(rename = "_id")] - pub id: String, - pub name: String, - pub description: String, - pub owner: String, - - pub bans: Vec<Ban>, - - pub default_permissions: i32, -} - -impl GuildRef { - pub fn from(id: String) -> Option<GuildRef> { - match database::get_collection("guilds").find_one( - doc! { "_id": id }, - FindOneOptions::builder() - .projection(doc! { - "name": 1, - "description": 1, - "owner": 1, - "bans": 1, - "default_permissions": 1 - }) - .build(), - ) { - Ok(result) => match result { - Some(doc) => { - Some(from_bson(mongodb::bson::mongodb::bson::Document(doc)).expect("Failed to unwrap guild.")) - } - None => None, - }, - Err(_) => None, - } - } - - pub fn fetch_data(&self, projection: Document) -> Option<Document> { - database::get_collection("guilds") - .find_one( - doc! { "_id": &self.id }, - FindOneOptions::builder().projection(projection).build(), - ) - .expect("Failed to fetch guild from database.") - } - - pub fn fetch_data_given(&self, mut filter: Document, projection: Document) -> Option<Document> { - filter.insert("_id", self.id.clone()); - database::get_collection("guilds") - .find_one( - filter, - FindOneOptions::builder().projection(projection).build(), - ) - .expect("Failed to fetch guild from database.") - } -} - -impl<'r> FromParam<'r> for GuildRef { - type Error = &'r RawStr; - - fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { - if let Some(guild) = GuildRef::from(param.to_string()) { - Ok(guild) - } else { - Err(param) - } - } -} - -pub fn get_member(guild_id: &String, member: &String) -> Option<Member> { - if let Ok(result) = database::get_collection("members").find_one( - doc! { - "_id.guild": &guild_id, - "_id.user": &member, - }, - None, - ) { - if let Some(doc) = result { - Some(from_bson(mongodb::bson::Document(doc)).expect("Failed to unwrap member.")) - } else { - None - } - } else { - None - } -} - -pub fn get_invite<U: Into<Option<String>>>( - code: &String, - user: U, -) -> Option<(String, String, Invite)> { - let mut doc = doc! { - "invites": { - "$elemMatch": { - "code": &code - } - } - }; - - if let Some(user_id) = user.into() { - doc.insert( - "bans", - doc! { - "$not": { - "$elemMatch": { - "id": user_id - } - } - }, - ); - } - - if let Ok(result) = database::get_collection("guilds").find_one( - doc, - FindOneOptions::builder() - .projection(doc! { - "_id": 1, - "name": 1, - "invites.$": 1, - }) - .build(), - ) { - if let Some(doc) = result { - let invite = doc - .get_array("invites") - .unwrap() - .iter() - .next() - .unwrap() - .as_document() - .unwrap(); - - Some(( - doc.get_str("_id").unwrap().to_string(), - doc.get_str("name").unwrap().to_string(), - from_bson(mongodb::bson::Document(invite.clone())).unwrap(), - )) - } else { - None - } - } else { - None - } -} diff --git a/src/notifications/mod.rs b/src/notifications/mod.rs index 897dec0..abc7723 100644 --- a/src/notifications/mod.rs +++ b/src/notifications/mod.rs @@ -18,7 +18,7 @@ pub fn send_message<U: Into<Option<Vec<String>>>, G: Into<Option<String>>>( let guild = guild.into(); data.push_to_cache(); - + if pubsub::send_message(users.clone(), guild.clone(), data.clone()) { state::send_message(users, guild, data.serialize()); diff --git a/src/routes/guild.rs b/src/routes/guild.rs index 52f5b4a..7726943 100644 --- a/src/routes/guild.rs +++ b/src/routes/guild.rs @@ -1,6 +1,6 @@ use super::channel::ChannelType; use super::Response; -use crate::database::guild::{get_invite, get_member, Guild}; +use crate::database::guild::{fetch_member as get_member, get_invite, Guild, MemberKey}; use crate::database::{ self, channel::fetch_channel, channel::Channel, Permission, PermissionCalculator, }; @@ -641,11 +641,17 @@ pub fn fetch_members(user: UserRef, target: Guild) -> Option<Response> { pub fn fetch_member(user: UserRef, target: Guild, other: String) -> Option<Response> { with_permissions!(user, target); - if let Some(member) = get_member(&target.id, &other) { - Some(Response::Success(json!({ - "id": member.id.user, - "nickname": member.nickname, - }))) + if let Ok(result) = get_member(MemberKey(target.id, user.id)) { + if let Some(member) = result { + Some(Response::Success(json!({ + "id": member.id.user, + "nickname": member.nickname, + }))) + } else { + Some(Response::NotFound( + json!({ "error": "Member does not exist!" }), + )) + } } else { Some(Response::InternalServerError( json!({ "error": "Failed to fetch member or user does not exist." }), @@ -668,10 +674,14 @@ pub fn kick_member(user: UserRef, target: Guild, other: String) -> Option<Respon return Some(Response::LackingPermission(Permission::KickMembers)); } - if get_member(&target.id, &other).is_none() { - return Some(Response::BadRequest( - json!({ "error": "User not part of guild." }), - )); + if let Ok(result) = get_member(MemberKey( target.id.clone(), other.clone() )) { + if result.is_none() { + return Some(Response::BadRequest( + json!({ "error": "User not part of guild." }), + )); + } + } else { + return Some(Response::InternalServerError(json!({ "error": "Failed to fetch member." }))) } if database::get_collection("members") @@ -734,10 +744,14 @@ pub fn ban_member( return Some(Response::LackingPermission(Permission::BanMembers)); } - if get_member(&target.id, &other).is_none() { - return Some(Response::BadRequest( - json!({ "error": "User not part of guild." }), - )); + if let Ok(result) = get_member(MemberKey( target.id.clone(), other.clone() )) { + if result.is_none() { + return Some(Response::BadRequest( + json!({ "error": "User not part of guild." }), + )); + } + } else { + return Some(Response::InternalServerError(json!({ "error": "Failed to fetch member." }))) } if database::get_collection("guilds") -- GitLab