diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index f508ec29..b01266c4 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -275,6 +275,15 @@ impl From for EmojiParent { } } +impl From for crate::EmojiParent { + fn from(value: EmojiParent) -> Self { + match value { + EmojiParent::Detached => crate::EmojiParent::Detached, + EmojiParent::Server { id } => crate::EmojiParent::Server { id }, + } + } +} + impl From for File { fn from(value: crate::File) -> Self { File { diff --git a/crates/core/database/src/util/reference.rs b/crates/core/database/src/util/reference.rs index 64357a96..7befa54d 100644 --- a/crates/core/database/src/util/reference.rs +++ b/crates/core/database/src/util/reference.rs @@ -7,7 +7,7 @@ use schemars::{ JsonSchema, }; -use crate::{Bot, Channel, Database, Message, Webhook}; +use crate::{Bot, Channel, Database, Emoji, Message, Webhook}; /// Reference to some object in the database #[derive(Serialize, Deserialize)] @@ -27,6 +27,11 @@ impl Reference { db.fetch_bot(&self.id).await } + /// Fetch emoji from Ref + pub async fn as_emoji(&self, db: &Database) -> Result { + db.fetch_emoji(&self.id).await + } + /// Fetch channel from Ref pub async fn as_channel(&self, db: &Database) -> Result { db.fetch_channel(&self.id).await diff --git a/crates/core/models/src/v0/emojis.rs b/crates/core/models/src/v0/emojis.rs index cf610bde..07451bd6 100644 --- a/crates/core/models/src/v0/emojis.rs +++ b/crates/core/models/src/v0/emojis.rs @@ -1,3 +1,12 @@ +use once_cell::sync::Lazy; +use regex::Regex; +use validator::Validate; + +/// Regex for valid emoji names +/// +/// Alphanumeric and underscores +pub static RE_EMOJI: Lazy = Lazy::new(|| Regex::new(r"^[a-z0-9_]+$").unwrap()); + auto_derived!( /// Emoji pub struct Emoji { @@ -30,4 +39,17 @@ auto_derived!( Server { id: String }, Detached, } + + /// Create a new emoji + #[derive(Validate)] + pub struct DataCreateEmoji { + /// Server name + #[validate(length(min = 1, max = 32), regex = "RE_EMOJI")] + pub name: String, + /// Parent information + pub parent: EmojiParent, + /// Whether the emoji is mature + #[serde(default)] + pub nsfw: bool, + } ); diff --git a/crates/delta/src/routes/channels/group_create.rs b/crates/delta/src/routes/channels/group_create.rs index cf4b3d43..62d0b527 100644 --- a/crates/delta/src/routes/channels/group_create.rs +++ b/crates/delta/src/routes/channels/group_create.rs @@ -87,6 +87,8 @@ mod test { assert_eq!(owner, user.id); assert_eq!(recipients.len(), 1); assert!(harness.db.fetch_channel(&id).await.is_ok()); + + // TODO: does not check for events } _ => unreachable!(), } diff --git a/crates/delta/src/routes/channels/message_query_stale.rs b/crates/delta/src/routes/channels/message_query_stale.rs deleted file mode 100644 index 015b6ad3..00000000 --- a/crates/delta/src/routes/channels/message_query_stale.rs +++ /dev/null @@ -1,26 +0,0 @@ -use revolt_quark::{models::User, Ref, Result}; - -use rocket::serde::json::Json; -use serde::{Deserialize, Serialize}; -use validator::Validate; - -/// # Query Parameters -#[derive(Validate, Serialize, Deserialize, JsonSchema)] -pub struct OptionsQueryStale { - /// Array of message IDs - #[validate(length(min = 0, max = 150))] - ids: Vec, -} - -/// # Poll Message Changes -/// -/// This route returns any changed message objects and tells you if any have been deleted. -/// -/// Don't actually poll this route, instead use this to update your local database. -/// -/// **DEPRECATED** -#[openapi(tag = "Messaging")] -#[post("/<_target>/messages/stale", data = "<_data>")] -pub async fn req(_user: User, _target: Ref, _data: Json) -> Result<()> { - Ok(()) -} diff --git a/crates/delta/src/routes/channels/mod.rs b/crates/delta/src/routes/channels/mod.rs index 4204bd14..114df3d4 100644 --- a/crates/delta/src/routes/channels/mod.rs +++ b/crates/delta/src/routes/channels/mod.rs @@ -16,7 +16,6 @@ mod message_delete; mod message_edit; mod message_fetch; mod message_query; -mod message_query_stale; mod message_react; mod message_search; mod message_send; @@ -38,7 +37,6 @@ pub fn routes() -> (Vec, OpenApi) { message_send::message_send, message_query::req, message_search::req, - message_query_stale::req, message_fetch::req, message_edit::req, message_bulk_delete::req, diff --git a/crates/delta/src/routes/customisation/emoji_create.rs b/crates/delta/src/routes/customisation/emoji_create.rs index dcf28164..69917091 100644 --- a/crates/delta/src/routes/customisation/emoji_create.rs +++ b/crates/delta/src/routes/customisation/emoji_create.rs @@ -1,31 +1,11 @@ -use once_cell::sync::Lazy; -use regex::Regex; -use revolt_quark::models::emoji::EmojiParent; -use revolt_quark::models::{Emoji, File, User}; -use revolt_quark::variables::delta::MAX_EMOJI_COUNT; -use revolt_quark::{perms, Db, Error, Permission, Result}; -use serde::Deserialize; +use revolt_config::config; +use revolt_database::{util::permissions::DatabasePermissionQuery, Database, Emoji, File, User}; +use revolt_models::v0; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_result::{create_error, Result}; use validator::Validate; -use rocket::serde::json::Json; - -/// Regex for valid emoji names -/// -/// Alphanumeric and underscores -pub static RE_EMOJI: Lazy = Lazy::new(|| Regex::new(r"^[a-z0-9_]+$").unwrap()); - -/// # Emoji Data -#[derive(Validate, Deserialize, JsonSchema)] -pub struct DataCreateEmoji { - /// Server name - #[validate(length(min = 1, max = 32), regex = "RE_EMOJI")] - name: String, - /// Parent information - parent: EmojiParent, - /// Whether the emoji is mature - #[serde(default)] - nsfw: bool, -} +use rocket::{serde::json::Json, State}; /// # Create New Emoji /// @@ -33,41 +13,45 @@ pub struct DataCreateEmoji { #[openapi(tag = "Emojis")] #[put("/emoji/", data = "")] pub async fn create_emoji( - db: &Db, + db: &State, user: User, id: String, - data: Json, -) -> Result> { + data: Json, +) -> Result> { + let config = config().await; + 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() + }) + })?; // Bots cannot manage emojis if user.bot.is_some() { - return Err(Error::IsBot); + return Err(create_error!(IsBot)); } // Validate we have permission to write into parent match &data.parent { - EmojiParent::Server { id } => { + v0::EmojiParent::Server { id } => { let server = db.fetch_server(id).await?; // Check for permission - perms(&user) - .server(&server) - .throw_permission(db, Permission::ManageCustomisation) - .await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + calculate_server_permissions(&mut query) + .await + .throw_if_lacking_channel_permission(ChannelPermission::ManageCustomisation)?; - // Check that there are no more than 100 emoji - // ! FIXME: hardcoded upper limit + // Check that we haven't hit the emoji limit let emojis = db.fetch_emoji_by_parent_id(&server.id).await?; - if emojis.len() > *MAX_EMOJI_COUNT { - return Err(Error::TooManyEmoji { - max: *MAX_EMOJI_COUNT, - }); + if emojis.len() > config.features.limits.default.server_emoji { + return Err(create_error!(TooManyEmoji { + max: config.features.limits.default.server_emoji, + })); } } - EmojiParent::Detached => return Err(Error::InvalidOperation), + v0::EmojiParent::Detached => return Err(create_error!(InvalidOperation)), }; // Find the relevant attachment @@ -76,7 +60,7 @@ pub async fn create_emoji( // Create the emoji object let emoji = Emoji { id, - parent: data.parent, + parent: data.parent.into(), creator_id: user.id, name: data.name, animated: "image/gif" == &attachment.content_type, @@ -85,5 +69,5 @@ pub async fn create_emoji( // Save emoji emoji.create(db).await?; - Ok(Json(emoji)) + Ok(Json(emoji.into())) } diff --git a/crates/delta/src/routes/customisation/emoji_delete.rs b/crates/delta/src/routes/customisation/emoji_delete.rs index 24f590f3..ad647e5b 100644 --- a/crates/delta/src/routes/customisation/emoji_delete.rs +++ b/crates/delta/src/routes/customisation/emoji_delete.rs @@ -1,19 +1,30 @@ -use revolt_quark::models::{emoji::EmojiParent, User}; -use revolt_quark::{perms, Db, EmptyResponse, Error, Permission, Ref, Result}; +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, EmojiParent, User, +}; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_result::{create_error, Result}; + +use rocket::State; +use rocket_empty::EmptyResponse; /// # Delete Emoji /// /// Delete an emoji by its id. #[openapi(tag = "Emojis")] -#[delete("/emoji/")] -pub async fn delete_emoji(db: &Db, user: User, id: Ref) -> Result { +#[delete("/emoji/")] +pub async fn delete_emoji( + db: &State, + user: User, + emoji_id: Reference, +) -> Result { // Bots cannot manage emoji if user.bot.is_some() { - return Err(Error::IsBot); + return Err(create_error!(IsBot)); } // Fetch the emoji - let emoji = id.as_emoji(db).await?; + let emoji = emoji_id.as_emoji(db).await?; // If we uploaded the emoji, then we have permission to delete it if emoji.creator_id != user.id { @@ -23,10 +34,10 @@ pub async fn delete_emoji(db: &Db, user: User, id: Ref) -> Result let server = db.fetch_server(id).await?; // Check for permission - perms(&user) - .server(&server) - .throw_permission(db, Permission::ManageCustomisation) - .await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + calculate_server_permissions(&mut query) + .await + .throw_if_lacking_channel_permission(ChannelPermission::ManageCustomisation)?; } EmojiParent::Detached => return Ok(EmptyResponse), }; diff --git a/crates/delta/src/routes/customisation/emoji_fetch.rs b/crates/delta/src/routes/customisation/emoji_fetch.rs index 30965192..375ebe59 100644 --- a/crates/delta/src/routes/customisation/emoji_fetch.rs +++ b/crates/delta/src/routes/customisation/emoji_fetch.rs @@ -1,13 +1,18 @@ -use revolt_quark::models::{Emoji, User}; -use revolt_quark::{Db, Ref, Result}; +use revolt_database::{util::reference::Reference, Database}; +use revolt_models::v0; +use revolt_result::Result; -use rocket::serde::json::Json; +use rocket::{serde::json::Json, State}; /// # Fetch Emoji /// /// Fetch an emoji by its id. #[openapi(tag = "Emojis")] -#[get("/emoji/")] -pub async fn fetch_emoji(db: &Db, _user: User, id: Ref) -> Result> { - id.as_emoji(db).await.map(Json) +#[get("/emoji/")] +pub async fn fetch_emoji(db: &State, emoji_id: Reference) -> Result> { + emoji_id + .as_emoji(db) + .await + .map(|emoji| emoji.into()) + .map(Json) }