From 6253a91276e567f72f716b8c2420d275dd558443 Mon Sep 17 00:00:00 2001 From: Paul Makles <paulmakles@gmail.com> Date: Mon, 28 Dec 2020 15:21:11 +0000 Subject: [PATCH] Move all entities to their own folder. --- src/database/channel.rs | 217 -------------------- src/database/entities/channel.rs | 31 +++ src/database/entities/guild.rs | 43 ++++ src/database/entities/message.rs | 22 ++ src/database/entities/mod.rs | 4 + src/database/entities/user.rs | 15 ++ src/database/guards/mod.rs | 19 ++ src/database/guild.rs | 341 ------------------------------- src/database/message.rs | 120 ----------- src/database/mod.rs | 10 +- src/database/mutual.rs | 130 ------------ src/database/permissions.rs | 228 --------------------- src/database/user.rs | 307 ---------------------------- 13 files changed, 135 insertions(+), 1352 deletions(-) delete mode 100644 src/database/channel.rs create mode 100644 src/database/entities/channel.rs create mode 100644 src/database/entities/guild.rs create mode 100644 src/database/entities/message.rs create mode 100644 src/database/entities/mod.rs create mode 100644 src/database/entities/user.rs create mode 100644 src/database/guards/mod.rs delete mode 100644 src/database/guild.rs delete mode 100644 src/database/message.rs delete mode 100644 src/database/mutual.rs delete mode 100644 src/database/permissions.rs delete mode 100644 src/database/user.rs diff --git a/src/database/channel.rs b/src/database/channel.rs deleted file mode 100644 index a1bfaa1..0000000 --- a/src/database/channel.rs +++ /dev/null @@ -1,217 +0,0 @@ -use super::get_collection; - -use lru::LruCache; -use mongodb::bson::{doc, from_bson, Bson}; -use rocket::http::RawStr; -use rocket::request::FromParam; -use rocket_contrib::json::JsonValue; -use serde::{Deserialize, Serialize}; -use std::sync::{Arc, Mutex}; -use rocket::futures::StreamExt; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct LastMessage { - // message id - id: String, - // author's id - user_id: String, - // truncated content with author's name prepended (for GDM / GUILD) - short_content: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Channel { - #[serde(rename = "_id")] - pub id: String, - #[serde(rename = "type")] - pub channel_type: u8, - - // DM: whether the DM is active - pub active: Option<bool>, - // DM + GDM: last message in channel - pub last_message: Option<LastMessage>, - // DM + GDM: recipients for channel - pub recipients: Option<Vec<String>>, - // GDM: owner of group - pub owner: Option<String>, - // GUILD: channel parent - pub guild: Option<String>, - // GUILD + GDM: channel name - pub name: Option<String>, - // GUILD + GDM: channel description - pub description: Option<String>, -} - -impl Channel { - pub fn serialise(self) -> JsonValue { - match self.channel_type { - 0 => json!({ - "id": self.id, - "type": self.channel_type, - "last_message": self.last_message, - "recipients": self.recipients, - }), - 1 => json!({ - "id": self.id, - "type": self.channel_type, - "last_message": self.last_message, - "recipients": self.recipients, - "name": self.name, - "owner": self.owner, - "description": self.description, - }), - 2 => json!({ - "id": self.id, - "type": self.channel_type, - "guild": self.guild, - "name": self.name, - "description": self.description, - }), - _ => unreachable!(), - } - } -} - -lazy_static! { - static ref CACHE: Arc<Mutex<LruCache<String, Channel>>> = - Arc::new(Mutex::new(LruCache::new(4_000_000))); -} - -pub async fn fetch_channel(id: &str) -> Result<Option<Channel>, String> { - { - if let Ok(mut cache) = CACHE.lock() { - let existing = cache.get(&id.to_string()); - - if let Some(channel) = existing { - return Ok(Some((*channel).clone())); - } - } else { - return Err("Failed to lock cache.".to_string()); - } - } - - let col = get_collection("channels"); - if let Ok(result) = col.find_one(doc! { "_id": id }, None).await { - if let Some(doc) = result { - if let Ok(channel) = from_bson(Bson::Document(doc)) as Result<Channel, _> { - let mut cache = CACHE.lock().unwrap(); - cache.put(id.to_string(), channel.clone()); - - Ok(Some(channel)) - } else { - Err("Failed to deserialize channel!".to_string()) - } - } else { - Ok(None) - } - } else { - Err("Failed to fetch channel from database.".to_string()) - } -} - -pub async fn fetch_channels(ids: &Vec<String>) -> Result<Vec<Channel>, String> { - let mut missing = vec![]; - let mut channels = vec![]; - - { - if let Ok(mut cache) = CACHE.lock() { - for id in ids { - let existing = cache.get(id); - - if let Some(channel) = existing { - channels.push((*channel).clone()); - } else { - missing.push(id); - } - } - } else { - return Err("Failed to lock cache.".to_string()); - } - } - - if missing.len() == 0 { - return Ok(channels); - } - - let col = get_collection("channels"); - if let Ok(mut result) = col.find(doc! { "_id": { "$in": missing } }, None).await { - while let Some(item) = result.next().await { - let mut cache = CACHE.lock().unwrap(); - if let Ok(doc) = item { - if let Ok(channel) = from_bson(Bson::Document(doc)) as Result<Channel, _> { - cache.put(channel.id.clone(), channel.clone()); - channels.push(channel); - } else { - return Err("Failed to deserialize channel!".to_string()); - } - } else { - return Err("Failed to fetch channel.".to_string()); - } - } - - Ok(channels) - } else { - Err("Failed to fetch channel from database.".to_string()) - } -} - -impl<'r> FromParam<'r> for Channel { - type Error = &'r RawStr; - - fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { - Err(param) - /*if let Ok(result) = fetch_channel(param).await { - if let Some(channel) = result { - Ok(channel) - } else { - Err(param) - } - } else { - Err(param) - }*/ - } -} - -/*use crate::notifications::events::Notification; - -pub fn process_event(event: &Notification) { - match event { - Notification::group_user_join(ev) => { - let mut cache = CACHE.lock().unwrap(); - if let Some(channel) = cache.peek_mut(&ev.id) { - channel.recipients.as_mut().unwrap().push(ev.user.clone()); - } - } - Notification::group_user_leave(ev) => { - let mut cache = CACHE.lock().unwrap(); - if let Some(channel) = cache.peek_mut(&ev.id) { - let recipients = channel.recipients.as_mut().unwrap(); - if let Some(pos) = recipients.iter().position(|x| *x == ev.user) { - recipients.remove(pos); - } - } - } - 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/entities/channel.rs b/src/database/entities/channel.rs new file mode 100644 index 0000000..413b7f1 --- /dev/null +++ b/src/database/entities/channel.rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct LastMessage { + id: String, + user_id: String, + short_content: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Channel { + #[serde(rename = "_id")] + pub id: String, + #[serde(rename = "type")] + pub channel_type: u8, + + // DM: whether the DM is active + pub active: Option<bool>, + // DM + GDM: last message in channel + pub last_message: Option<LastMessage>, + // DM + GDM: recipients for channel + pub recipients: Option<Vec<String>>, + // GDM: owner of group + pub owner: Option<String>, + // GUILD: channel parent + pub guild: Option<String>, + // GUILD + GDM: channel name + pub name: Option<String>, + // GUILD + GDM: channel description + pub description: Option<String>, +} diff --git a/src/database/entities/guild.rs b/src/database/entities/guild.rs new file mode 100644 index 0000000..ee5a54e --- /dev/null +++ b/src/database/entities/guild.rs @@ -0,0 +1,43 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct MemberCompositeKey { + pub guild: String, + pub user: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Member { + #[serde(rename = "_id")] + pub id: MemberCompositeKey, + pub nickname: Option<String>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Invite { + pub code: String, + pub creator: String, + pub channel: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Ban { + pub id: String, + pub reason: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Guild { + #[serde(rename = "_id")] + pub id: String, + // pub nonce: String, used internally + pub name: String, + pub description: String, + pub owner: String, + + pub channels: Vec<String>, + pub invites: Vec<Invite>, + pub bans: Vec<Ban>, + + pub default_permissions: u32, +} diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs new file mode 100644 index 0000000..9f8c233 --- /dev/null +++ b/src/database/entities/message.rs @@ -0,0 +1,22 @@ +use mongodb::bson::DateTime; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct PreviousEntry { + pub content: String, + pub time: DateTime, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Message { + #[serde(rename = "_id")] + pub id: String, + pub nonce: Option<String>, + pub channel: String, + pub author: String, + + pub content: String, + pub edited: Option<DateTime>, + + pub previous_content: Vec<PreviousEntry>, +} diff --git a/src/database/entities/mod.rs b/src/database/entities/mod.rs new file mode 100644 index 0000000..4ad9863 --- /dev/null +++ b/src/database/entities/mod.rs @@ -0,0 +1,4 @@ +pub mod channel; +pub mod message; +pub mod guild; +pub mod user; \ No newline at end of file diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs new file mode 100644 index 0000000..2e44ed6 --- /dev/null +++ b/src/database/entities/user.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Relationship { + pub id: String, + pub status: u8, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct User { + #[serde(rename = "_id")] + pub id: String, + pub username: Option<String>, + pub relations: Option<Vec<Relationship>>, +} diff --git a/src/database/guards/mod.rs b/src/database/guards/mod.rs new file mode 100644 index 0000000..e308776 --- /dev/null +++ b/src/database/guards/mod.rs @@ -0,0 +1,19 @@ +/** +// ! FIXME +impl<'r> FromParam<'r> for User { + type Error = &'r RawStr; + + fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { + Err(param) + /*if let Ok(result) = fetch_channel(param).await { + if let Some(channel) = result { + Ok(channel) + } else { + Err(param) + } + } else { + Err(param) + }*/ + } +} +*/ diff --git a/src/database/guild.rs b/src/database/guild.rs deleted file mode 100644 index b8243f3..0000000 --- a/src/database/guild.rs +++ /dev/null @@ -1,341 +0,0 @@ -use super::channel::fetch_channels; -use super::get_collection; - -use lru::LruCache; -use rocket::futures::StreamExt; -use mongodb::bson::{doc, from_bson, Bson}; -use rocket::http::RawStr; -use rocket::request::FromParam; -use rocket_contrib::json::JsonValue; -use serde::{Deserialize, Serialize}; -use std::sync::{Arc, Mutex}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct MemberRef { - pub guild: String, - pub user: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Member { - #[serde(rename = "_id")] - pub id: MemberRef, - pub nickname: Option<String>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Invite { - pub code: String, - pub creator: String, - pub channel: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Ban { - pub id: String, - pub reason: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Guild { - #[serde(rename = "_id")] - pub id: String, - // pub nonce: String, used internally - pub name: String, - pub description: String, - pub owner: String, - - pub channels: Vec<String>, - pub invites: Vec<Invite>, - pub bans: Vec<Ban>, - - pub default_permissions: u32, -} - -impl Guild { - pub fn serialise(self) -> JsonValue { - json!({ - "id": self.id, - "name": self.name, - "description": self.description, - "owner": self.owner - }) - } - - pub async fn fetch_channels(&self) -> Result<Vec<super::channel::Channel>, String> { - super::channel::fetch_channels(&self.channels).await - } - - pub async fn seralise_with_channels(self) -> Result<JsonValue, String> { - let channels = self - .fetch_channels() - .await? - .into_iter() - .map(|x| x.serialise()) - .collect(); - - let mut value = self.serialise(); - value - .as_object_mut() - .unwrap() - .insert("channels".to_string(), channels); - Ok(value) - } -} - -#[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 async fn fetch_guild(id: &str) -> Result<Option<Guild>, String> { - { - if let Ok(mut cache) = CACHE.lock() { - let existing = cache.get(&id.to_string()); - - if let Some(guild) = existing { - return Ok(Some((*guild).clone())); - } - } else { - return Err("Failed to lock cache.".to_string()); - } - } - - let col = get_collection("guilds"); - if let Ok(result) = col.find_one(doc! { "_id": id }, None).await { - if let Some(doc) = result { - dbg!(doc.to_string()); - if let Ok(guild) = from_bson(Bson::Document(doc)) as Result<Guild, _> { - let mut cache = CACHE.lock().unwrap(); - cache.put(id.to_string(), guild.clone()); - - Ok(Some(guild)) - } else { - Err("Failed to deserialize guild!".to_string()) - } - } else { - Ok(None) - } - } else { - Err("Failed to fetch guild from database.".to_string()) - } -} - -pub async fn fetch_guilds(ids: &Vec<String>) -> Result<Vec<Guild>, String> { - let mut missing = vec![]; - let mut guilds = vec![]; - - { - if let Ok(mut cache) = CACHE.lock() { - for id in ids { - let existing = cache.get(id); - - if let Some(guild) = existing { - guilds.push((*guild).clone()); - } else { - missing.push(id); - } - } - } else { - return Err("Failed to lock cache.".to_string()); - } - } - - if missing.len() == 0 { - return Ok(guilds); - } - - let col = get_collection("guilds"); - if let Ok(mut result) = col.find(doc! { "_id": { "$in": missing } }, None).await { - if let Some(item) = result.next().await { - let mut cache = CACHE.lock().unwrap(); - if let Ok(doc) = item { - dbg!(doc.to_string()); - if let Ok(guild) = from_bson(Bson::Document(doc)) as Result<Guild, _> { - cache.put(guild.id.clone(), guild.clone()); - guilds.push(guild); - } else { - return Err("Failed to deserialize guild!".to_string()); - } - } else { - return Err("Failed to fetch guild.".to_string()); - } - } - - Ok(guilds) - } else { - Err("Failed to fetch channel from database.".to_string()) - } -} - -pub async fn serialise_guilds_with_channels(ids: &Vec<String>) -> Result<Vec<JsonValue>, String> { - let guilds = fetch_guilds(&ids).await?; - let cids: Vec<String> = guilds.iter().flat_map(|x| x.channels.clone()).collect(); - - let channels = fetch_channels(&cids).await?; - Ok(guilds - .into_iter() - .map(|x| { - let id = x.id.clone(); - let mut obj = x.serialise(); - obj.as_object_mut().unwrap().insert( - "channels".to_string(), - channels - .iter() - .filter(|x| x.guild.is_some() && x.guild.as_ref().unwrap() == &id) - .map(|x| x.clone().serialise()) - .collect(), - ); - obj - }) - .collect()) -} - -pub async fn fetch_member(key: MemberKey) -> Result<Option<Member>, String> { - { - if let Ok(mut cache) = MEMBER_CACHE.lock() { - let existing = cache.get(&key); - - if let Some(member) = existing { - return Ok(Some((*member).clone())); - } - } else { - return Err("Failed to lock cache.".to_string()); - } - } - - let col = get_collection("members"); - if let Ok(result) = col.find_one( - doc! { - "_id.guild": &key.0, - "_id.user": &key.1, - }, - None, - ).await { - if let Some(doc) = result { - 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 { - Ok(None) - } - } else { - 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> { - Err(param) - /*if let Ok(result) = fetch_guild(param) { - if let Some(channel) = result { - Ok(channel) - } else { - Err(param) - } - } else { - Err(param) - }*/ - } -} - -pub async 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) = get_collection("guilds").find_one( - doc, - mongodb::options::FindOneOptions::builder() - .projection(doc! { - "_id": 1, - "name": 1, - "invites.$": 1, - }) - .build(), - ).await { - 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(Bson::Document(invite.clone())).unwrap(), - )) - } else { - None - } - } else { - 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_delete(_ev) => {} // ? for later use - Notification::guild_delete(ev) => { - let mut cache = CACHE.lock().unwrap(); - cache.pop(&ev.id); - } - Notification::guild_user_join(ev) => { - let mut cache = MEMBER_CACHE.lock().unwrap(); - cache.put( - MemberKey(ev.id.clone(), ev.user.clone()), - Member { - id: MemberRef { - guild: ev.id.clone(), - user: ev.user.clone(), - }, - nickname: None, - }, - ); - } - Notification::guild_user_leave(ev) => { - let mut cache = MEMBER_CACHE.lock().unwrap(); - cache.pop(&MemberKey(ev.id.clone(), ev.user.clone())); - } - _ => {} - } -}*/ diff --git a/src/database/message.rs b/src/database/message.rs deleted file mode 100644 index f03e747..0000000 --- a/src/database/message.rs +++ /dev/null @@ -1,120 +0,0 @@ -use super::get_collection; -use crate::database::channel::Channel; -use crate::pubsub::hive; -use crate::routes::channel::ChannelType; - -//use mongodb::bson::from_bson; -//use rocket::futures::StreamExt; -//use mongodb::bson::{doc, to_bson, Bson, DateTime}; -use mongodb::bson::{doc, to_bson, DateTime}; -use rocket::http::RawStr; -use rocket::request::FromParam; -use serde::{Deserialize, Serialize}; - -use log::warn; - -#[derive(Serialize, Deserialize, Debug)] -pub struct PreviousEntry { - pub content: String, - pub time: DateTime, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct Message { - #[serde(rename = "_id")] - pub id: String, - pub nonce: Option<String>, - pub channel: String, - pub author: String, - - pub content: String, - pub edited: Option<DateTime>, - - pub previous_content: Vec<PreviousEntry>, -} - -// ? TODO: write global send message -// ? pub fn send_message(); -// ? handle websockets? -impl Message { - pub async fn send(&self, target: &Channel) -> bool { - if get_collection("messages") - .insert_one(to_bson(&self).unwrap().as_document().unwrap().clone(), None) - .await - .is_ok() - { - if hive::publish( - &target.id, - crate::pubsub::events::Notification::message_create( - crate::pubsub::events::message::Create { - id: self.id.clone(), - nonce: self.nonce.clone(), - channel: self.channel.clone(), - author: self.author.clone(), - content: self.content.clone(), - }, - ), - ) - .is_err() - { - warn!("Saved message but couldn't send notification."); - } - - let short_content: String = self.content.chars().take(24).collect(); - - // !! this stuff can be async - if target.channel_type == ChannelType::DM as u8 - || target.channel_type == ChannelType::GROUPDM as u8 - { - let mut update = doc! { - "$set": { - "last_message": { - "id": &self.id, - "user_id": &self.author, - "short_content": short_content, - } - } - }; - - if target.channel_type == ChannelType::DM as u8 { - update - .get_document_mut("$set") - .unwrap() - .insert("active", true); - } - - if get_collection("channels") - .update_one(doc! { "_id": &target.id }, update, None) - .await - .is_ok() - { - true - } else { - false - } - } else { - true - } - } else { - false - } - } -} - -impl<'r> FromParam<'r> for Message { - type Error = &'r RawStr; - - fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { - Err(param) - /*let col = get_collection("messages"); - let result = col - .find_one(doc! { "_id": param.to_string() }, None) - .unwrap(); - - if let Some(message) = result { - Ok(from_bson(Bson::Document(message)).expect("Failed to unwrap message.")) - } else { - Err(param) - }*/ - } -} diff --git a/src/database/mod.rs b/src/database/mod.rs index 118e0fa..5f5810a 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -27,12 +27,4 @@ pub fn get_collection(collection: &str) -> Collection { } pub mod migrations; - -// pub mod channel; -// pub mod guild; -// pub mod message; -// pub mod mutual; -// pub mod permissions; -// pub mod user; - -// pub use permissions::*; +pub mod entities; diff --git a/src/database/mutual.rs b/src/database/mutual.rs deleted file mode 100644 index 1304840..0000000 --- a/src/database/mutual.rs +++ /dev/null @@ -1,130 +0,0 @@ -use super::{get_collection, MemberPermissions}; - -use mongodb::bson::{doc, Document}; -use mongodb::options::FindOptions; -use rocket::futures::StreamExt; - -pub async fn find_mutual_guilds(user_id: &str, target_id: &str) -> Vec<String> { - let col = get_collection("members"); - if let Ok(mut result) = col.find( - doc! { - "$and": [ - { "id": user_id }, - { "id": target_id }, - ] - }, - FindOptions::builder().projection(doc! { "_id": 1 }).build(), - ).await { - let mut results = vec![]; - - while let Some(doc) = result.next().await { - if let Ok(guild) = doc { - results.push(guild.get_str("_id").unwrap().to_string()); - } - } - - results - } else { - vec![] - } -} - -pub async fn find_mutual_friends(user_id: &str, target_id: &str) -> Vec<String> { - let col = get_collection("users"); - if let Ok(mut result) = col.find( - doc! { - "$and": [ - { "relations": { "$elemMatch": { "id": user_id, "status": 0 } } }, - { "relations": { "$elemMatch": { "id": target_id, "status": 0 } } }, - ] - }, - FindOptions::builder().projection(doc! { "_id": 1 }).build(), - ).await { - let mut results = vec![]; - - while let Some(doc) = result.next().await { - if let Ok(user) = doc { - results.push(user.get_str("_id").unwrap().to_string()); - } - } - - results - } else { - vec![] - } -} - -pub async fn find_mutual_groups(user_id: &str, target_id: &str) -> Vec<String> { - let col = get_collection("channels"); - if let Ok(mut result) = col.find( - doc! { - "type": 1, - "$and": [ - { "recipients": user_id }, - { "recipients": target_id }, - ] - }, - FindOptions::builder().projection(doc! { "_id": 1 }).build(), - ).await { - let mut results = vec![]; - - while let Some(doc) = result.next().await { - if let Ok(group) = doc { - results.push(group.get_str("_id").unwrap().to_string()); - } - } - - results - } else { - vec![] - } -} - -pub async fn has_mutual_connection(user_id: &str, target_id: &str, with_permission: bool) -> bool { - let mut doc = doc! { "_id": 1 }; - - if with_permission { - doc.insert("default_permissions", 1); - } - - let opt = FindOptions::builder().projection(doc); - - if let Ok(mut result) = get_collection("guilds").find( - doc! { - "$and": [ - { "members": { "$elemMatch": { "id": user_id } } }, - { "members": { "$elemMatch": { "id": target_id } } }, - ] - }, - if with_permission { - opt.build() - } else { - opt.limit(1).build() - }, - ).await { - if with_permission { - while let Some(item) = result.next().await { - // ? logic should match permissions.rs#calculate - if let Ok(guild) = item { - if guild.get_str("owner").unwrap() == user_id { - return true; - } - - let permissions = guild.get_i32("default_permissions").unwrap() as u32; - - if MemberPermissions([permissions]).get_send_direct_messages() { - return true; - } - } - } - - false - } else if result.collect::<Vec<Result<Document, _>>>().await.len() > 0 { - true - } else { - false - } - } else { - false - } -} diff --git a/src/database/permissions.rs b/src/database/permissions.rs deleted file mode 100644 index e5726ca..0000000 --- a/src/database/permissions.rs +++ /dev/null @@ -1,228 +0,0 @@ -use super::mutual::has_mutual_connection; -use crate::database::channel::Channel; -use crate::database::guild::{fetch_guild, fetch_member, Guild, Member, MemberKey}; -use crate::database::user::{User, UserRelationship}; - -use num_enum::TryFromPrimitive; - -#[derive(Debug, PartialEq, Eq, TryFromPrimitive)] -#[repr(u8)] -pub enum Relationship { - Friend = 0, - Outgoing = 1, - Incoming = 2, - Blocked = 3, - BlockedOther = 4, - NONE = 5, - SELF = 6, -} - -#[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)] -#[repr(u32)] -pub enum Permission { - Access = 1, - CreateInvite = 2, - KickMembers = 4, - BanMembers = 8, - ReadMessages = 16, - SendMessages = 32, - ManageMessages = 64, - ManageChannels = 128, - ManageServer = 256, - ManageRoles = 512, - SendDirectMessages = 1024, -} - -bitfield! { - pub struct MemberPermissions(MSB0 [u32]); - u32; - pub get_access, set_access: 31; - pub get_create_invite, set_create_invite: 30; - pub get_kick_members, set_kick_members: 29; - pub get_ban_members, set_ban_members: 28; - pub get_read_messages, set_read_messages: 27; - pub get_send_messages, set_send_messages: 26; - pub get_manage_messages, set_manage_messages: 25; - pub get_manage_channels, set_manage_channels: 24; - pub get_manage_server, set_manage_server: 23; - pub get_manage_roles, set_manage_roles: 22; - pub get_send_direct_messages, set_send_direct_messages: 21; -} - -pub fn get_relationship_internal( - user_id: &str, - target_id: &str, - relationships: &Option<Vec<UserRelationship>>, -) -> Relationship { - if user_id == target_id { - return Relationship::SELF; - } - - if let Some(arr) = &relationships { - for entry in arr { - if entry.id == target_id { - match entry.status { - 0 => return Relationship::Friend, - 1 => return Relationship::Outgoing, - 2 => return Relationship::Incoming, - 3 => return Relationship::Blocked, - 4 => return Relationship::BlockedOther, - _ => return Relationship::NONE, - } - } - } - } - - Relationship::NONE -} - -pub fn get_relationship(a: &User, b: &User) -> Relationship { - if a.id == b.id { - return Relationship::SELF; - } - - get_relationship_internal(&a.id, &b.id, &a.relations) -} - -pub struct PermissionCalculator { - pub user: User, - pub channel: Option<Channel>, - pub guild: Option<Guild>, - pub member: Option<Member>, -} - -impl PermissionCalculator { - pub fn new(user: User) -> PermissionCalculator { - PermissionCalculator { - user, - channel: None, - guild: None, - member: None, - } - } - - pub fn channel(self, channel: Channel) -> PermissionCalculator { - PermissionCalculator { - channel: Some(channel), - ..self - } - } - - pub fn guild(self, guild: Guild) -> PermissionCalculator { - PermissionCalculator { - guild: Some(guild), - ..self - } - } - - pub async fn fetch_data(mut self) -> PermissionCalculator { - let guild = if let Some(value) = self.guild { - Some(value) - } else if let Some(channel) = &self.channel { - match channel.channel_type { - 0..=1 => None, - 2 => { - if let Some(id) = &channel.guild { - if let Ok(result) = fetch_guild(id).await { - result - } else { - None - } - } else { - None - } - } - _ => None, - } - } else { - None - }; - - if let Some(guild) = &guild { - if let Ok(result) = fetch_member(MemberKey(guild.id.clone(), self.user.id.clone())).await { - self.member = result; - } - } - - self.guild = guild; - self - } - - pub async fn calculate(&self) -> u32 { - let mut permissions: u32 = 0; - if let Some(guild) = &self.guild { - if let Some(_member) = &self.member { - // ? logic should match mutual.rs#has_mutual_connection - if guild.owner == self.user.id { - return u32::MAX; - } - - permissions = guild.default_permissions as u32; - } - } - - if let Some(channel) = &self.channel { - match channel.channel_type { - 0 => { - if let Some(arr) = &channel.recipients { - let mut other_user = None; - for item in arr { - if item != &self.user.id { - other_user = Some(item); - } - } - - if let Some(other) = other_user { - let relationship = get_relationship_internal( - &self.user.id, - &other, - &self.user.relations, - ); - - 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).await { - permissions = 1024 + 128 + 32 + 16 + 1; - } else { - permissions = 1; - } - } else { - // ? In this case, it is a "self DM". - return 1024 + 128 + 32 + 16 + 1; - } - } - } - 1 => { - if let Some(id) = &channel.owner { - if &self.user.id == id { - return u32::MAX; - } - } - - if let Some(arr) = &channel.recipients { - for item in arr { - if item == &self.user.id { - permissions = 177; - break; - } - } - } - } - 2 => { - // nothing implemented yet - } - _ => {} - } - } - - permissions - } - - pub async fn as_permission(&self) -> MemberPermissions<[u32; 1]> { - MemberPermissions([self.calculate().await]) - } -} diff --git a/src/database/user.rs b/src/database/user.rs deleted file mode 100644 index e20ba46..0000000 --- a/src/database/user.rs +++ /dev/null @@ -1,307 +0,0 @@ -use super::get_collection; -use super::channel::fetch_channels; -use super::guild::serialise_guilds_with_channels; - -use lru::LruCache; -use rocket::futures::StreamExt; -use mongodb::options::FindOptions; -use rocket::http::{RawStr, Status}; -use serde::{Deserialize, Serialize}; -use rocket_contrib::json::JsonValue; -use mongodb::bson::{doc, from_bson, Bson, DateTime, Document}; -use rocket::request::{self, FromParam, FromRequest, Request, Outcome}; - -use std::sync::{Arc, Mutex}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UserEmailVerification { - pub verified: bool, - pub target: Option<String>, - pub expiry: Option<DateTime>, - pub rate_limit: Option<DateTime>, - pub code: Option<String>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UserRelationship { - pub id: String, - pub status: u8, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct User { - #[serde(rename = "_id")] - pub id: String, - pub email: String, - pub username: String, - pub password: String, - pub display_name: String, - pub access_token: Option<String>, - pub email_verification: UserEmailVerification, - pub relations: Option<Vec<UserRelationship>>, -} - -impl User { - pub fn serialise(self, relationship: i32) -> JsonValue { - if relationship == super::Relationship::SELF as i32 { - json!({ - "id": self.id, - "username": self.username, - "display_name": self.display_name, - "email": self.email, - "verified": self.email_verification.verified, - }) - } else { - json!({ - "id": self.id, - "username": self.username, - "display_name": self.display_name, - "relationship": relationship - }) - } - } - - pub async fn find_guilds(&self) -> Result<Vec<String>, String> { - let members = get_collection("members") - .find( - doc! { - "_id.user": &self.id - }, - None, - ) - .await - .map_err(|_| "Failed to fetch members.")?; - - Ok(members - .collect::<Vec<Result<Document, _>>>() - .await - .into_iter() - .filter_map(|x| match x { - Ok(doc) => match doc.get_document("_id") { - Ok(id) => match id.get_str("guild") { - Ok(value) => Some(value.to_string()), - Err(_) => None, - }, - Err(_) => None, - }, - Err(_) => None, - }) - .collect()) - } - - pub async fn find_dms(&self) -> Result<Vec<String>, String> { - let channels = get_collection("channels") - .find( - doc! { - "recipients": &self.id - }, - FindOptions::builder().projection(doc! { "_id": 1 }).build(), - ) - .await - .map_err(|_| "Failed to fetch channel ids.")?; - - Ok(channels - .collect::<Vec<Result<Document, _>>>() - .await - .into_iter() - .filter_map(|x| x.ok()) - .filter_map(|x| match x.get_str("_id") { - Ok(value) => Some(value.to_string()), - Err(_) => None, - }) - .collect()) - } - - pub async fn create_payload(self) -> Result<JsonValue, String> { - let v = vec![]; - let relations = self.relations.as_ref().unwrap_or(&v); - - let users: Vec<JsonValue> = fetch_users(&relations.iter().map(|x| x.id.clone()).collect()).await? - .into_iter() - .map(|x| { - let id = x.id.clone(); - x.serialise(relations.iter().find(|y| y.id == id).unwrap().status as i32) - }) - .collect(); - - let channels: Vec<JsonValue> = fetch_channels(&self.find_dms().await?).await? - .into_iter() - .map(|x| x.serialise()) - .collect(); - - Ok(json!({ - "users": users, - "channels": channels, - "guilds": serialise_guilds_with_channels(&self.find_guilds().await?).await?, - "user": self.serialise(super::Relationship::SELF as i32) - })) - } -} - -lazy_static! { - static ref CACHE: Arc<Mutex<LruCache<String, User>>> = - Arc::new(Mutex::new(LruCache::new(4_000_000))); -} - -pub async fn fetch_user(id: &str) -> Result<Option<User>, String> { - { - if let Ok(mut cache) = CACHE.lock() { - let existing = cache.get(&id.to_string()); - - if let Some(user) = existing { - return Ok(Some((*user).clone())); - } - } else { - return Err("Failed to lock cache.".to_string()); - } - } - - let col = get_collection("users"); - if let Ok(result) = col.find_one(doc! { "_id": id }, None).await { - if let Some(doc) = result { - if let Ok(user) = from_bson(Bson::Document(doc)) as Result<User, _> { - let mut cache = CACHE.lock().unwrap(); - cache.put(id.to_string(), user.clone()); - - Ok(Some(user)) - } else { - Err("Failed to deserialize user!".to_string()) - } - } else { - Ok(None) - } - } else { - Err("Failed to fetch user from database.".to_string()) - } -} - -pub async fn fetch_users(ids: &Vec<String>) -> Result<Vec<User>, String> { - let mut missing = vec![]; - let mut users = vec![]; - - { - if let Ok(mut cache) = CACHE.lock() { - for id in ids { - let existing = cache.get(id); - - if let Some(user) = existing { - users.push((*user).clone()); - } else { - missing.push(id); - } - } - } else { - return Err("Failed to lock cache.".to_string()); - } - } - - if missing.len() == 0 { - return Ok(users); - } - - let col = get_collection("users"); - if let Ok(mut result) = col.find(doc! { "_id": { "$in": missing } }, None).await { - while let Some(item) = result.next().await { - let mut cache = CACHE.lock().unwrap(); - if let Ok(doc) = item { - if let Ok(user) = from_bson(Bson::Document(doc)) as Result<User, _> { - cache.put(user.id.clone(), user.clone()); - users.push(user); - } else { - return Err("Failed to deserialize user!".to_string()); - } - } else { - return Err("Failed to fetch user.".to_string()); - } - } - - Ok(users) - } else { - Err("Failed to fetch user from database.".to_string()) - } -} - -#[derive(Debug)] -pub enum AuthError { - Failed, - Missing, - Invalid, -} - -#[rocket::async_trait] -impl<'a, 'r> FromRequest<'a, 'r> for User { - type Error = AuthError; - - async fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> { - let u = request.headers().get("x-user").next(); - let t = request.headers().get("x-auth-token").next(); - - if let Some(uid) = u { - if let Some(token) = t { - if let Ok(result) = fetch_user(uid).await { - if let Some(user) = result { - if let Some(access_token) = &user.access_token { - if access_token == token { - Outcome::Success(user) - } else { - Outcome::Failure((Status::Forbidden, AuthError::Invalid)) - } - } else { - Outcome::Failure((Status::Forbidden, AuthError::Invalid)) - } - } else { - Outcome::Failure((Status::Forbidden, AuthError::Invalid)) - } - } else { - Outcome::Failure((Status::Forbidden, AuthError::Failed)) - } - } else { - Outcome::Failure((Status::Forbidden, AuthError::Missing)) - } - } else { - Outcome::Failure((Status::Forbidden, AuthError::Missing)) - } - } -} - -impl<'r> FromParam<'r> for User { - type Error = &'r RawStr; - - fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { - Err(param) - /*if let Ok(result) = fetch_user(¶m.to_string()).await { - if let Some(user) = result { - Ok(user) - } else { - Err(param) - } - } else { - Err(param) - }*/ - } -} - -/*pub fn process_event(event: &Notification) { - match event { - Notification::user_friend_status(ev) => { - let mut cache = CACHE.lock().unwrap(); - if let Some(user) = cache.peek_mut(&ev.id) { - if let Some(relations) = user.relations.as_mut() { - if ev.status == 0 { - if let Some(pos) = relations.iter().position(|x| x.id == ev.user) { - relations.remove(pos); - } - } else if let Some(entry) = relations.iter_mut().find(|x| x.id == ev.user) { - entry.status = ev.status as u8; - } else { - relations.push(UserRelationship { - id: ev.id.clone(), - status: ev.status as u8, - }); - } - } - } - } - _ => {} - } -}*/ -- GitLab