From a7e0c42ee4fb5cabe8135c8bde3286b19ec1f2e3 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Thu, 7 Jul 2022 13:23:31 +0100 Subject: [PATCH] feat: add emoji --- .../src/routes/customisation/emoji_create.rs | 75 +++++++++++++++++++ .../src/routes/customisation/emoji_delete.rs | 36 +++++++++ .../src/routes/customisation/emoji_fetch.rs | 13 ++++ crates/delta/src/routes/customisation/mod.rs | 14 ++++ crates/delta/src/routes/mod.rs | 8 ++ crates/delta/src/routes/servers/emoji_list.rs | 17 +++++ crates/delta/src/routes/servers/mod.rs | 29 ++++--- crates/delta/src/util/regex.rs | 5 ++ crates/quark/src/events/client.rs | 9 ++- crates/quark/src/events/impl.rs | 12 +++ crates/quark/src/impl/dummy/media/emoji.rs | 41 ++++++++++ crates/quark/src/impl/dummy/mod.rs | 1 + .../src/impl/generic/media/attachment.rs | 5 ++ crates/quark/src/impl/generic/media/emoji.rs | 36 +++++++++ crates/quark/src/impl/generic/mod.rs | 1 + .../impl/mongo/admin/migrations/scripts.rs | 35 ++++++++- crates/quark/src/impl/mongo/media/emoji.rs | 48 ++++++++++++ crates/quark/src/impl/mongo/mod.rs | 1 + crates/quark/src/impl/mongo/servers/server.rs | 14 ++++ crates/quark/src/models/media/emoji.rs | 24 ++++++ crates/quark/src/models/mod.rs | 2 + .../quark/src/permissions/defn/permission.rs | 5 +- crates/quark/src/traits/media/attachment.rs | 9 +++ crates/quark/src/traits/media/emoji.rs | 20 +++++ crates/quark/src/traits/mod.rs | 6 +- crates/quark/src/util/ref.rs | 7 +- crates/quark/src/util/result.rs | 2 + docker-compose.yml | 2 +- 28 files changed, 453 insertions(+), 24 deletions(-) create mode 100644 crates/delta/src/routes/customisation/emoji_create.rs create mode 100644 crates/delta/src/routes/customisation/emoji_delete.rs create mode 100644 crates/delta/src/routes/customisation/emoji_fetch.rs create mode 100644 crates/delta/src/routes/customisation/mod.rs create mode 100644 crates/delta/src/routes/servers/emoji_list.rs create mode 100644 crates/quark/src/impl/dummy/media/emoji.rs create mode 100644 crates/quark/src/impl/generic/media/emoji.rs create mode 100644 crates/quark/src/impl/mongo/media/emoji.rs create mode 100644 crates/quark/src/models/media/emoji.rs create mode 100644 crates/quark/src/traits/media/emoji.rs diff --git a/crates/delta/src/routes/customisation/emoji_create.rs b/crates/delta/src/routes/customisation/emoji_create.rs new file mode 100644 index 00000000..b2440069 --- /dev/null +++ b/crates/delta/src/routes/customisation/emoji_create.rs @@ -0,0 +1,75 @@ +use revolt_quark::models::emoji::EmojiParent; +use revolt_quark::models::{Emoji, File, User}; +use revolt_quark::{perms, Db, Error, Permission, Result}; +use serde::Deserialize; +use validator::Validate; + +use crate::util::regex::RE_EMOJI; + +use rocket::serde::json::Json; + +/// # Emoji Data +#[derive(Validate, Deserialize, JsonSchema)] +pub struct DataCreateEmoji { + /// Server name + #[validate(length(min = 1, max = 32), regex = "RE_EMOJI")] + name: String, + parent: EmojiParent, +} + +/// # Create New Emoji +/// +/// Create an emoji by its Autumn upload id. +#[openapi(tag = "Emojis")] +#[put("/emoji/", data = "")] +pub async fn create_emoji( + db: &Db, + user: User, + id: String, + data: Json, +) -> Result> { + let data = data.into_inner(); + data.validate() + .map_err(|error| Error::FailedValidation { error })?; + + // Bots cannot manage emojis + if user.bot.is_some() { + return Err(Error::IsBot); + } + + // Validate we have permission to write into parent + match &data.parent { + EmojiParent::Server { id } => { + let server = db.fetch_server(id).await?; + + // Check for permission + perms(&user) + .server(&server) + .throw_permission(db, Permission::ManageCustomisation) + .await?; + + // Check that there are no more than 100 emoji + // ! FIXME: hardcoded upper limit + let emojis = db.fetch_emoji_by_parent_id(&server.id).await?; + if emojis.len() > 100 { + return Err(Error::TooManyEmoji); + } + } + }; + + // Find the relevant attachment + let attachment = File::use_emoji(db, &id, &id).await?; + + // Create the emoji object + let emoji = Emoji { + id, + parent: data.parent, + creator_id: user.id, + name: data.name, + animated: "image/gif" == &attachment.content_type, + }; + + // Save emoji + emoji.create(db).await?; + Ok(Json(emoji)) +} diff --git a/crates/delta/src/routes/customisation/emoji_delete.rs b/crates/delta/src/routes/customisation/emoji_delete.rs new file mode 100644 index 00000000..24def4cc --- /dev/null +++ b/crates/delta/src/routes/customisation/emoji_delete.rs @@ -0,0 +1,36 @@ +use revolt_quark::models::{emoji::EmojiParent, User}; +use revolt_quark::{perms, Db, EmptyResponse, Error, Permission, Ref, Result}; + +/// # 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 { + // Bots cannot manage emoji + if user.bot.is_some() { + return Err(Error::IsBot); + } + + // Fetch the emoji + let emoji = id.as_emoji(db).await?; + + // If we uploaded the emoji, then we have permission to delete it + if emoji.creator_id != user.id { + // Otherwise, validate we have permission to delete from parent + match &emoji.parent { + EmojiParent::Server { id } => { + let server = db.fetch_server(id).await?; + + // Check for permission + perms(&user) + .server(&server) + .throw_permission(db, Permission::ManageCustomisation) + .await?; + } + }; + } + + // Delete the emoji + emoji.delete(db).await.map(|_| EmptyResponse) +} diff --git a/crates/delta/src/routes/customisation/emoji_fetch.rs b/crates/delta/src/routes/customisation/emoji_fetch.rs new file mode 100644 index 00000000..30965192 --- /dev/null +++ b/crates/delta/src/routes/customisation/emoji_fetch.rs @@ -0,0 +1,13 @@ +use revolt_quark::models::{Emoji, User}; +use revolt_quark::{Db, Ref, Result}; + +use rocket::serde::json::Json; + +/// # 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) +} diff --git a/crates/delta/src/routes/customisation/mod.rs b/crates/delta/src/routes/customisation/mod.rs new file mode 100644 index 00000000..19ce099a --- /dev/null +++ b/crates/delta/src/routes/customisation/mod.rs @@ -0,0 +1,14 @@ +use rocket::Route; +use rocket_okapi::okapi::openapi3::OpenApi; + +mod emoji_create; +mod emoji_delete; +mod emoji_fetch; + +pub fn routes() -> (Vec, OpenApi) { + openapi_get_routes_spec![ + emoji_create::create_emoji, + emoji_delete::delete_emoji, + emoji_fetch::fetch_emoji + ] +} diff --git a/crates/delta/src/routes/mod.rs b/crates/delta/src/routes/mod.rs index 4b384f35..716b8051 100644 --- a/crates/delta/src/routes/mod.rs +++ b/crates/delta/src/routes/mod.rs @@ -5,6 +5,7 @@ use rocket_okapi::{okapi::openapi3::OpenApi, settings::OpenApiSettings}; mod bots; mod channels; +mod customisation; mod invites; mod onboard; mod push; @@ -25,6 +26,7 @@ pub fn mount(mut rocket: Rocket) -> Rocket { "/channels" => channels::routes(), "/servers" => servers::routes(), "/invites" => invites::routes(), + "/custom" => customisation::routes(), "/auth/account" => rocket_rauth::routes::account::routes(), "/auth/session" => rocket_rauth::routes::session::routes(), "/auth/mfa" => rocket_rauth::routes::mfa::routes(), @@ -96,6 +98,12 @@ fn custom_openapi_spec() -> OpenApi { "Invites" ] }, + { + "name": "Customisation", + "tags": [ + "Emojis" + ] + }, { "name": "Authentication", "tags": [ diff --git a/crates/delta/src/routes/servers/emoji_list.rs b/crates/delta/src/routes/servers/emoji_list.rs new file mode 100644 index 00000000..4df08437 --- /dev/null +++ b/crates/delta/src/routes/servers/emoji_list.rs @@ -0,0 +1,17 @@ +use revolt_quark::models::{Emoji, User}; +use revolt_quark::{perms, Db, Ref, Result}; + +use rocket::serde::json::Json; + +/// # 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>> { + let server = target.as_server(db).await?; + perms(&user).server(&server).calc(db).await?; + + // Fetch all emoji from server if we can view it + db.fetch_emoji_by_parent_id(&server.id).await.map(Json) +} diff --git a/crates/delta/src/routes/servers/mod.rs b/crates/delta/src/routes/servers/mod.rs index 91c24752..8cea54f2 100644 --- a/crates/delta/src/routes/servers/mod.rs +++ b/crates/delta/src/routes/servers/mod.rs @@ -1,30 +1,26 @@ use rocket::Route; use rocket_okapi::okapi::openapi3::OpenApi; -mod server_ack; -mod server_create; -mod server_delete; -mod server_edit; -mod server_fetch; - -mod channel_create; - -mod member_edit; -mod member_fetch; -mod member_fetch_all; -mod member_remove; - mod ban_create; mod ban_list; mod ban_remove; - +mod channel_create; +mod emoji_list; mod invites_fetch; - +mod member_edit; +mod member_fetch; +mod member_fetch_all; +mod member_remove; mod permissions_set; mod permissions_set_default; mod roles_create; mod roles_delete; mod roles_edit; +mod server_ack; +mod server_create; +mod server_delete; +mod server_edit; +mod server_fetch; pub fn routes() -> (Vec, OpenApi) { openapi_get_routes_spec![ @@ -46,6 +42,7 @@ pub fn routes() -> (Vec, OpenApi) { roles_edit::req, roles_delete::req, permissions_set::req, - permissions_set_default::req + permissions_set_default::req, + emoji_list::list_emoji ] } diff --git a/crates/delta/src/util/regex.rs b/crates/delta/src/util/regex.rs index 7e78cf99..1c5b90d1 100644 --- a/crates/delta/src/util/regex.rs +++ b/crates/delta/src/util/regex.rs @@ -7,3 +7,8 @@ use regex::Regex; /// Block lookalike characters pub static RE_USERNAME: Lazy = Lazy::new(|| Regex::new(r"^[^\u200BА-Яа-яΑ-Ωα-ω@#:\n\r\[\]]+$").unwrap()); + +/// Regex for valid emoji names +/// +/// Alphanumeric and underscores +pub static RE_EMOJI: Lazy = Lazy::new(|| Regex::new(r"^[a-zA-Z0-9_]+$").unwrap()); diff --git a/crates/quark/src/events/client.rs b/crates/quark/src/events/client.rs index 3dd2d027..a46d9411 100644 --- a/crates/quark/src/events/client.rs +++ b/crates/quark/src/events/client.rs @@ -5,7 +5,7 @@ use crate::models::message::{AppendMessage, PartialMessage}; use crate::models::server::{FieldsRole, FieldsServer, PartialRole, PartialServer}; use crate::models::server_member::{FieldsMember, MemberCompositeKey, PartialMember}; use crate::models::user::{FieldsUser, PartialUser, RelationshipStatus}; -use crate::models::{Channel, Member, Message, Server, User, UserSettings}; +use crate::models::{Channel, Emoji, Member, Message, Server, User, UserSettings}; use crate::Error; /// WebSocket Client Errors @@ -52,6 +52,7 @@ pub enum EventV1 { servers: Vec, channels: Vec, members: Vec, + emojis: Option>, }, /// Ping response @@ -170,4 +171,10 @@ pub enum EventV1 { /// Settings updated remotely UserSettingsUpdate { id: String, update: UserSettings }, + + /// New emoji + EmojiCreate(Emoji), + + /// Delete emoji + EmojiDelete { id: String }, } diff --git a/crates/quark/src/events/impl.rs b/crates/quark/src/events/impl.rs index 60dabb60..31cf56e9 100644 --- a/crates/quark/src/events/impl.rs +++ b/crates/quark/src/events/impl.rs @@ -151,6 +151,17 @@ impl State { ) .await?; + // Fetch customisations. + let emojis = Some( + db.fetch_emoji_by_parent_ids( + &servers + .iter() + .map(|x| x.id.to_string()) + .collect::>(), + ) + .await?, + ); + // Copy data into local state cache. self.cache.users = users.iter().cloned().map(|x| (x.id.clone(), x)).collect(); self.cache @@ -202,6 +213,7 @@ impl State { servers, channels, members, + emojis, }) } diff --git a/crates/quark/src/impl/dummy/media/emoji.rs b/crates/quark/src/impl/dummy/media/emoji.rs new file mode 100644 index 00000000..82847fc9 --- /dev/null +++ b/crates/quark/src/impl/dummy/media/emoji.rs @@ -0,0 +1,41 @@ +use crate::models::emoji::EmojiParent; +use crate::models::Emoji; +use crate::{AbstractEmoji, Result}; + +use super::super::DummyDb; + +#[async_trait] +impl AbstractEmoji for DummyDb { + /// Fetch an emoji by its id + async fn fetch_emoji(&self, id: &str) -> Result { + Ok(Emoji { + id: id.into(), + name: id.into(), + parent: EmojiParent::Server { id: id.into() }, + creator_id: id.into(), + animated: false, + }) + } + + /// Fetch emoji by their ids + async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result> { + Ok(vec![self.fetch_emoji(parent_id).await?]) + } + + /// Fetch emoji by their parent ids + async fn fetch_emoji_by_parent_ids(&self, _parent_ids: &[String]) -> Result> { + Ok(vec![]) + } + + /// Insert emoji into database. + async fn insert_emoji(&self, emoji: &Emoji) -> Result<()> { + info!("Insert {emoji:?}"); + Ok(()) + } + + /// Delete an emoji by its id + async fn delete_emoji(&self, emoji: &Emoji) -> Result<()> { + info!("Delete {emoji:?}"); + Ok(()) + } +} diff --git a/crates/quark/src/impl/dummy/mod.rs b/crates/quark/src/impl/dummy/mod.rs index b39439b3..cfb9b8ea 100644 --- a/crates/quark/src/impl/dummy/mod.rs +++ b/crates/quark/src/impl/dummy/mod.rs @@ -6,6 +6,7 @@ pub mod admin { pub mod media { pub mod attachment; + pub mod emoji; } pub mod channels { diff --git a/crates/quark/src/impl/generic/media/attachment.rs b/crates/quark/src/impl/generic/media/attachment.rs index 11b7dd9d..a21a13b8 100644 --- a/crates/quark/src/impl/generic/media/attachment.rs +++ b/crates/quark/src/impl/generic/media/attachment.rs @@ -30,4 +30,9 @@ impl File { db.find_and_use_attachment(id, "banners", "server", parent) .await } + + pub async fn use_emoji(db: &Database, id: &str, parent: &str) -> Result { + db.find_and_use_attachment(id, "emojis", "object", parent) + .await + } } diff --git a/crates/quark/src/impl/generic/media/emoji.rs b/crates/quark/src/impl/generic/media/emoji.rs new file mode 100644 index 00000000..dade6736 --- /dev/null +++ b/crates/quark/src/impl/generic/media/emoji.rs @@ -0,0 +1,36 @@ +use crate::{ + events::client::EventV1, + models::{emoji::EmojiParent, Emoji}, + Database, Result, +}; + +impl Emoji { + /// Get parent id + fn parent(&self) -> &str { + match &self.parent { + EmojiParent::Server { id } => id, + } + } + + /// Create an emoji + pub async fn create(&self, db: &Database) -> Result<()> { + db.insert_emoji(self).await?; + EventV1::EmojiCreate(self.clone()) + .p(self.parent().to_string()) + .await; + + Ok(()) + } + + /// Delete an emoji + pub async fn delete(self, db: &Database) -> Result<()> { + EventV1::EmojiDelete { + id: self.id.to_string(), + } + .p(self.parent().to_string()) + .await; + + db.mark_attachment_as_deleted(&self.id).await?; + db.delete_emoji(&self).await + } +} diff --git a/crates/quark/src/impl/generic/mod.rs b/crates/quark/src/impl/generic/mod.rs index 743ceecf..4c963b06 100644 --- a/crates/quark/src/impl/generic/mod.rs +++ b/crates/quark/src/impl/generic/mod.rs @@ -6,6 +6,7 @@ pub mod admin { pub mod media { pub mod attachment; + pub mod emoji; } pub mod channels { diff --git a/crates/quark/src/impl/mongo/admin/migrations/scripts.rs b/crates/quark/src/impl/mongo/admin/migrations/scripts.rs index 37f0a728..6f8c08d5 100644 --- a/crates/quark/src/impl/mongo/admin/migrations/scripts.rs +++ b/crates/quark/src/impl/mongo/admin/migrations/scripts.rs @@ -16,7 +16,7 @@ struct MigrationInfo { revision: i32, } -pub const LATEST_REVISION: i32 = 16; +pub const LATEST_REVISION: i32 = 17; pub async fn migrate_database(db: &MongoDb) { let migrations = db.col::("migrations"); @@ -611,6 +611,39 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { .unwrap(); } + if revision <= 16 { + info!("Running migration [revision 16 / 07-07-2022]: Add `emojis` collection and rAuth migration."); + + let rauth_db = rauth::Database::MongoDb(rauth::database::MongoDb(db.db())); + rauth_db + .run_migration(rauth::Migration::M2022_06_09AddIndexForDeletion) + .await + .unwrap(); + + db.db() + .create_collection("emojis", None) + .await + .expect("Failed to create emojis collection."); + + db.db() + .run_command( + doc! { + "createIndexes": "emojis", + "indexes": [ + { + "key": { + "parent.id": 1_i32, + }, + "name": "parent_id" + } + ] + }, + None, + ) + .await + .expect("Failed to create emoji parent index."); + } + // Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`. // Reminder to update LATEST_REVISION when adding new migrations. diff --git a/crates/quark/src/impl/mongo/media/emoji.rs b/crates/quark/src/impl/mongo/media/emoji.rs new file mode 100644 index 00000000..596bef0e --- /dev/null +++ b/crates/quark/src/impl/mongo/media/emoji.rs @@ -0,0 +1,48 @@ +use crate::models::Emoji; +use crate::{AbstractEmoji, Result}; + +use super::super::MongoDb; + +static COL: &str = "emojis"; + +#[async_trait] +impl AbstractEmoji for MongoDb { + /// Fetch an emoji by its id + async fn fetch_emoji(&self, id: &str) -> Result { + self.find_one_by_id(COL, id).await + } + + /// Fetch emoji by their ids + async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result> { + self.find( + COL, + doc! { + "parent.id": parent_id + }, + ) + .await + } + + /// Fetch emoji by their parent ids + async fn fetch_emoji_by_parent_ids(&self, parent_ids: &[String]) -> Result> { + self.find( + COL, + doc! { + "parent.id": { + "$in": parent_ids + } + }, + ) + .await + } + + /// Insert emoji into database. + async fn insert_emoji(&self, emoji: &Emoji) -> Result<()> { + self.insert_one(COL, emoji).await.map(|_| ()) + } + + /// Delete an emoji by its id + async fn delete_emoji(&self, emoji: &Emoji) -> Result<()> { + self.delete_one_by_id(COL, &emoji.id).await.map(|_| ()) + } +} diff --git a/crates/quark/src/impl/mongo/mod.rs b/crates/quark/src/impl/mongo/mod.rs index 230e53a8..fc4d7a2e 100644 --- a/crates/quark/src/impl/mongo/mod.rs +++ b/crates/quark/src/impl/mongo/mod.rs @@ -17,6 +17,7 @@ pub mod admin { pub mod media { pub mod attachment; + pub mod emoji; } pub mod channels { diff --git a/crates/quark/src/impl/mongo/servers/server.rs b/crates/quark/src/impl/mongo/servers/server.rs index b8efabdb..babf897f 100644 --- a/crates/quark/src/impl/mongo/servers/server.rs +++ b/crates/quark/src/impl/mongo/servers/server.rs @@ -18,6 +18,20 @@ impl MongoDb { }) .await?; + // Delete all emoji. + self.col::("emojis") + .delete_many( + doc! { + "parent.id": &server.id + }, + None, + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "delete_many", + with: "emojis", + })?; + // Delete all channels. self.col::("channels") .delete_many( diff --git a/crates/quark/src/models/media/emoji.rs b/crates/quark/src/models/media/emoji.rs new file mode 100644 index 00000000..b1c35d37 --- /dev/null +++ b/crates/quark/src/models/media/emoji.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; + +/// Information about what owns this emoji +#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)] +#[serde(tag = "type")] +pub enum EmojiParent { + Server { id: String }, +} + +/// Representation of an Emoji on Revolt +#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)] +pub struct Emoji { + /// Unique Id + #[serde(rename = "_id")] + pub id: String, + /// What owns this emoji + pub parent: EmojiParent, + /// Uploader user id + pub creator_id: String, + /// Emoji name + pub name: String, + /// Whether the emoji is animated + pub animated: bool, +} diff --git a/crates/quark/src/models/mod.rs b/crates/quark/src/models/mod.rs index c926dc1a..20bf360e 100644 --- a/crates/quark/src/models/mod.rs +++ b/crates/quark/src/models/mod.rs @@ -5,6 +5,7 @@ mod admin { mod media { pub mod attachment; + pub mod emoji; } mod channels { @@ -37,6 +38,7 @@ pub use bot::Bot; pub use channel::Channel; pub use channel_invite::Invite; pub use channel_unread::ChannelUnread; +pub use emoji::Emoji; pub use message::Message; pub use migrations::MigrationInfo; pub use server::Server; diff --git a/crates/quark/src/permissions/defn/permission.rs b/crates/quark/src/permissions/defn/permission.rs index 95cf85fe..881fcd71 100644 --- a/crates/quark/src/permissions/defn/permission.rs +++ b/crates/quark/src/permissions/defn/permission.rs @@ -21,8 +21,10 @@ pub enum Permission { ManagePermissions = 1 << 2, /// Manage roles on server ManageRole = 1 << 3, + /// Manage server customisation (includes emoji) + ManageCustomisation = 1 << 4, - // % 2 bits reserved + // % 1 bits reserved // * Member permissions /// Kick other members below their ranking @@ -122,6 +124,7 @@ bitfield! { pub can_manage_server, _: 62; pub can_manage_permissions, _: 61; pub can_manage_roles, _: 60; + pub can_manage_customisation, _: 59; // * Member permissions pub can_kick_members, _: 57; diff --git a/crates/quark/src/traits/media/attachment.rs b/crates/quark/src/traits/media/attachment.rs index 121c1483..36918635 100644 --- a/crates/quark/src/traits/media/attachment.rs +++ b/crates/quark/src/traits/media/attachment.rs @@ -3,6 +3,7 @@ use crate::Result; #[async_trait] pub trait AbstractAttachment: Sync + Send { + /// Find an attachment by its details and mark it as used by a given parent. async fn find_and_use_attachment( &self, id: &str, @@ -10,8 +11,16 @@ pub trait AbstractAttachment: Sync + Send { parent_type: &str, parent_id: &str, ) -> Result; + + /// Insert attachment into database. async fn insert_attachment(&self, attachment: &File) -> Result<()>; + + /// Mark an attachment as having been reported. async fn mark_attachment_as_reported(&self, id: &str) -> Result<()>; + + /// Mark an attachment as having been deleted. async fn mark_attachment_as_deleted(&self, id: &str) -> Result<()>; + + /// Mark multiple attachments as having been deleted. async fn mark_attachments_as_deleted(&self, ids: &[String]) -> Result<()>; } diff --git a/crates/quark/src/traits/media/emoji.rs b/crates/quark/src/traits/media/emoji.rs new file mode 100644 index 00000000..b8ed849e --- /dev/null +++ b/crates/quark/src/traits/media/emoji.rs @@ -0,0 +1,20 @@ +use crate::models::Emoji; +use crate::Result; + +#[async_trait] +pub trait AbstractEmoji: Sync + Send { + /// Fetch an emoji by its id + async fn fetch_emoji(&self, id: &str) -> Result; + + /// Fetch emoji by their parent id + async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result>; + + /// Fetch emoji by their parent ids + async fn fetch_emoji_by_parent_ids(&self, parent_ids: &[String]) -> Result>; + + /// Insert emoji into database. + async fn insert_emoji(&self, emoji: &Emoji) -> Result<()>; + + /// Delete an emoji by its id + async fn delete_emoji(&self, emoji: &Emoji) -> Result<()>; +} diff --git a/crates/quark/src/traits/mod.rs b/crates/quark/src/traits/mod.rs index 59894016..93764ed8 100644 --- a/crates/quark/src/traits/mod.rs +++ b/crates/quark/src/traits/mod.rs @@ -4,6 +4,7 @@ mod admin { mod media { pub mod attachment; + pub mod emoji; } mod channels { @@ -28,6 +29,7 @@ mod users { pub use admin::migrations::AbstractMigrations; pub use media::attachment::AbstractAttachment; +pub use media::emoji::AbstractEmoji; pub use channels::channel::AbstractChannel; pub use channels::channel_invite::AbstractChannelInvite; @@ -42,14 +44,12 @@ pub use users::bot::AbstractBot; pub use users::user::AbstractUser; pub use users::user_settings::AbstractUserSettings; -// pub trait AbstractEventEmitter {} -// + AbstractEventEmitter - pub trait AbstractDatabase: Sync + Send + AbstractMigrations + AbstractAttachment + + AbstractEmoji + AbstractChannel + AbstractChannelInvite + AbstractChannelUnread diff --git a/crates/quark/src/util/ref.rs b/crates/quark/src/util/ref.rs index c2223d0d..a6709d04 100644 --- a/crates/quark/src/util/ref.rs +++ b/crates/quark/src/util/ref.rs @@ -4,7 +4,7 @@ use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::models::{Bot, Channel, Invite, Member, Message, Server, ServerBan, User}; +use crate::models::{Bot, Channel, Emoji, Invite, Member, Message, Server, ServerBan, User}; use crate::presence::presence_is_online; use crate::{Database, Result}; @@ -63,6 +63,11 @@ impl Ref { pub async fn as_ban(&self, db: &Database, server: &str) -> Result { db.fetch_ban(server, &self.id).await } + + /// Fetch emoji from Ref + pub async fn as_emoji(&self, db: &Database) -> Result { + db.fetch_emoji(&self.id).await + } } impl<'r> FromParam<'r> for Ref { diff --git a/crates/quark/src/util/result.rs b/crates/quark/src/util/result.rs index 54b26488..0f4fb6ca 100644 --- a/crates/quark/src/util/result.rs +++ b/crates/quark/src/util/result.rs @@ -56,6 +56,7 @@ pub enum Error { TooManyServers { max: usize, }, + TooManyEmoji, // ? Bot related errors. ReachedMaximumBots, @@ -156,6 +157,7 @@ impl<'r> Responder<'r, 'static> for Error { Error::InvalidRole => Status::NotFound, Error::Banned => Status::Forbidden, Error::TooManyServers { .. } => Status::Forbidden, + Error::TooManyEmoji => Status::BadRequest, Error::ReachedMaximumBots => Status::BadRequest, Error::IsBot => Status::BadRequest, diff --git a/docker-compose.yml b/docker-compose.yml index 68e03386..6852cc96 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,7 +54,7 @@ services: " # File server (autumn) autumn: - image: ghcr.io/revoltchat/autumn:1.1.4 + image: ghcr.io/revoltchat/autumn:1.1.5 env_file: .env depends_on: - database