diff --git a/crates/core/database/src/models/server_bans/model.rs b/crates/core/database/src/models/server_bans/model.rs index 8e5175a0..46e8a889 100644 --- a/crates/core/database/src/models/server_bans/model.rs +++ b/crates/core/database/src/models/server_bans/model.rs @@ -1,4 +1,6 @@ -use crate::MemberCompositeKey; +use revolt_result::Result; + +use crate::{Database, MemberCompositeKey, Server}; auto_derived!( /// Server Ban @@ -10,3 +12,24 @@ auto_derived!( pub reason: Option, } ); + +impl ServerBan { + /// Create ban + pub async fn create( + db: &Database, + server: &Server, + user_id: &str, + reason: Option, + ) -> Result { + let ban = ServerBan { + id: MemberCompositeKey { + server: server.id.to_string(), + user: user_id.to_string(), + }, + reason, + }; + + db.insert_ban(&ban).await?; + Ok(ban) + } +} diff --git a/crates/core/database/src/models/server_members/model.rs b/crates/core/database/src/models/server_members/model.rs index 07c349fb..63a35ec3 100644 --- a/crates/core/database/src/models/server_members/model.rs +++ b/crates/core/database/src/models/server_members/model.rs @@ -215,4 +215,47 @@ impl Member { false } } + + /// Remove member from server + pub async fn remove( + self, + db: &Database, + server: &Server, + intention: RemovalIntention, + silent: bool, + ) -> Result<()> { + db.delete_member(&self.id).await?; + + EventV1::ServerMemberLeave { + id: self.id.server.to_string(), + user: self.id.user.to_string(), + } + .p(self.id.server.to_string()) + .await; + + if !silent { + if let Some(id) = server + .system_messages + .as_ref() + .and_then(|x| match intention { + RemovalIntention::Leave => x.user_left.as_ref(), + RemovalIntention::Kick => x.user_kicked.as_ref(), + RemovalIntention::Ban => x.user_banned.as_ref(), + }) + { + match intention { + RemovalIntention::Leave => SystemMessage::UserLeft { id: self.id.user }, + RemovalIntention::Kick => SystemMessage::UserKicked { id: self.id.user }, + RemovalIntention::Ban => SystemMessage::UserBanned { id: self.id.user }, + } + .into_message(id.to_string()) + // TODO: support notifications here in the future? + .send_without_notifications(db, false, false) + .await + .ok(); + } + } + + Ok(()) + } } diff --git a/crates/core/database/src/models/servers/model.rs b/crates/core/database/src/models/servers/model.rs index 6beebe06..8e427fb4 100644 --- a/crates/core/database/src/models/servers/model.rs +++ b/crates/core/database/src/models/servers/model.rs @@ -255,71 +255,6 @@ impl Server { Err(create_error!(NotFound)) } } - - /* - - /// Remove a member from a server - pub async fn remove_member( - &self, - db: &Database, - member: Member, - intention: RemovalIntention, - silent: bool, - ) -> Result<()> { - db.delete_member(&member.id).await?; - - /* // TODO: EventV1::ServerMemberLeave { - id: self.id.to_string(), - user: member.id.user.clone(), - } - .p(member.id.server) - .await; */ - - if !silent { - if let Some(id) = self.system_messages.as_ref().and_then(|x| match intention { - RemovalIntention::Leave => x.user_left.as_ref(), - RemovalIntention::Kick => x.user_kicked.as_ref(), - RemovalIntention::Ban => x.user_banned.as_ref(), - }) { - match intention { - RemovalIntention::Leave => SystemMessage::UserLeft { id: member.id.user }, - RemovalIntention::Kick => SystemMessage::UserKicked { id: member.id.user }, - RemovalIntention::Ban => SystemMessage::UserBanned { id: member.id.user }, - } - .into_message(id.to_string()) - .create_no_web_push(db, id, false) - .await - .ok(); - } - } - - Ok(()) - } - - /// Create ban - pub async fn ban_user( - self, - db: &Database, - id: MemberCompositeKey, - reason: Option, - ) -> Result { - let ban = ServerBan { id, reason }; - db.insert_ban(&ban).await?; - Ok(ban) - } - - /// Ban a member from a server - pub async fn ban_member( - self, - db: &Database, - member: Member, - reason: Option, - ) -> Result { - self.remove_member(db, member.clone(), RemovalIntention::Ban, false) - .await?; - - self.ban_user(db, member.id, reason).await - } */ } impl Role { diff --git a/crates/core/database/src/util/reference.rs b/crates/core/database/src/util/reference.rs index c3a6fd21..4c220654 100644 --- a/crates/core/database/src/util/reference.rs +++ b/crates/core/database/src/util/reference.rs @@ -7,7 +7,9 @@ use schemars::{ JsonSchema, }; -use crate::{Bot, Channel, Database, Emoji, Invite, Message, Server, User, Webhook}; +use crate::{ + Bot, Channel, Database, Emoji, Invite, Member, Message, Server, ServerBan, User, Webhook, +}; /// Reference to some object in the database #[derive(Serialize, Deserialize)] @@ -22,6 +24,11 @@ impl Reference { Reference { id } } + /// Fetch ban from Ref + pub async fn as_ban(&self, db: &Database, server: &str) -> Result { + db.fetch_ban(server, &self.id).await + } + /// Fetch bot from Ref pub async fn as_bot(&self, db: &Database) -> Result { db.fetch_bot(&self.id).await @@ -57,6 +64,11 @@ impl Reference { Ok(msg) } + /// Fetch member from Ref + pub async fn as_member(&self, db: &Database, server: &str) -> Result { + db.fetch_member(server, &self.id).await + } + /// Fetch server from Ref pub async fn as_server(&self, db: &Database) -> Result { db.fetch_server(&self.id).await diff --git a/crates/core/models/src/v0/server_bans.rs b/crates/core/models/src/v0/server_bans.rs index cf243ee7..b6debfcd 100644 --- a/crates/core/models/src/v0/server_bans.rs +++ b/crates/core/models/src/v0/server_bans.rs @@ -1,4 +1,7 @@ -use super::MemberCompositeKey; +use super::{File, MemberCompositeKey, User}; + +#[cfg(feature = "validator")] +use validator::Validate; auto_derived!( /// Server Ban @@ -9,4 +12,44 @@ auto_derived!( /// Reason for ban creation pub reason: Option, } + + /// Information for new server ban + #[cfg_attr(feature = "validator", derive(Validate))] + pub struct DataBanCreate { + /// Ban reason + #[cfg_attr(feature = "validator", validate(length(min = 0, max = 1024)))] + pub reason: Option, + } + + /// Just enough information to list a ban + pub struct BannedUser { + /// Id of the banned user + #[cfg_attr(feature = "serde", serde(rename = "_id"))] + pub id: String, + /// Username of the banned user + pub username: String, + /// Discriminator of the banned user + pub discriminator: String, + /// Avatar of the banned user + pub avatar: Option, + } + + /// Ban list result + pub struct BanListResult { + /// Users objects + pub users: Vec, + /// Ban objects + pub bans: Vec, + } ); + +impl From for BannedUser { + fn from(user: User) -> Self { + BannedUser { + id: user.id, + username: user.username, + discriminator: user.discriminator, + avatar: user.avatar, + } + } +} diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index f6cb4ef0..9c1951ad 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -1,73 +1,66 @@ -use revolt_quark::{ - models::{server_member::MemberCompositeKey, ServerBan, User}, - perms, Db, Error, Permission, Ref, Result, +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, RemovalIntention, ServerBan, User, }; +use revolt_models::v0; -use rocket::serde::json::Json; +use revolt_permissions::{ + calculate_channel_permissions, calculate_server_permissions, ChannelPermission, +}; +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; use validator::Validate; -/// # Ban Information -#[derive(Validate, Serialize, Deserialize, JsonSchema)] -pub struct DataBanCreate { - /// Ban reason - #[validate(length(min = 1, max = 1024))] - reason: Option, -} - /// # Ban User /// /// Ban a user by their id. #[openapi(tag = "Server Members")] #[put("//bans/", data = "")] -pub async fn req( - db: &Db, +pub async fn ban( + db: &State, user: User, - server: Ref, - target: Ref, - data: Json, -) -> Result> { + server: Reference, + target: Reference, + data: Json, +) -> Result> { let data = data.into_inner(); - data.validate() - .map_err(|error| Error::FailedValidation { error })?; + data.validate().map_err(|error| { + create_error!(FailedValidation { + error: error.to_string() + }) + })?; let server = server.as_server(db).await?; if target.id == user.id { - return Err(Error::CannotRemoveYourself); + return Err(create_error!(CannotRemoveYourself)); } if target.id == server.owner { - return Err(Error::InvalidOperation); + return Err(create_error!(InvalidOperation)); } - let mut permissions = perms(&user).server(&server); - - permissions - .throw_permission(db, Permission::BanMembers) - .await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + calculate_server_permissions(&mut query) + .await + .throw_if_lacking_channel_permission(ChannelPermission::BanMembers)?; // If member exists, check privileges against them if let Ok(member) = target.as_member(db, &server.id).await { - if member.get_ranking(permissions.server.get().unwrap()) - <= permissions.get_member_rank().unwrap_or(i64::MIN) + if member.get_ranking(&query.server_ref().as_ref().unwrap()) + <= query.get_member_rank().unwrap_or(i64::MIN) { - return Err(Error::NotElevated); + return Err(create_error!(NotElevated)); } - server.ban_member(db, member, data.reason).await.map(Json) - } else { - let server_id = server.id.to_string(); - server - .ban_user( - db, - MemberCompositeKey { - server: server_id, - user: target.id, - }, - data.reason, - ) - .await - .map(Json) + member + .remove(db, &server, RemovalIntention::Ban, false) + .await?; } + + ServerBan::create(db, &server, &target.id, data.reason) + .await + .map(Into::into) + .map(Json) } diff --git a/crates/delta/src/routes/servers/ban_list.rs b/crates/delta/src/routes/servers/ban_list.rs index d9884d69..5ab63fb0 100644 --- a/crates/delta/src/routes/servers/ban_list.rs +++ b/crates/delta/src/routes/servers/ban_list.rs @@ -1,60 +1,33 @@ -use revolt_quark::models::{File, ServerBan, User}; -use revolt_quark::{perms, Db, Permission, Ref, Result}; +use futures::future::join_all; +use revolt_database::util::permissions::DatabasePermissionQuery; +use revolt_database::util::reference::Reference; +use revolt_database::{Database, User}; +use revolt_models::v0; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_result::Result; use rocket::serde::json::Json; -use serde::{Deserialize, Serialize}; - -/// # Banned User -/// -/// Just enoguh user information to list bans. -#[derive(Serialize, Deserialize, JsonSchema)] -struct BannedUser { - /// Id of the banned user - #[serde(rename = "_id")] - pub id: String, - /// Username of the banned user - pub username: String, - /// Discriminator of the banned user - pub discriminator: String, - /// Avatar of the banned user - pub avatar: Option, -} - -/// # Ban List Result -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct BanListResult { - /// Users objects - users: Vec, - /// Ban objects - bans: Vec, -} - -impl From for BannedUser { - fn from(user: User) -> Self { - BannedUser { - id: user.id, - username: user.username, - discriminator: user.discriminator, - avatar: user.avatar, - } - } -} +use rocket::State; /// # Fetch Bans /// /// Fetch all bans on a server. #[openapi(tag = "Server Members")] #[get("//bans")] -pub async fn req(db: &Db, user: User, target: Ref) -> Result> { +pub async fn list( + db: &State, + user: User, + target: Reference, +) -> Result> { let server = target.as_server(db).await?; - perms(&user) - .server(&server) - .throw_permission(db, Permission::BanMembers) - .await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + calculate_server_permissions(&mut query) + .await + .throw_if_lacking_channel_permission(ChannelPermission::BanMembers)?; let bans = db.fetch_bans(&server.id).await?; - let users = db - .fetch_users( + let users = join_all( + db.fetch_users( &bans .iter() .map(|x| &x.id.user) @@ -63,8 +36,12 @@ pub async fn req(db: &Db, user: User, target: Ref) -> Result ) .await? .into_iter() - .map(|x| x.into()) - .collect(); + .map(|u| u.into_self()), + ) + .await; - Ok(Json(BanListResult { users, bans })) + Ok(Json(v0::BanListResult { + users: users.into_iter().map(Into::into).collect(), + bans: bans.into_iter().map(Into::into).collect(), + })) } diff --git a/crates/delta/src/routes/servers/ban_remove.rs b/crates/delta/src/routes/servers/ban_remove.rs index 3486ce45..3b1b7285 100644 --- a/crates/delta/src/routes/servers/ban_remove.rs +++ b/crates/delta/src/routes/servers/ban_remove.rs @@ -1,16 +1,28 @@ -use revolt_quark::{models::User, perms, Db, EmptyResponse, Permission, Ref, Result}; +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, User, +}; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_result::Result; +use rocket::State; +use rocket_empty::EmptyResponse; /// # Unban user /// /// Remove a user's ban. #[openapi(tag = "Server Members")] #[delete("//bans/")] -pub async fn req(db: &Db, user: User, server: Ref, target: Ref) -> Result { +pub async fn unban( + db: &State, + user: User, + server: Reference, + target: Reference, +) -> Result { let server = server.as_server(db).await?; - perms(&user) - .server(&server) - .throw_permission(db, Permission::BanMembers) - .await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + calculate_server_permissions(&mut query) + .await + .throw_if_lacking_channel_permission(ChannelPermission::BanMembers)?; let ban = target.as_ban(db, &server.id).await?; db.delete_ban(&ban.id).await.map(|_| EmptyResponse) diff --git a/crates/delta/src/routes/servers/emoji_list.rs b/crates/delta/src/routes/servers/emoji_list.rs index 4df08437..405e5d43 100644 --- a/crates/delta/src/routes/servers/emoji_list.rs +++ b/crates/delta/src/routes/servers/emoji_list.rs @@ -1,17 +1,31 @@ -use revolt_quark::models::{Emoji, User}; -use revolt_quark::{perms, Db, Ref, Result}; - -use rocket::serde::json::Json; +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, User, +}; +use revolt_models::v0; +use revolt_permissions::PermissionQuery; +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; /// # Fetch Server Emoji /// /// Fetch all emoji on a server. #[openapi(tag = "Server Customisation")] #[get("//emojis")] -pub async fn list_emoji(db: &Db, user: User, target: Ref) -> Result>> { +pub async fn list_emoji( + db: &State, + user: User, + target: Reference, +) -> Result>> { let server = target.as_server(db).await?; - perms(&user).server(&server).calc(db).await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + if !query.are_we_a_member().await { + return Err(create_error!(NotFound)); + } // Fetch all emoji from server if we can view it - db.fetch_emoji_by_parent_id(&server.id).await.map(Json) + db.fetch_emoji_by_parent_id(&server.id) + .await + .map(|v| v.into_iter().map(Into::into).collect()) + .map(Json) } diff --git a/crates/delta/src/routes/servers/invites_fetch.rs b/crates/delta/src/routes/servers/invites_fetch.rs index b5a77bf9..05958485 100644 --- a/crates/delta/src/routes/servers/invites_fetch.rs +++ b/crates/delta/src/routes/servers/invites_fetch.rs @@ -1,21 +1,30 @@ -use revolt_quark::{ - models::{Invite, User}, - perms, Db, Permission, Ref, Result, +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, User, }; - -use rocket::serde::json::Json; +use revolt_models::v0; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_result::Result; +use rocket::{serde::json::Json, State}; /// # Fetch Invites /// /// Fetch all server invites. #[openapi(tag = "Server Members")] #[get("//invites")] -pub async fn req(db: &Db, user: User, target: Ref) -> Result>> { +pub async fn invites( + db: &State, + user: User, + target: Reference, +) -> Result>> { let server = target.as_server(db).await?; - perms(&user) - .server(&server) - .throw_permission(db, Permission::ManageServer) - .await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + calculate_server_permissions(&mut query) + .await + .throw_if_lacking_channel_permission(ChannelPermission::ManageServer)?; - db.fetch_invites_for_server(&server.id).await.map(Json) + db.fetch_invites_for_server(&server.id) + .await + .map(|v| v.into_iter().map(Into::into).collect()) + .map(Json) } diff --git a/crates/delta/src/routes/servers/mod.rs b/crates/delta/src/routes/servers/mod.rs index 636da3d0..0b7f2bea 100644 --- a/crates/delta/src/routes/servers/mod.rs +++ b/crates/delta/src/routes/servers/mod.rs @@ -37,10 +37,10 @@ pub fn routes() -> (Vec, OpenApi) { member_fetch::req, member_edit::req, member_experimental_query::member_experimental_query, - ban_create::req, - ban_remove::req, - ban_list::req, - invites_fetch::req, + ban_create::ban, + ban_remove::unban, + ban_list::list, + invites_fetch::invites, roles_create::req, roles_edit::req, roles_fetch::req,