diff --git a/src/database/guild.rs b/src/database/guild.rs index 7a4155bf218c10ba6ca551124006a202dc380ac7..b2ff3b5653b3d40a4f74a750208010dc4c08f770 100644 --- a/src/database/guild.rs +++ b/src/database/guild.rs @@ -24,7 +24,6 @@ pub struct Guild { pub owner: String, pub channels: Vec<String>, - pub members: Vec<Member>, pub invites: Vec<Invite>, pub default_permissions: u32, diff --git a/src/database/mutual.rs b/src/database/mutual.rs index 9fd3ed162a9f3c0b5378db9399fb26ba31ee392e..5eb234169e2d19cfe7290918baa95aa9c84432bd 100644 --- a/src/database/mutual.rs +++ b/src/database/mutual.rs @@ -4,12 +4,12 @@ use bson::doc; use mongodb::options::{FindOneOptions, FindOptions}; pub fn find_mutual_guilds(user_id: &str, target_id: &str) -> Vec<String> { - let col = get_collection("guilds"); + let col = get_collection("members"); if let Ok(result) = col.find( doc! { "$and": [ - { "members": { "$elemMatch": { "id": user_id } } }, - { "members": { "$elemMatch": { "id": target_id } } }, + { "id": user_id }, + { "id": target_id }, ] }, FindOptions::builder().projection(doc! { "_id": 1 }).build(), diff --git a/src/database/permissions.rs b/src/database/permissions.rs index 13d812a5e8d6f7ba4c400d2aaec1d96b7555b71f..02e6397b204700d31715fb00dae25fb7556f2454 100644 --- a/src/database/permissions.rs +++ b/src/database/permissions.rs @@ -1,4 +1,5 @@ use super::mutual::has_mutual_connection; +use crate::database::get_collection; use crate::database::user::UserRelationship; use crate::guards::auth::UserRef; use crate::guards::channel::ChannelRef; @@ -32,6 +33,7 @@ pub enum Permission { ManageChannels = 128, ManageServer = 256, ManageRoles = 512, + SendDirectMessages = 1024, } bitfield! { @@ -47,6 +49,7 @@ bitfield! { 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( @@ -134,21 +137,20 @@ impl PermissionCalculator { let mut permissions: u32 = 0; if let Some(guild) = guild { - if let Some(_data) = guild.fetch_data_given( + if let Ok(result) = get_collection("members").find_one( doc! { - "members": { - "$elemMatch": { - "id": &self.user.id, - } - } + "_id.user": &self.user.id, + "_id.guild": &guild.id, }, - doc! {}, + None, ) { - if guild.owner == self.user.id { - return u32::MAX; - } + if result.is_some() { + if guild.owner == self.user.id { + return u32::MAX; + } - permissions = guild.default_permissions as u32; + permissions = guild.default_permissions as u32; + } } } diff --git a/src/routes/account.rs b/src/routes/account.rs index c7a2e767c7196572ba82fc3c872053c5c818052b..eb854d697acc9ee5367a66c91113afdd64587c3e 100644 --- a/src/routes/account.rs +++ b/src/routes/account.rs @@ -246,14 +246,19 @@ pub fn login(info: Json<Login>) -> Response { Some(t) => t.to_string(), None => { let token = gen_token(92); - if col.update_one( - doc! { "_id": &user.id }, - doc! { "$set": { "access_token": token.clone() } }, - None, - ).is_err() { - return Response::InternalServerError(json!({ "error": "Failed database operation." })); + if col + .update_one( + doc! { "_id": &user.id }, + doc! { "$set": { "access_token": token.clone() } }, + None, + ) + .is_err() + { + return Response::InternalServerError( + json!({ "error": "Failed database operation." }), + ); } - + token } }; @@ -277,9 +282,7 @@ pub struct Token { pub fn token(info: Json<Token>) -> Response { let col = database::get_collection("users"); - if let Ok(result) = col - .find_one(doc! { "access_token": info.token.clone() }, None) - { + if let Ok(result) = col.find_one(doc! { "access_token": info.token.clone() }, None) { if let Some(user) = result { Response::Success(json!({ "id": user.get_str("_id").unwrap(), diff --git a/src/routes/guild.rs b/src/routes/guild.rs index edd92ed1fe70f6ff2a576d4b4dcd83c68df38560..142df3bb4495ec983e3bdc0f47e1d8a0a5d55f32 100644 --- a/src/routes/guild.rs +++ b/src/routes/guild.rs @@ -1,10 +1,11 @@ use super::channel::ChannelType; use super::Response; -use crate::database::{self, channel::Channel, PermissionCalculator}; +use crate::database::{self, channel::Channel, PermissionCalculator, Permission}; use crate::guards::auth::UserRef; use crate::guards::guild::GuildRef; use bson::{doc, from_bson, Bson}; +use mongodb::options::FindOptions; use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; use ulid::Ulid; @@ -26,32 +27,59 @@ macro_rules! with_permissions { /// fetch your guilds #[get("/@me")] pub fn my_guilds(user: UserRef) -> Response { - let col = database::get_collection("guilds"); - let guilds = col - .find( + if let Ok(result) = database::get_collection("members").find( + doc! { + "_id.user": &user.id + }, + None, + ) { + let mut guilds = vec![]; + for item in result { + if let Ok(entry) = item { + guilds.push(Bson::String( + entry + .get_document("_id") + .unwrap() + .get_str("guild") + .unwrap() + .to_string(), + )); + } + } + + if let Ok(result) = database::get_collection("guilds").find( doc! { - "members": { - "$elemMatch": { - "id": user.id, - } + "_id": { + "$in": guilds } }, - None, - ) - .unwrap(); - - let mut parsed = vec![]; - for item in guilds { - let doc = item.unwrap(); - parsed.push(json!({ - "id": doc.get_str("_id").unwrap(), - "name": doc.get_str("name").unwrap(), - "description": doc.get_str("description").unwrap(), - "owner": doc.get_str("owner").unwrap(), - })); - } + FindOptions::builder() + .projection(doc! { + "_id": 1, + "name": 1, + "description": 1, + "owner": 1, + }) + .build(), + ) { + let mut parsed = vec![]; + for item in result { + let doc = item.unwrap(); + parsed.push(json!({ + "id": doc.get_str("_id").unwrap(), + "name": doc.get_str("name").unwrap(), + "description": doc.get_str("description").unwrap(), + "owner": doc.get_str("owner").unwrap(), + })); + } - Response::Success(json!(parsed)) + Response::Success(json!(parsed)) + } else { + Response::InternalServerError(json!({ "error": "Failed to fetch guilds." })) + } + } else { + Response::InternalServerError(json!({ "error": "Failed to fetch memberships." })) + } } /// fetch a guild @@ -157,21 +185,33 @@ pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response { ); } + if database::get_collection("members") + .insert_one( + doc! { + "_id": { + "guild": &id, + "user": &user.id + } + }, + None, + ) + .is_err() + { + return Response::InternalServerError( + json!({ "error": "Failed to add you to members list." }), + ); + } + if col .insert_one( doc! { - "_id": id.clone(), + "_id": &id, "nonce": nonce, "name": name, "description": description, - "owner": user.id.clone(), + "owner": &user.id, "channels": [ - channel_id.clone() - ], - "members": [ - { - "id": user.id, - } + &channel_id ], "invites": [], "default_permissions": 51, @@ -189,3 +229,87 @@ pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response { Response::InternalServerError(json!({ "error": "Failed to create guild." })) } } + +/// fetch a guild's member +#[get("/<target>/members")] +pub fn fetch_members(user: UserRef, target: GuildRef) -> Option<Response> { + with_permissions!(user, target); + + if let Ok(result) = + database::get_collection("members").find(doc! { "_id.guild": target.id }, None) + { + let mut users = vec![]; + + for item in result { + if let Ok(doc) = item { + users.push(json!({ + "id": doc.get_document("_id").unwrap().get_str("user").unwrap(), + "nickname": doc.get_str("nickname").ok(), + })); + } + } + + Some(Response::Success(json!(users))) + } else { + Some(Response::InternalServerError( + json!({ "error": "Failed to fetch members." }), + )) + } +} + +/// fetch a guild member +#[get("/<target>/members/<other>")] +pub fn fetch_member(user: UserRef, target: GuildRef, other: String) -> Option<Response> { + with_permissions!(user, target); + + if let Ok(result) = database::get_collection("members").find_one( + doc! { + "_id.guild": &target.id, + "_id.user": &other, + }, + None, + ) { + if let Some(doc) = result { + Some(Response::Success(json!({ + "id": doc.get_document("_id").unwrap().get_str("user").unwrap(), + "nickname": doc.get_str("nickname").ok(), + }))) + } else { + Some(Response::NotFound( + json!({ "error": "User not part of guild." }), + )) + } + } else { + Some(Response::InternalServerError( + json!({ "error": "Failed to fetch member." }), + )) + } +} + +/// kick a guild member +#[delete("/<target>/members/<other>")] +pub fn kick_member(user: UserRef, target: GuildRef, other: String) -> Option<Response> { + let permissions = with_permissions!(user, target); + + if !permissions.get_kick_members() { + return Some(Response::LackingPermission(Permission::KickMembers)); + } + + if user.id == other { + return Some(Response::BadRequest(json!({ "error": "Cannot kick yourself." }))) + } + + if database::get_collection("members").delete_one( + doc! { + "_id.guild": &target.id, + "_id.user": &other, + }, + None, + ).is_ok() { + Some(Response::Result(super::Status::Ok)) + } else { + Some(Response::InternalServerError( + json!({ "error": "Failed to kick member." }), + )) + } +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index b6fc93330e238a2bb7f89017fa7f315bd35e90f9..fe95c2ddfe286dd209ce50055576320451593013 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -103,6 +103,13 @@ pub fn mount(rocket: Rocket) -> Rocket { ) .mount( "/api/guild", - routes![guild::my_guilds, guild::guild, guild::create_guild], + routes![ + guild::my_guilds, + guild::guild, + guild::create_guild, + guild::fetch_members, + guild::fetch_member, + guild::kick_member + ], ) }