From 7af9a77df4c61e49aa51090ba2120076ae620f72 Mon Sep 17 00:00:00 2001 From: Paul Makles <paulmakles@gmail.com> Date: Thu, 9 Apr 2020 09:49:10 +0100 Subject: [PATCH] Refractor to use UserRef. --- Rocket.toml | 4 + src/database/guild.rs | 2 +- src/database/mod.rs | 2 +- src/database/permissions.rs | 107 ++++++++++++++++++++++++++- src/guards/auth.rs | 117 ++++++++++++++++++++++++++++- src/guards/channel.rs | 54 +++++++++++++- src/guards/guild.rs | 67 ++++++++++++++++- src/routes/channel.rs | 126 ++++++++++++++++--------------- src/routes/guild.rs | 11 +-- src/routes/user.rs | 144 +++++++++++++++++++++--------------- 10 files changed, 496 insertions(+), 138 deletions(-) diff --git a/Rocket.toml b/Rocket.toml index 87e1da4..bc6d28f 100644 --- a/Rocket.toml +++ b/Rocket.toml @@ -1,3 +1,7 @@ [development] address = "192.168.0.10" port = 5500 + +[production] +address = "192.168.0.10" +port = 5500 diff --git a/src/database/guild.rs b/src/database/guild.rs index 0939d1b..cdbdef2 100644 --- a/src/database/guild.rs +++ b/src/database/guild.rs @@ -1,7 +1,7 @@ use bson::{bson, doc}; use serde::{Deserialize, Serialize}; -use super::{get_collection, MemberPermissions}; +use super::get_collection; use mongodb::options::FindOneOptions; pub fn find_member_permissions<C: Into<Option<String>>>( diff --git a/src/database/mod.rs b/src/database/mod.rs index 91ad67a..ecc10c5 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -24,10 +24,10 @@ pub fn get_collection(collection: &str) -> Collection { get_db().collection(collection) } -pub mod permissions; pub mod channel; pub mod guild; pub mod message; +pub mod permissions; pub mod user; pub use permissions::*; diff --git a/src/database/permissions.rs b/src/database/permissions.rs index d915cf6..48f62b7 100644 --- a/src/database/permissions.rs +++ b/src/database/permissions.rs @@ -9,6 +9,107 @@ bitfield! { pub get_send_messages, set_send_messages: 2; } -//struct PermissionCalculator { - //channel: Option<>, -//} \ No newline at end of file +use super::get_collection; +use crate::guards::channel::ChannelRef; +use crate::guards::guild::GuildRef; + +use bson::{bson, doc}; +use mongodb::options::FindOneOptions; + +pub struct PermissionCalculator { + pub user_id: String, + pub channel: Option<ChannelRef>, + pub guild: Option<GuildRef>, +} + +impl PermissionCalculator { + pub fn new(user_id: String) -> PermissionCalculator { + PermissionCalculator { + user_id, + channel: None, + guild: None, + } + } + + pub fn channel(self, channel: ChannelRef) -> PermissionCalculator { + PermissionCalculator { + channel: Some(channel), + ..self + } + } + + pub fn guild(self, guild: GuildRef) -> PermissionCalculator { + PermissionCalculator { + guild: Some(guild), + ..self + } + } + + pub fn calculate(self) -> u8 { + 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 { + GuildRef::from(id.clone()) + } else { + None + } + } + _ => None, + } + } else { + None + }; + + let mut permissions = 0; + if let Some(guild) = guild { + if let Some(_data) = guild.fetch_data_given( + doc! { + "members": { + "$elemMatch": { + "id": &self.user_id, + } + } + }, + doc! { } + ) { + if guild.owner == self.user_id { + return u8::MAX; + } + + permissions = guild.default_permissions; + } + } + + if let Some(channel) = &self.channel { + match channel.channel_type { + 0 => { + if let Some(arr) = &channel.recipients { + for item in arr { + if item == &self.user_id { + permissions = 49; + break; + } + } + } + }, + 1 => { + unreachable!() + }, + 2 => { + // nothing implemented yet + }, + _ => {} + } + } + + permissions as u8 + } + + pub fn as_permission(self) -> MemberPermissions<[u8; 1]> { + MemberPermissions([ self.calculate() ]) + } +} diff --git a/src/guards/auth.rs b/src/guards/auth.rs index c9381f1..66df8ea 100644 --- a/src/guards/auth.rs +++ b/src/guards/auth.rs @@ -1,11 +1,51 @@ use rocket::http::{RawStr, Status}; use rocket::request::{self, FromParam, FromRequest, Request}; use rocket::Outcome; - -use bson::{bson, doc, from_bson}; +use bson::{bson, doc, from_bson, Document}; +use serde::{Deserialize, Serialize}; +use mongodb::options::FindOneOptions; use crate::database; -use database::user::User; +use database::user::{User, UserRelationship}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct UserRef { + pub id: String, + pub username: String, + pub email_verified: bool, +} + +impl UserRef { + pub fn fetch_data(&self, projection: Document) -> Option<Document> { + database::get_collection("users") + .find_one( + doc! { "_id": &self.id }, + FindOneOptions::builder().projection(projection).build(), + ) + .expect("Failed to fetch user from database.") + } + + pub fn fetch_relationships(&self) -> Option<Vec<UserRelationship>> { + let user = database::get_collection("users") + .find_one( + doc! { "_id": &self.id }, + FindOneOptions::builder().projection(doc! { "relations": 1 }).build(), + ) + .expect("Failed to fetch user relationships from database.") + .expect("Missing user document."); + + if let Ok(arr) = user.get_array("relations") { + let mut relationships = vec![]; + for item in arr { + relationships.push(from_bson(item.clone()).unwrap()); + } + + Some(relationships) + } else { + None + } + } +} #[derive(Debug)] pub enum AuthError { @@ -14,6 +54,45 @@ pub enum AuthError { Invalid, } +impl<'a, 'r> FromRequest<'a, 'r> for UserRef { + type Error = AuthError; + + fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> { + let keys: Vec<_> = request.headers().get("x-auth-token").collect(); + match keys.len() { + 0 => Outcome::Failure((Status::Forbidden, AuthError::Missing)), + 1 => { + let key = keys[0]; + let result = database::get_collection("users") + .find_one( + doc! { "access_token": key }, + FindOneOptions::builder() + .projection(doc! { + "_id": 1, + "username": 1, + "email_verification.verified": 1, + }) + .build(), + ) + .unwrap(); + + if let Some(user) = result { + Outcome::Success( + UserRef { + id: user.get_str("_id").unwrap().to_string(), + username: user.get_str("username").unwrap().to_string(), + email_verified: user.get_document("email_verification").unwrap().get_bool("verified").unwrap(), + } + ) + } else { + Outcome::Failure((Status::Forbidden, AuthError::Invalid)) + } + } + _ => Outcome::Failure((Status::BadRequest, AuthError::BadCount)), + } + } +} + impl<'a, 'r> FromRequest<'a, 'r> for User { type Error = AuthError; @@ -39,6 +118,38 @@ impl<'a, 'r> FromRequest<'a, 'r> for User { } } +impl<'r> FromParam<'r> for UserRef { + type Error = &'r RawStr; + + fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { + let col = database::get_db().collection("users"); + let result = database::get_collection("users") + .find_one( + doc! { "_id": param.to_string() }, + FindOneOptions::builder() + .projection(doc! { + "_id": 1, + "username": 1, + "email_verification.verified": 1, + }) + .build(), + ) + .unwrap(); + + if let Some(user) = result { + Ok( + UserRef { + id: user.get_str("_id").unwrap().to_string(), + username: user.get_str("username").unwrap().to_string(), + email_verified: user.get_document("email_verification").unwrap().get_bool("verified").unwrap(), + } + ) + } else { + Err(param) + } + } +} + impl<'r> FromParam<'r> for User { type Error = &'r RawStr; diff --git a/src/guards/channel.rs b/src/guards/channel.rs index ee42bf4..254ded4 100644 --- a/src/guards/channel.rs +++ b/src/guards/channel.rs @@ -1,6 +1,8 @@ -use bson::{bson, doc, from_bson}; +use bson::{bson, doc, from_bson, Document}; +use mongodb::options::FindOneOptions; use rocket::http::RawStr; use rocket::request::FromParam; +use serde::{Deserialize, Serialize}; use crate::database; @@ -24,6 +26,56 @@ impl<'r> FromParam<'r> for Channel { } } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ChannelRef { + #[serde(rename = "_id")] + pub id: String, + #[serde(rename = "type")] + pub channel_type: u8, + + // information required for permission calculations + pub recipients: Option<Vec<String>>, + pub guild: Option<String>, +} + +impl ChannelRef { + pub fn fetch_data(&self, projection: Document) -> Option<Document> { + database::get_collection("channels") + .find_one( + doc! { "_id": &self.id }, + FindOneOptions::builder().projection(projection).build(), + ) + .expect("Failed to fetch channel from database.") + } +} + +impl<'r> FromParam<'r> for ChannelRef { + type Error = &'r RawStr; + + fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { + let id = param.to_string(); + let result = database::get_collection("channels") + .find_one( + doc! { "_id": id }, + FindOneOptions::builder() + .projection(doc! { + "_id": 1, + "type": 1, + "recipients": 1, + "guild": 1, + }) + .build(), + ) + .unwrap(); + + if let Some(channel) = result { + Ok(from_bson(bson::Bson::Document(channel)).expect("Failed to deserialize channel.")) + } else { + Err(param) + } + } +} + impl<'r> FromParam<'r> for Message { type Error = &'r RawStr; diff --git a/src/guards/guild.rs b/src/guards/guild.rs index 58c2583..b4af1fd 100644 --- a/src/guards/guild.rs +++ b/src/guards/guild.rs @@ -1,10 +1,73 @@ -use bson::{bson, doc, from_bson}; +use bson::{bson, doc, from_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::Guild; -use database::guild::Guild; +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GuildRef { + #[serde(rename = "_id")] + pub id: String, + + pub owner: String, + 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! { + "owner": 1, + "default_permissions": 1 + }) + .build(), + ) { + Ok(result) => match result { + Some(doc) => { + Some(from_bson(bson::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) + } + } +} impl<'r> FromParam<'r> for Guild { type Error = &'r RawStr; diff --git a/src/routes/channel.rs b/src/routes/channel.rs index cd53de2..5dc57be 100644 --- a/src/routes/channel.rs +++ b/src/routes/channel.rs @@ -1,5 +1,7 @@ use super::Response; -use crate::database::{self, channel::Channel, message::Message, user::User}; +use crate::database::{self, channel::Channel, message::Message, user::User, PermissionCalculator}; +use crate::guards::channel::ChannelRef; +use crate::guards::auth::UserRef; use crate::websocket; use bson::{bson, doc, from_bson, Bson::UtcDatetime}; @@ -17,53 +19,65 @@ pub enum ChannelType { GUILDCHANNEL = 2, } -fn has_permission(user: &User, target: &Channel) -> bool { - match target.channel_type { - 0..=1 => { - if let Some(arr) = &target.recipients { - for item in arr { - if item == &user.id { - return true; - } - } +macro_rules! with_permissions { + ($user: expr, $target: expr) => { + { + let permissions = PermissionCalculator::new($user.id.clone()) + .channel($target.clone()) + .as_permission(); + + if !permissions.get_access() { + return None; } - false + permissions } - 2 => false, - _ => false, - } -} - -fn get_recipients(target: &Channel) -> Vec<String> { - match target.channel_type { - 0..=1 => target.recipients.clone().unwrap(), - _ => vec![], - } + }; } /// fetch channel information #[get("/<target>")] -pub fn channel(user: User, target: Channel) -> Option<Response> { - if !has_permission(&user, &target) { - return None; - } +pub fn channel(user: UserRef, target: ChannelRef) -> Option<Response> { + with_permissions!(user, target); - Some(Response::Success(json!({ - "id": target.id, - "type": target.channel_type, - "recipients": get_recipients(&target), - }))) + match target.channel_type { + 0..=1 => Some(Response::Success( + json!({ + "id": target.id, + "type": target.channel_type, + "recipients": target.recipients, + }) + )), + 2 => { + if let Some(info) = target.fetch_data( + doc! { + "name": 1, + "description": 1, + } + ) { + Some(Response::Success( + json!({ + "id": target.id, + "type": target.channel_type, + "guild": target.guild, + "name": info.get_str("name").unwrap(), + "description": info.get_str("description").unwrap_or(""), + }) + )) + } else { + None + } + }, + _ => unreachable!() + } } /// delete channel /// or leave group DM /// or close DM conversation #[delete("/<target>")] -pub fn delete(user: User, target: Channel) -> Option<Response> { - if !has_permission(&user, &target) { - return None; - } +pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> { + with_permissions!(user, target); let col = database::get_collection("channels"); Some(match target.channel_type { @@ -93,10 +107,8 @@ pub fn delete(user: User, target: Channel) -> Option<Response> { /// fetch channel messages #[get("/<target>/messages")] -pub fn messages(user: User, target: Channel) -> Option<Response> { - if !has_permission(&user, &target) { - return None; - } +pub fn messages(user: UserRef, target: ChannelRef) -> Option<Response> { + with_permissions!(user, target); let col = database::get_collection("messages"); let result = col.find(doc! { "channel": target.id }, None).unwrap(); @@ -124,10 +136,8 @@ pub struct SendMessage { /// send a message to a channel #[post("/<target>/messages", data = "<message>")] -pub fn send_message(user: User, target: Channel, message: Json<SendMessage>) -> Option<Response> { - if !has_permission(&user, &target) { - return None; - } +pub fn send_message(user: UserRef, target: ChannelRef, message: Json<SendMessage>) -> Option<Response> { + with_permissions!(user, target); let content: String = message.content.chars().take(2000).collect(); let nonce: String = message.nonce.chars().take(32).collect(); @@ -164,7 +174,7 @@ pub fn send_message(user: User, target: Channel, message: Json<SendMessage>) -> .unwrap(); } - websocket::queue_message( + /*websocket::queue_message( get_recipients(&target), json!({ "type": "message", @@ -177,7 +187,7 @@ pub fn send_message(user: User, target: Channel, message: Json<SendMessage>) -> }, }) .to_string(), - ); + );*/ Response::Success(json!({ "id": id })) } else { @@ -190,10 +200,8 @@ pub fn send_message(user: User, target: Channel, message: Json<SendMessage>) -> /// get a message #[get("/<target>/messages/<message>")] -pub fn get_message(user: User, target: Channel, message: Message) -> Option<Response> { - if !has_permission(&user, &target) { - return None; - } +pub fn get_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> { + with_permissions!(user, target); let prev = // ! CHECK IF USER HAS PERMISSION TO VIEW EDITS OF MESSAGES @@ -228,14 +236,12 @@ pub struct EditMessage { /// edit a message #[patch("/<target>/messages/<message>", data = "<edit>")] pub fn edit_message( - user: User, - target: Channel, + user: UserRef, + target: ChannelRef, message: Message, edit: Json<EditMessage>, ) -> Option<Response> { - if !has_permission(&user, &target) { - return None; - } + with_permissions!(user, target); Some(if message.author != user.id { Response::Unauthorized(json!({ "error": "You did not send this message." })) @@ -266,7 +272,7 @@ pub fn edit_message( None, ) { Ok(_) => { - websocket::queue_message( + /*websocket::queue_message( get_recipients(&target), json!({ "type": "message_update", @@ -278,7 +284,7 @@ pub fn edit_message( }, }) .to_string(), - ); + );*/ Response::Result(super::Status::Ok) } @@ -291,10 +297,8 @@ pub fn edit_message( /// delete a message #[delete("/<target>/messages/<message>")] -pub fn delete_message(user: User, target: Channel, message: Message) -> Option<Response> { - if !has_permission(&user, &target) { - return None; - } +pub fn delete_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> { + with_permissions!(user, target); Some(if message.author != user.id { Response::Unauthorized(json!({ "error": "You did not send this message." })) @@ -303,7 +307,7 @@ pub fn delete_message(user: User, target: Channel, message: Message) -> Option<R match col.delete_one(doc! { "_id": message.id.clone() }, None) { Ok(_) => { - websocket::queue_message( + /*websocket::queue_message( get_recipients(&target), json!({ "type": "message_delete", @@ -313,7 +317,7 @@ pub fn delete_message(user: User, target: Channel, message: Message) -> Option<R }, }) .to_string(), - ); + );*/ Response::Result(super::Status::Ok) } diff --git a/src/routes/guild.rs b/src/routes/guild.rs index 76b807c..9457a3e 100644 --- a/src/routes/guild.rs +++ b/src/routes/guild.rs @@ -5,6 +5,7 @@ use crate::database::{ guild::{find_member_permissions, Guild}, user::User, }; +use crate::guards::auth::UserRef; use bson::{bson, doc, from_bson, Bson}; use rocket_contrib::json::{Json, JsonValue}; @@ -15,7 +16,7 @@ use super::channel::ChannelType; /// fetch your guilds #[get("/@me")] -pub fn my_guilds(user: User) -> Response { +pub fn my_guilds(user: UserRef) -> Response { let col = database::get_collection("guilds"); let guilds = col .find( @@ -46,7 +47,7 @@ pub fn my_guilds(user: User) -> Response { /// fetch a guild #[get("/<target>")] -pub fn guild(user: User, target: Guild) -> Option<Response> { +pub fn guild(user: UserRef, target: Guild) -> Option<Response> { if find_member_permissions(user.id.clone(), target.id.clone(), None) == 0 { return None; } @@ -72,7 +73,7 @@ pub fn guild(user: User, target: Guild) -> Option<Response> { .expect("Failed to unwrap channel."); channels.push(json!({ - "_id": channel.id, + "id": channel.id, "last_message": channel.last_message, "name": channel.name, "description": channel.description, @@ -102,8 +103,8 @@ pub struct CreateGuild { /// create a new guild #[post("/create", data = "<info>")] -pub fn create_guild(user: User, info: Json<CreateGuild>) -> Response { - if !user.email_verification.verified { +pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response { + if !user.email_verified { return Response::Unauthorized(json!({ "error": "Email not verified!" })); } diff --git a/src/routes/user.rs b/src/routes/user.rs index 516745b..01dcc3f 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -1,27 +1,75 @@ use super::Response; -use crate::database::{self, channel::Channel, user::User}; +use crate::database::{self, channel::Channel, user::UserRelationship}; use crate::routes::channel; +use crate::guards::auth::UserRef; use bson::{bson, doc, from_bson}; use mongodb::options::FindOptions; -use rocket_contrib::json::{Json, JsonValue}; +use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; use ulid::Ulid; +enum Relationship { + FRIEND = 0, + OUTGOING = 1, + INCOMING = 2, + BLOCKED = 3, + BLOCKEDOTHER = 4, + NONE = 5, + SELF = 6, +} + +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 +} + +fn get_relationship(a: &UserRef, b: &UserRef) -> Relationship { + if a.id == b.id { + return Relationship::SELF; + } + + get_relationship_internal(&a.id, &b.id, &a.fetch_relationships()) +} + /// retrieve your user information #[get("/@me")] -pub fn me(user: User) -> JsonValue { - json!({ - "id": user.id, - "username": user.username, - "email": user.email, - "verified": user.email_verification.verified, - }) +pub fn me(user: UserRef) -> Response { + if let Some(info) = user.fetch_data(doc! { "email": 1 }) { + Response::Success( + json!({ + "id": user.id, + "username": user.username, + "email": info.get_str("email").unwrap(), + "verified": user.email_verified, + }) + ) + } else { + Response::InternalServerError(json!({ "error": "Failed to fetch information from database." })) + } } /// retrieve another user's information #[get("/<target>")] -pub fn user(user: User, target: User) -> Response { +pub fn user(user: UserRef, target: UserRef) -> Response { Response::Success(json!({ "id": target.id, "username": target.username, @@ -37,25 +85,30 @@ pub struct Query { /// lookup a user on Revolt /// currently only supports exact username searches #[post("/lookup", data = "<query>")] -pub fn lookup(user: User, query: Json<Query>) -> Response { +pub fn lookup(user: UserRef, query: Json<Query>) -> Response { + let relationships = user.fetch_relationships(); let col = database::get_collection("users"); let users = col .find( doc! { "username": query.username.clone() }, - FindOptions::builder().limit(10).build(), + FindOptions::builder() + .projection(doc! { "_id": 1, "username": 1 }) + .limit(10) + .build(), ) .expect("Failed user lookup"); let mut results = Vec::new(); for item in users { - let u: User = - from_bson(bson::Bson::Document(item.unwrap())).expect("Failed to unwrap user."); - results.push(json!({ - "id": u.id, - "username": u.username, - "relationship": get_relationship(&user, &u) as u8 - })); + if let Ok(doc) = item { + let id = doc.get_str("id").unwrap(); + results.push(json!({ + "id": id.clone(), + "username": doc.get_str("username").unwrap(), + "relationship": get_relationship_internal(&user.id, &id, &relationships) as u8 + })); + } } Response::Success(json!(results)) @@ -63,7 +116,7 @@ pub fn lookup(user: User, query: Json<Query>) -> Response { /// retrieve all of your DMs #[get("/@me/dms")] -pub fn dms(user: User) -> Response { +pub fn dms(user: UserRef) -> Response { let col = database::get_collection("channels"); let results = col @@ -101,7 +154,7 @@ pub fn dms(user: User) -> Response { /// open a DM with a user #[get("/<target>/dm")] -pub fn dm(user: User, target: User) -> Response { +pub fn dm(user: UserRef, target: UserRef) -> Response { let col = database::get_collection("channels"); match col.find_one( @@ -128,44 +181,13 @@ pub fn dm(user: User, target: User) -> Response { } } -enum Relationship { - FRIEND = 0, - OUTGOING = 1, - INCOMING = 2, - BLOCKED = 3, - BLOCKEDOTHER = 4, - NONE = 5, - SELF = 6, -} - -fn get_relationship(a: &User, b: &User) -> Relationship { - if a.id == b.id { - return Relationship::SELF; - } - - if let Some(arr) = &b.relations { - for entry in arr { - if entry.id == a.id { - match entry.status { - 0 => return Relationship::FRIEND, - 1 => return Relationship::INCOMING, - 2 => return Relationship::OUTGOING, - 3 => return Relationship::BLOCKEDOTHER, - 4 => return Relationship::BLOCKED, - _ => return Relationship::NONE, - } - } - } - } - - Relationship::NONE -} - /// retrieve all of your friends #[get("/@me/friend")] -pub fn get_friends(user: User) -> Response { +pub fn get_friends(user: UserRef) -> Response { + let relationships = user.fetch_relationships(); + let mut results = Vec::new(); - if let Some(arr) = user.relations { + if let Some(arr) = relationships { for item in arr { results.push(json!({ "id": item.id, @@ -179,7 +201,7 @@ pub fn get_friends(user: User) -> Response { /// retrieve friend status with user #[get("/<target>/friend")] -pub fn get_friend(user: User, target: User) -> Response { +pub fn get_friend(user: UserRef, target: UserRef) -> Response { let relationship = get_relationship(&user, &target); Response::Success(json!({ "status": relationship as u8 })) @@ -187,9 +209,9 @@ pub fn get_friend(user: User, target: User) -> Response { /// create or accept a friend request #[put("/<target>/friend")] -pub fn add_friend(user: User, target: User) -> Response { - let col = database::get_collection("users"); +pub fn add_friend(user: UserRef, target: UserRef) -> Response { let relationship = get_relationship(&user, &target); + let col = database::get_collection("users"); match relationship { Relationship::FRIEND => Response::BadRequest(json!({ "error": "Already friends." })), @@ -300,9 +322,9 @@ pub fn add_friend(user: User, target: User) -> Response { /// remove a friend or deny a request #[delete("/<target>/friend")] -pub fn remove_friend(user: User, target: User) -> Response { - let col = database::get_collection("users"); +pub fn remove_friend(user: UserRef, target: UserRef) -> Response { let relationship = get_relationship(&user, &target); + let col = database::get_collection("users"); match relationship { Relationship::FRIEND | Relationship::OUTGOING | Relationship::INCOMING => { -- GitLab