From a6bc9fcbb3d901600ca3c9d3fca23d1b3c8c64db Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Sat, 28 Oct 2023 14:25:29 +0100 Subject: [PATCH] refactor: implement Server::create --- .../src/models/server_members/model.rs | 17 ++- .../core/database/src/models/servers/model.rs | 127 +++++++----------- .../core/database/src/models/users/model.rs | 13 ++ crates/core/models/src/v0/servers.rs | 28 +++- crates/delta/src/routes/bots/invite.rs | 35 ++--- crates/delta/src/routes/servers/mod.rs | 2 +- .../delta/src/routes/servers/server_create.rs | 109 ++++----------- 7 files changed, 134 insertions(+), 197 deletions(-) diff --git a/crates/core/database/src/models/server_members/model.rs b/crates/core/database/src/models/server_members/model.rs index dc86cf5e..035b408f 100644 --- a/crates/core/database/src/models/server_members/model.rs +++ b/crates/core/database/src/models/server_members/model.rs @@ -3,8 +3,8 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; use crate::{ - events::client::EventV1, util::permissions::DatabasePermissionQuery, Database, File, Server, - SystemMessage, User, + events::client::EventV1, util::permissions::DatabasePermissionQuery, Channel, Database, File, + Server, SystemMessage, User, }; auto_derived_partial!( @@ -80,9 +80,8 @@ impl Member { db: &Database, server: &Server, user: &User, - // channels: Option>, - //) -> Result> { - ) -> Result<()> { + channels: Option>, + ) -> Result> { if db.fetch_ban(&server.id, &user.id).await.is_ok() { return Err(create_error!(Banned)); } @@ -101,9 +100,10 @@ impl Member { db.insert_member(&member).await?; - let mut channels = vec![]; + let should_fetch = channels.is_none(); + let mut channels = channels.unwrap_or_default(); - if true { + if should_fetch { let query = DatabasePermissionQuery::new(db, user).server(server); let existing_channels = db.fetch_channels(&server.channels).await?; @@ -152,8 +152,7 @@ impl Member { .ok(); } - // Ok(channels) - Ok(()) + Ok(channels) } /// Update member data diff --git a/crates/core/database/src/models/servers/model.rs b/crates/core/database/src/models/servers/model.rs index 97b408d6..e3ab13c1 100644 --- a/crates/core/database/src/models/servers/model.rs +++ b/crates/core/database/src/models/servers/model.rs @@ -1,10 +1,11 @@ use std::collections::{HashMap, HashSet}; -use revolt_permissions::OverrideField; +use revolt_models::v0::{self, DataCreateServerChannel}; +use revolt_permissions::{OverrideField, DEFAULT_PERMISSION_SERVER}; use revolt_result::Result; use ulid::Ulid; -use crate::{events::client::EventV1, Database, File}; +use crate::{events::client::EventV1, Channel, Database, File, User}; auto_derived_partial!( /// Server @@ -131,8 +132,51 @@ auto_derived!( #[allow(clippy::disallowed_methods)] impl Server { /// Create a server - pub async fn create(&self, db: &Database) -> Result<()> { - db.insert_server(self).await + pub async fn create( + db: &Database, + data: v0::DataCreateServer, + owner: &User, + create_default_channels: bool, + ) -> Result<(Server, Vec)> { + let mut server = Server { + id: ulid::Ulid::new().to_string(), + owner: owner.id.to_string(), + name: data.name, + description: data.description, + channels: vec![], + nsfw: data.nsfw.unwrap_or(false), + default_permissions: *DEFAULT_PERMISSION_SERVER as i64, + + analytics: false, + banner: None, + categories: None, + discoverable: false, + flags: None, + icon: None, + roles: HashMap::new(), + system_messages: None, + }; + + let channels: Vec = if create_default_channels { + vec![ + Channel::create_server_channel( + db, + &mut server, + DataCreateServerChannel { + channel_type: v0::LegacyServerChannelType::Text, + name: "General".to_string(), + ..Default::default() + }, + false, + ) + .await?, + ] + } else { + vec![] + }; + + db.insert_server(&server).await?; + Ok((server, channels)) } /// Update server data @@ -209,80 +253,7 @@ impl Server { } } - /* /// Create a new member in a server - pub async fn create_member( - &self, - db: &Database, - user: User, - channels: Option>, - ) -> Result> { - if db.fetch_ban(&self.id, &user.id).await.is_ok() { - return Err(Error::Banned); - } - - let member = Member { - id: MemberCompositeKey { - server: self.id.clone(), - user: user.id.clone(), - }, - joined_at: Timestamp::now_utc(), - nickname: None, - avatar: None, - roles: vec![], - timeout: None, - }; - - db.insert_member(&member).await?; - - let should_fetch = channels.is_none(); - let mut channels = channels.unwrap_or_default(); - - if should_fetch { - let perm = perms(&user).server(self).member(&member); - let existing_channels = db.fetch_channels(&self.channels).await?; - for channel in existing_channels { - if perm - .clone() - .channel(&channel) - .has_permission(db, Permission::ViewChannel) - .await? - { - channels.push(channel); - } - } - } - - /* // TODO: EventV1::ServerMemberJoin { - id: self.id.clone(), - user: user.id.clone(), - } - .p(self.id.clone()) - .await; - - EventV1::ServerCreate { - id: self.id.clone(), - server: self.clone(), - channels: channels.clone(), - } - .private(user.id.clone()) - .await; */ - - if let Some(id) = self - .system_messages - .as_ref() - .and_then(|x| x.user_joined.as_ref()) - { - SystemMessage::UserJoined { - id: user.id.clone(), - } - .into_message(id.to_string()) - .create_no_web_push(db, id, false) - .await - .ok(); - } - - Ok(channels) - } + /* /// Remove a member from a server pub async fn remove_member( diff --git a/crates/core/database/src/models/users/model.rs b/crates/core/database/src/models/users/model.rs index bb7c19b6..b5cd7379 100644 --- a/crates/core/database/src/models/users/model.rs +++ b/crates/core/database/src/models/users/model.rs @@ -4,6 +4,7 @@ use crate::{events::client::EventV1, Database, File, RatelimitEvent}; use once_cell::sync::Lazy; use rand::seq::SliceRandom; +use revolt_config::config; use revolt_result::{create_error, Error, ErrorType, Result}; use ulid::Ulid; @@ -218,6 +219,18 @@ impl User { .is_empty()) } + /// Check if this user can acquire another server + pub async fn can_acquire_server(&self, db: &Database) -> Result<()> { + let config = config().await; + if db.fetch_server_count(&self.id).await? <= config.features.limits.default.servers { + Ok(()) + } else { + Err(create_error!(TooManyServers { + max: config.features.limits.default.servers + })) + } + } + /// Sanitise and validate a username can be used pub fn validate_username(username: String) -> Result { // Copy the username for validation diff --git a/crates/core/models/src/v0/servers.rs b/crates/core/models/src/v0/servers.rs index a32c20e7..d8eafde5 100644 --- a/crates/core/models/src/v0/servers.rs +++ b/crates/core/models/src/v0/servers.rs @@ -1,8 +1,11 @@ -use super::File; +use super::{Channel, File}; use revolt_permissions::OverrideField; use std::collections::HashMap; +#[cfg(feature = "validator")] +use validator::Validate; + auto_derived_partial!( /// Server pub struct Server { @@ -141,4 +144,27 @@ auto_derived!( #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub user_banned: Option, } + + /// Information about new server to create + #[derive(Default)] + #[cfg_attr(feature = "validator", derive(Validate))] + pub struct DataCreateServer { + /// Server name + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))] + pub name: String, + /// Server description + #[cfg_attr(feature = "validator", validate(length(min = 0, max = 1024)))] + pub description: Option, + /// Whether this server is age-restricted + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub nsfw: Option, + } + + /// Information returned when creating server + pub struct CreateServerLegacyResponse { + /// Server object + pub server: Server, + /// Default channels + pub channels: Vec, + } ); diff --git a/crates/delta/src/routes/bots/invite.rs b/crates/delta/src/routes/bots/invite.rs index eada9682..24228627 100644 --- a/crates/delta/src/routes/bots/invite.rs +++ b/crates/delta/src/routes/bots/invite.rs @@ -42,7 +42,7 @@ pub async fn invite_bot( .await .throw_if_lacking_channel_permission(ChannelPermission::ManageServer)?; - Member::create(db, &server, &bot_user) + Member::create(db, &server, &bot_user, None) .await .map(|_| EmptyResponse) } @@ -66,7 +66,7 @@ pub async fn invite_bot( mod test { use crate::{rocket, util::test::TestHarness}; use revolt_database::{events::client::EventV1, Bot, Channel, Server}; - use revolt_models::v0; + use revolt_models::v0::{self, DataCreateServer}; use rocket::http::{ContentType, Header, Status}; #[rocket::async_test] @@ -125,26 +125,17 @@ mod test { .await .expect("`Bot`"); - // FIXME: Server::create_server - let server = Server { - id: ulid::Ulid::new().to_string(), - name: TestHarness::rand_string(), - owner: user.id.to_string(), - analytics: false, - discoverable: false, - nsfw: false, - banner: None, - icon: None, - categories: None, - channels: vec![], - default_permissions: 0, - description: None, - flags: None, - roles: Default::default(), - system_messages: None, - }; - - server.create(&harness.db).await.unwrap(); + let (server, _) = Server::create( + &harness.db, + DataCreateServer { + name: TestHarness::rand_string(), + ..Default::default() + }, + &user, + false, + ) + .await + .unwrap(); let response = harness .client diff --git a/crates/delta/src/routes/servers/mod.rs b/crates/delta/src/routes/servers/mod.rs index 96bf0452..9e0eed6d 100644 --- a/crates/delta/src/routes/servers/mod.rs +++ b/crates/delta/src/routes/servers/mod.rs @@ -25,7 +25,7 @@ mod server_fetch; pub fn routes() -> (Vec, OpenApi) { openapi_get_routes_spec![ - server_create::req, + server_create::create_server, server_delete::req, server_fetch::req, server_edit::req, diff --git a/crates/delta/src/routes/servers/server_create.rs b/crates/delta/src/routes/servers/server_create.rs index 3a7ed5ce..8e682c4c 100644 --- a/crates/delta/src/routes/servers/server_create.rs +++ b/crates/delta/src/routes/servers/server_create.rs @@ -1,102 +1,39 @@ -use std::collections::HashMap; - -use revolt_quark::{ - models::{Channel, Server, User}, - variables::delta::MAX_SERVER_COUNT, - Db, Error, Result, DEFAULT_PERMISSION_SERVER, -}; +use revolt_database::{Database, Member, Server, User}; +use revolt_models::v0; +use revolt_result::{create_error, Result}; use rocket::serde::json::Json; -use serde::{Deserialize, Serialize}; -use ulid::Ulid; +use rocket::State; use validator::Validate; -/// # Server Data -#[derive(Validate, Deserialize, JsonSchema)] -pub struct DataCreateServer { - /// Server name - #[validate(length(min = 1, max = 32))] - name: String, - /// Server description - #[validate(length(min = 0, max = 1024))] - description: Option, - /// Whether this server is age-restricted - #[serde(skip_serializing_if = "Option::is_none")] - nsfw: Option, -} - -/// # Create Server Response -#[derive(Validate, Serialize, JsonSchema)] -pub struct CreateServerResponse { - /// Server object - server: Server, - /// Default channels - channels: Vec, -} - /// # Create Server /// /// Create a new server. #[openapi(tag = "Server Information")] -#[post("/create", data = "")] -pub async fn req( - db: &Db, +#[post("/create", data = "")] +pub async fn create_server( + db: &State, user: User, - info: Json, -) -> Result> { + data: Json, +) -> Result> { if user.bot.is_some() { - return Err(Error::IsBot); + return Err(create_error!(IsBot)); } - let info = info.into_inner(); - info.validate() - .map_err(|error| Error::FailedValidation { error })?; + let data = data.into_inner(); + data.validate().map_err(|error| { + create_error!(FailedValidation { + error: error.to_string() + }) + })?; - if !user.can_acquire_server(db).await? { - return Err(Error::TooManyServers { - max: *MAX_SERVER_COUNT, - }); - } + user.can_acquire_server(db).await?; - let DataCreateServer { - name, - description, - nsfw, - } = info; + let (server, channels) = Server::create(db, data, &user, true).await?; + let channels = Member::create(db, &server, &user, Some(channels)).await?; - let channel_id = Ulid::new().to_string(); - let server_id = Ulid::new().to_string(); - - let channel = Channel::TextChannel { - id: channel_id.clone(), - server: server_id.clone(), - - name: "General".into(), - description: None, - - icon: None, - last_message_id: None, - - default_permissions: None, - role_permissions: HashMap::new(), - - nsfw: nsfw.unwrap_or(false), - }; - - db.insert_channel(&channel).await?; - - let server = Server { - id: server_id.clone(), - owner: user.id.clone(), - name, - description, - channels: vec![channel_id], - nsfw: nsfw.unwrap_or(false), - default_permissions: *DEFAULT_PERMISSION_SERVER as i64, - ..Default::default() - }; - - server.create(db).await?; - let channels = server.create_member(db, user, Some(vec![channel])).await?; - Ok(Json(CreateServerResponse { server, channels })) + Ok(Json(v0::CreateServerLegacyResponse { + server: server.into(), + channels: channels.into_iter().map(|channel| channel.into()).collect(), + })) }