From 793061685ddd1648dc492dab61a5544ee287c450 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 24 Aug 2026 10:57:44 -0700 Subject: [PATCH] feat: implement discover endpoints (#940) * feat: implement discover endpoints Signed-off-by: IAmTomahawkx Release-As: 0.15.2 --- crates/core/database/src/drivers/reference.rs | 9 +- .../admin_migrations/ops/mongodb/init.rs | 19 ++++ .../admin_migrations/ops/mongodb/scripts.rs | 48 ++++++++-- .../src/models/discover_requests/mod.rs | 5 ++ .../src/models/discover_requests/model.rs | 36 ++++++++ .../src/models/discover_requests/ops.rs | 35 ++++++++ .../models/discover_requests/ops/mongodb.rs | 90 +++++++++++++++++++ .../models/discover_requests/ops/reference.rs | 77 ++++++++++++++++ crates/core/database/src/models/mod.rs | 19 ++-- crates/core/database/src/util/bridge/v0.rs | 40 +++++++++ crates/core/models/src/v0/discover.rs | 35 ++++++++ crates/core/models/src/v0/mod.rs | 14 +-- crates/core/result/src/axum.rs | 2 + crates/core/result/src/lib.rs | 5 ++ crates/core/result/src/rocket.rs | 2 + .../routes/bots/discover/discover_add_bot.rs | 41 +++++++++ .../routes/bots/discover/discover_get_bot.rs | 43 +++++++++ .../bots/discover/discover_remove_bot.rs | 55 ++++++++++++ crates/delta/src/routes/bots/discover/mod.rs | 3 + crates/delta/src/routes/bots/mod.rs | 4 + .../routes/servers/discover/discover_add.rs | 41 +++++++++ .../routes/servers/discover/discover_get.rs | 43 +++++++++ .../servers/discover/discover_remove.rs | 55 ++++++++++++ .../delta/src/routes/servers/discover/mod.rs | 3 + crates/delta/src/routes/servers/mod.rs | 4 + 25 files changed, 703 insertions(+), 25 deletions(-) create mode 100644 crates/core/database/src/models/discover_requests/mod.rs create mode 100644 crates/core/database/src/models/discover_requests/model.rs create mode 100644 crates/core/database/src/models/discover_requests/ops.rs create mode 100644 crates/core/database/src/models/discover_requests/ops/mongodb.rs create mode 100644 crates/core/database/src/models/discover_requests/ops/reference.rs create mode 100644 crates/core/models/src/v0/discover.rs create mode 100644 crates/delta/src/routes/bots/discover/discover_add_bot.rs create mode 100644 crates/delta/src/routes/bots/discover/discover_get_bot.rs create mode 100644 crates/delta/src/routes/bots/discover/discover_remove_bot.rs create mode 100644 crates/delta/src/routes/bots/discover/mod.rs create mode 100644 crates/delta/src/routes/servers/discover/discover_add.rs create mode 100644 crates/delta/src/routes/servers/discover/discover_get.rs create mode 100644 crates/delta/src/routes/servers/discover/discover_remove.rs create mode 100644 crates/delta/src/routes/servers/discover/mod.rs diff --git a/crates/core/database/src/drivers/reference.rs b/crates/core/database/src/drivers/reference.rs index 014f3fbe..714d6932 100644 --- a/crates/core/database/src/drivers/reference.rs +++ b/crates/core/database/src/drivers/reference.rs @@ -3,9 +3,10 @@ use std::{collections::HashMap, sync::Arc}; use futures::lock::Mutex; use crate::{ - Account, AccountInvite, AuditLogEntry, Bot, Channel, ChannelCompositeKey, ChannelUnread, Emoji, - File, FileHash, Invite, MFATicket, Member, MemberCompositeKey, Message, PolicyChange, - RatelimitEvent, Report, Server, ServerBan, Session, Snapshot, User, UserSettings, Webhook, + Account, AccountInvite, AuditLogEntry, Bot, Channel, ChannelCompositeKey, ChannelUnread, + DiscoverBan, DiscoverRequest, DiscoverRequestType, Emoji, File, FileHash, Invite, MFATicket, + Member, MemberCompositeKey, Message, PolicyChange, RatelimitEvent, Report, Server, ServerBan, + Session, Snapshot, User, UserSettings, Webhook, }; database_derived!( @@ -19,6 +20,8 @@ database_derived!( pub channel_unreads: Arc>>, pub channel_webhooks: Arc>>, pub emojis: Arc>>, + pub discover_requests: Arc>>, + pub discover_bans: Arc>>, pub file_hashes: Arc>>, pub files: Arc>>, pub messages: Arc>>, diff --git a/crates/core/database/src/models/admin_migrations/ops/mongodb/init.rs b/crates/core/database/src/models/admin_migrations/ops/mongodb/init.rs index eadceaf5..231082e7 100644 --- a/crates/core/database/src/models/admin_migrations/ops/mongodb/init.rs +++ b/crates/core/database/src/models/admin_migrations/ops/mongodb/init.rs @@ -113,6 +113,10 @@ pub async fn create_database(db: &MongoDb) { .await .expect("Failed to create mfa_tickets collection."); + db.create_collection("discover_requests") + .await + .expect("Failed to create discover_requests collection"); + db.run_command(doc! { "createIndexes": "users", "indexes": [ @@ -414,5 +418,20 @@ pub async fn create_database(db: &MongoDb) { .await .unwrap(); + db.run_command(doc! { + "createIndexes": "discover_requests", + "indexes": [ + { + "key": { + "request_type": 1, + "request_id": 1 + }, + "name": "request_type_id" + } + ] + }) + .await + .expect("Failed to create discover_requests index"); + info!("Created database."); } diff --git a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs index 2da6e9e6..c2cf0739 100644 --- a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs +++ b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs @@ -26,7 +26,7 @@ struct MigrationInfo { revision: i32, } -pub const LATEST_REVISION: i32 = 52; // MUST BE +1 to last migration +pub const LATEST_REVISION: i32 = 53; // MUST BE +1 to last migration pub async fn migrate_database(db: &MongoDb) { let migrations = db.col::("migrations"); @@ -1299,7 +1299,9 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { } for session in sessions { - let timestamp = iso8601_timestamp::Timestamp::from(Ulid::from_string(&session._id).unwrap().datetime()); + let timestamp = iso8601_timestamp::Timestamp::from( + Ulid::from_string(&session._id).unwrap().datetime(), + ); db.db() .collection::("sessions") @@ -1478,14 +1480,15 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { if revision <= 50 { info!("Running migration [revision 50 / 13-04-2026]: Rename invites collection to account_invites"); - let result = db.db() + let result = db + .db() .client() .database("admin") .run_command(doc! { - "renameCollection": "revolt.invites", - "to": "revolt.account_invites", - "dropTarget": true - }) + "renameCollection": "revolt.invites", + "to": "revolt.account_invites", + "dropTarget": true + }) .await; if let Err(e) = result { @@ -1496,7 +1499,7 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { } } - if revision >= 51 { + if revision <= 51 { info!("Running migration [revision 51 / 28-11-2025]: Add audit logs collection"); db.db() @@ -1529,6 +1532,35 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { .expect("Failed to create audit_logs index"); }; + if revision <= 52 { + let config = revolt_config::config().await; + if config.production { + info!("Running migration [revision 52 / 20-08-2026]: Discover endpoints"); + db.db() + .create_collection("discover_requests") + .await + .expect("Failed to create discover_requests collection"); + + db.db() + .run_command(doc! { + "createIndexes": "discover_requests", + "indexes": [ + { + "key": { + "request_type": 1, + "request_id": 1 + }, + "name": "request_type_id" + } + ] + }) + .await + .expect("Failed to create index"); + } else { + info!("Skipping migration [revision 52 / 20-08-2026]: Discover endpoints"); + } + } + // Reminder to update LATEST_REVISION when adding new migrations. LATEST_REVISION.max(revision) } diff --git a/crates/core/database/src/models/discover_requests/mod.rs b/crates/core/database/src/models/discover_requests/mod.rs new file mode 100644 index 00000000..4d801b73 --- /dev/null +++ b/crates/core/database/src/models/discover_requests/mod.rs @@ -0,0 +1,5 @@ +mod model; +mod ops; + +pub use model::*; +pub use ops::*; diff --git a/crates/core/database/src/models/discover_requests/model.rs b/crates/core/database/src/models/discover_requests/model.rs new file mode 100644 index 00000000..879c95af --- /dev/null +++ b/crates/core/database/src/models/discover_requests/model.rs @@ -0,0 +1,36 @@ +auto_derived!( + #[derive(Hash)] + pub enum DiscoverRequestType { + Bot, + Server, + } + + pub enum DiscoverRequestStatus { + Pending, + UnderReview, + Denied(Option), // reason + Approved(Option), // reason + } + + /// Discover request + pub struct DiscoverRequest { + /// The type of request. + #[serde(rename = "type")] + pub request_type: DiscoverRequestType, + /// The ID of the bot/server + pub request_id: String, + /// status of the request + pub status: DiscoverRequestStatus, + } + + pub struct DiscoverBan { + /// Ban Id + #[serde(rename = "_id")] + pub id: String, + /// The type of item. + #[serde(rename = "type")] + pub item_type: DiscoverRequestType, + /// The ID of the bot/server + pub item_id: String, + } +); diff --git a/crates/core/database/src/models/discover_requests/ops.rs b/crates/core/database/src/models/discover_requests/ops.rs new file mode 100644 index 00000000..7be8d494 --- /dev/null +++ b/crates/core/database/src/models/discover_requests/ops.rs @@ -0,0 +1,35 @@ +use revolt_result::Result; + +use crate::{DiscoverRequest, DiscoverRequestType}; + +#[cfg(feature = "mongodb")] +mod mongodb; +mod reference; + +#[async_trait] +pub trait AbstractDiscoverRequest: Sync + Send { + /// Insert discover request into database. + /// Update an existing one if it was previously denied + async fn insert_discover_request( + &self, + request_type: DiscoverRequestType, + item: &str, + ) -> Result; + + /// Fetch Discover request by their parent id + async fn fetch_discover_request_by_item_id( + &self, + request_type: DiscoverRequestType, + item: &str, + ) -> Result; + + /// Remove Discover request + async fn delete_discover_request( + &self, + request_type: DiscoverRequestType, + item: &str, + ) -> Result<()>; + + /// Fetch if the item is banned from being requested. If the item is Some, then the item is banned. + async fn get_discover_ban(&self, item_type: DiscoverRequestType, item: &str) -> Result; +} diff --git a/crates/core/database/src/models/discover_requests/ops/mongodb.rs b/crates/core/database/src/models/discover_requests/ops/mongodb.rs new file mode 100644 index 00000000..088f273e --- /dev/null +++ b/crates/core/database/src/models/discover_requests/ops/mongodb.rs @@ -0,0 +1,90 @@ +use revolt_result::Result; + +use crate::DiscoverBan; +use crate::DiscoverRequest; +use crate::DiscoverRequestStatus; +use crate::DiscoverRequestType; +use crate::MongoDb; + +use super::AbstractDiscoverRequest; + +static DISCOVER_COL: &str = "discover_requests"; +static DISCOVER_BANS_COL: &str = "discover_bans"; + +#[async_trait] +impl AbstractDiscoverRequest for MongoDb { + /// Insert request into database. + async fn insert_discover_request( + &self, + request_type: DiscoverRequestType, + item: &str, + ) -> Result { + if let Ok(mut prev) = self + .fetch_discover_request_by_item_id(request_type.clone(), item) + .await + { + match prev.status { + DiscoverRequestStatus::Approved(_) + | DiscoverRequestStatus::Pending + | DiscoverRequestStatus::UnderReview => return Err(create_error!(NoEffect)), + _ => Ok(()), + }?; + self.col::(DISCOVER_COL).update_one( + doc! {"request_type": bson::to_bson(&request_type).expect("failed to serialize"), "request_id": item}, + doc! {"$set": {"status": bson::to_bson(&DiscoverRequestStatus::Pending).expect("failed to serialize")}}, + ).await.map_err(|_| create_database_error!("update_one", DISCOVER_COL))?; + + prev.status = DiscoverRequestStatus::Pending; + Ok(prev) + } else { + let ret = DiscoverRequest { + request_type, + request_id: item.to_string(), + status: DiscoverRequestStatus::Pending, + }; + self.col::(DISCOVER_COL) + .insert_one(ret.clone()) + .await + .map_err(|_| create_database_error!("insert_one", DISCOVER_COL))?; + Ok(ret) + } + } + + /// Fetch discover by item type/id combo + async fn fetch_discover_request_by_item_id( + &self, + request_type: DiscoverRequestType, + item: &str, + ) -> Result { + query!( + self, + find_one, + DISCOVER_COL, + doc! {"request_type": bson::to_bson(&request_type).expect("failed to serialize"), "request_id": item} + )?.ok_or_else(|| create_database_error!("find_one", DISCOVER_COL)) + } + + /// Remove discover request + async fn delete_discover_request( + &self, + request_type: DiscoverRequestType, + item: &str, + ) -> Result<()> { + query!( + self, + delete_one, + DISCOVER_COL, + doc! {"request_type": bson::to_bson(&request_type).expect("failed to serialize"), "request_id": item} + ).map(|_| ()) + } + + /// Fetch if the item is banned from being requested + async fn get_discover_ban(&self, item_type: DiscoverRequestType, item: &str) -> Result { + query!( + self, + find_one, + DISCOVER_BANS_COL, + doc! {"request_type": bson::to_bson(&item_type).expect("failed to serialize"), "request_id": item} + )?.ok_or_else(|| create_database_error!("find_one", DISCOVER_COL)).map(|_: DiscoverBan| true) + } +} diff --git a/crates/core/database/src/models/discover_requests/ops/reference.rs b/crates/core/database/src/models/discover_requests/ops/reference.rs new file mode 100644 index 00000000..30ca0110 --- /dev/null +++ b/crates/core/database/src/models/discover_requests/ops/reference.rs @@ -0,0 +1,77 @@ +use revolt_result::Result; + +use crate::ReferenceDb; +use crate::{DiscoverRequest, DiscoverRequestStatus, DiscoverRequestType}; + +use super::AbstractDiscoverRequest; + +#[async_trait] +impl AbstractDiscoverRequest for ReferenceDb { + /// Insert request into database. + async fn insert_discover_request( + &self, + request_type: DiscoverRequestType, + item: &str, + ) -> Result { + let ret = DiscoverRequest { + request_type: request_type.clone(), + request_id: item.to_string(), + status: DiscoverRequestStatus::Pending, + }; + + let mut discover = self.discover_requests.lock().await; + if let std::collections::hash_map::Entry::Vacant(e) = + discover.entry((request_type, item.to_string())) + { + e.insert(ret.clone()); + Ok(ret) + } else { + Err(create_database_error!("insert", "discover_requests")) + } + } + + /// Fetch discover by item type/id combo + async fn fetch_discover_request_by_item_id( + &self, + request_type: DiscoverRequestType, + item: &str, + ) -> Result { + let discover = self.discover_requests.lock().await; + discover + .iter() + .find(|(_, d)| d.request_id == item && d.request_type == request_type) + .map(|(_, d)| d.clone()) + .ok_or_else(|| create_error!(NotFound)) + } + + /// Remove discover request + async fn delete_discover_request( + &self, + request_type: DiscoverRequestType, + item: &str, + ) -> Result<()> { + let discover = self.discover_requests.lock().await; + let req = discover + .iter() + .find(|(_, d)| d.request_id == item && d.request_type == request_type) + .map(|(id, _)| id); + + if let Some(req) = req { + let mut discover = self.discover_requests.lock().await; + discover.remove_entry(req); + Ok(()) + } else { + Err(create_error!(NotFound)) + } + } + + /// Fetch if the item is banned from being requested + async fn get_discover_ban(&self, item_type: DiscoverRequestType, item: &str) -> Result { + let discover = self.discover_bans.lock().await; + discover + .iter() + .find(|(_, d)| d.item_id == item && d.item_type == item_type) + .map(|(_, _)| true) + .ok_or_else(|| create_error!(NotFound)) + } +} diff --git a/crates/core/database/src/models/mod.rs b/crates/core/database/src/models/mod.rs index cf9bca31..00416abe 100644 --- a/crates/core/database/src/models/mod.rs +++ b/crates/core/database/src/models/mod.rs @@ -1,3 +1,5 @@ +mod account_invites; +mod accounts; mod admin_migrations; mod audit_logs; mod bots; @@ -5,10 +7,12 @@ mod channel_invites; mod channel_unreads; mod channel_webhooks; mod channels; +mod discover_requests; mod emojis; mod file_hashes; mod files; mod messages; +mod mfa_tickets; mod policy_changes; mod ratelimit_events; mod safety_reports; @@ -16,13 +20,12 @@ mod safety_snapshots; mod server_bans; mod server_members; mod servers; +mod sessions; mod user_settings; mod users; -mod accounts; -mod account_invites; -mod sessions; -mod mfa_tickets; +pub use account_invites::*; +pub use accounts::*; pub use admin_migrations::*; pub use audit_logs::*; pub use bots::*; @@ -30,10 +33,12 @@ pub use channel_invites::*; pub use channel_unreads::*; pub use channel_webhooks::*; pub use channels::*; +pub use discover_requests::*; pub use emojis::*; pub use file_hashes::*; pub use files::*; pub use messages::*; +pub use mfa_tickets::*; pub use policy_changes::*; pub use ratelimit_events::*; pub use safety_reports::*; @@ -41,12 +46,9 @@ pub use safety_snapshots::*; pub use server_bans::*; pub use server_members::*; pub use servers::*; +pub use sessions::*; pub use user_settings::*; pub use users::*; -pub use accounts::*; -pub use account_invites::*; -pub use sessions::*; -pub use mfa_tickets::*; use crate::{Database, ReferenceDb}; @@ -80,6 +82,7 @@ pub trait AbstractDatabase: + account_invites::AbstractAccountInvites + sessions::AbstractSessions + mfa_tickets::AbstractMFATickets + + discover_requests::AbstractDiscoverRequest { } diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index 65e90b73..88dbaee5 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -102,6 +102,46 @@ impl From for ChannelCompositeKey { } } +impl From for DiscoverBan { + fn from(value: crate::DiscoverBan) -> Self { + DiscoverBan { + id: value.id, + item_type: value.item_type.into(), + item_id: value.item_id, + } + } +} + +impl From for DiscoverRequest { + fn from(value: crate::DiscoverRequest) -> Self { + DiscoverRequest { + request_type: value.request_type.into(), + request_id: value.request_id, + status: value.status.into(), + } + } +} + +impl From for DiscoverRequestType { + fn from(value: crate::DiscoverRequestType) -> Self { + match value { + crate::DiscoverRequestType::Bot => DiscoverRequestType::Bot, + crate::DiscoverRequestType::Server => DiscoverRequestType::Server, + } + } +} + +impl From for DiscoverRequestStatus { + fn from(value: crate::DiscoverRequestStatus) -> Self { + match value { + crate::DiscoverRequestStatus::Approved(s) => DiscoverRequestStatus::Approved(s), + crate::DiscoverRequestStatus::Denied(s) => DiscoverRequestStatus::Denied(s), + crate::DiscoverRequestStatus::Pending => DiscoverRequestStatus::Pending, + crate::DiscoverRequestStatus::UnderReview => DiscoverRequestStatus::UnderReview, + } + } +} + impl From for Webhook { fn from(value: crate::Webhook) -> Self { Webhook { diff --git a/crates/core/models/src/v0/discover.rs b/crates/core/models/src/v0/discover.rs new file mode 100644 index 00000000..7f9187c4 --- /dev/null +++ b/crates/core/models/src/v0/discover.rs @@ -0,0 +1,35 @@ +auto_derived!( + pub enum DiscoverRequestType { + Bot, + Server, + } + + pub enum DiscoverRequestStatus { + Pending, + UnderReview, + Denied(Option), // reason + Approved(Option), // reason + } + + /// Discover request + pub struct DiscoverRequest { + /// The type of request. + #[serde(rename = "type")] + pub request_type: DiscoverRequestType, + /// The ID of the bot/server + pub request_id: String, + /// status of the request + pub status: DiscoverRequestStatus, + } + + pub struct DiscoverBan { + /// Ban Id + #[serde(rename = "_id")] + pub id: String, + /// The type of item. + #[serde(rename = "type")] + pub item_type: DiscoverRequestType, + /// The ID of the bot/server + pub item_id: String, + } +); diff --git a/crates/core/models/src/v0/mod.rs b/crates/core/models/src/v0/mod.rs index c6fc7f7c..39fea4b3 100644 --- a/crates/core/models/src/v0/mod.rs +++ b/crates/core/models/src/v0/mod.rs @@ -1,41 +1,43 @@ +mod accounts; mod audit_logs; mod bots; mod channel_invites; mod channel_unreads; mod channel_webhooks; mod channels; +mod discover; mod embeds; mod emojis; mod files; mod messages; +mod mfa_tickets; mod policy_changes; mod safety_reports; mod server_bans; mod server_members; mod servers; +mod sessions; mod user_settings; mod users; -mod accounts; -mod mfa_tickets; -mod sessions; +pub use accounts::*; pub use audit_logs::*; pub use bots::*; pub use channel_invites::*; pub use channel_unreads::*; pub use channel_webhooks::*; pub use channels::*; +pub use discover::*; pub use embeds::*; pub use emojis::*; pub use files::*; pub use messages::*; +pub use mfa_tickets::*; pub use policy_changes::*; pub use safety_reports::*; pub use server_bans::*; pub use server_members::*; pub use servers::*; +pub use sessions::*; pub use user_settings::*; pub use users::*; -pub use accounts::*; -pub use mfa_tickets::*; -pub use sessions::*; \ No newline at end of file diff --git a/crates/core/result/src/axum.rs b/crates/core/result/src/axum.rs index 12695e1a..6e468a79 100644 --- a/crates/core/result/src/axum.rs +++ b/crates/core/result/src/axum.rs @@ -12,6 +12,8 @@ impl IntoResponse for Error { let status = match self.error_type { ErrorType::LabelMe => StatusCode::INTERNAL_SERVER_ERROR, + ErrorType::ContactSupport { .. } => StatusCode::BAD_REQUEST, + ErrorType::AlreadyOnboarded => StatusCode::FORBIDDEN, ErrorType::UnknownUser => StatusCode::NOT_FOUND, diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index e82d8d33..a6adbb1c 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -57,6 +57,11 @@ pub enum ErrorType { /// This error was not labeled :( LabelMe, + // ? Support Errors + ContactSupport { + msg: String, + }, + // ? Onboarding related errors AlreadyOnboarded, diff --git a/crates/core/result/src/rocket.rs b/crates/core/result/src/rocket.rs index 8a1ff841..e35801d2 100644 --- a/crates/core/result/src/rocket.rs +++ b/crates/core/result/src/rocket.rs @@ -14,6 +14,8 @@ impl<'r> Responder<'r, 'static> for Error { let status = match self.error_type { ErrorType::LabelMe => Status::InternalServerError, + ErrorType::ContactSupport { .. } => Status::BadRequest, + ErrorType::AlreadyOnboarded => Status::Forbidden, ErrorType::UnknownUser => Status::NotFound, diff --git a/crates/delta/src/routes/bots/discover/discover_add_bot.rs b/crates/delta/src/routes/bots/discover/discover_add_bot.rs new file mode 100644 index 00000000..dcf81a7c --- /dev/null +++ b/crates/delta/src/routes/bots/discover/discover_add_bot.rs @@ -0,0 +1,41 @@ +use revolt_config::config; +use revolt_database::{util::reference::Reference, Database, DiscoverRequestType, User}; +use rocket_empty::EmptyResponse; + +use revolt_result::{create_error, Result}; +use rocket::State; + +/// # Add bot to Discover +/// +/// This puts your bot into the Discover request queue. +/// This endpoint is ONLY USEFUL in production on stoat.chat/app . +#[openapi(tag = "Discover")] +#[put("//discover")] +pub async fn discover_add_bot( + db: &State, + bot_id: Reference<'_>, + user: User, +) -> Result { + let config = config().await; + if !config.production { + return Err(create_error!(NoEffect)); + } + + let bot = bot_id.as_bot(db).await?; + if (bot.owner != user.id && bot.id != user.id) && !user.privileged { + return Err(create_error!(NotOwner)); + } + + if db + .get_discover_ban(DiscoverRequestType::Bot, &bot.id) + .await + .is_ok() + { + return Err(create_error!(Banned)); + } + + db.insert_discover_request(DiscoverRequestType::Bot, &bot.id) + .await?; + + Ok(EmptyResponse) +} diff --git a/crates/delta/src/routes/bots/discover/discover_get_bot.rs b/crates/delta/src/routes/bots/discover/discover_get_bot.rs new file mode 100644 index 00000000..38811f9d --- /dev/null +++ b/crates/delta/src/routes/bots/discover/discover_get_bot.rs @@ -0,0 +1,43 @@ +use revolt_config::config; +use revolt_database::{util::reference::Reference, Database, DiscoverRequestType, User}; +use revolt_models::v0; + +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; + +/// # Get Discover request status +/// +/// Fetches the status of your Discover request. +/// If it has been approved or denied, the reason will be provided (if applicable). +/// This endpoint is ONLY USEFUL in production on stoat.chat/app . +#[openapi(tag = "Discover")] +#[get("//discover")] +pub async fn discover_get_bot( + db: &State, + bot_id: Reference<'_>, + user: User, +) -> Result> { + let config = config().await; + if !config.production { + return Err(create_error!(NoEffect)); + } + + let bot = bot_id.as_bot(db).await?; + if (bot.owner != user.id && bot.id != user.id) && !user.privileged { + return Err(create_error!(NotOwner)); + } + + if db + .get_discover_ban(DiscoverRequestType::Bot, &bot.id) + .await + .is_ok() + { + return Err(create_error!(Banned)); + } + + let ret = db + .fetch_discover_request_by_item_id(DiscoverRequestType::Bot, &bot.id) + .await?; + + Ok(Json(ret.into())) +} diff --git a/crates/delta/src/routes/bots/discover/discover_remove_bot.rs b/crates/delta/src/routes/bots/discover/discover_remove_bot.rs new file mode 100644 index 00000000..c358c042 --- /dev/null +++ b/crates/delta/src/routes/bots/discover/discover_remove_bot.rs @@ -0,0 +1,55 @@ +use revolt_config::config; +use revolt_database::{ + util::reference::Reference, Database, DiscoverRequestStatus, DiscoverRequestType, User, +}; + +use revolt_result::{create_error, Result}; +use rocket::State; +use rocket_empty::EmptyResponse; + +/// # Delete Discover request +/// +/// This cannot be used if your request is no longer in the queue (ie approved or rejected). +/// If you wish to reapply after a rejection, submit another POST. +/// This endpoint is ONLY USEFUL in production on stoat.chat/app . +#[openapi(tag = "Discover")] +#[delete("//discover")] +pub async fn discover_remove_bot( + db: &State, + bot_id: Reference<'_>, + user: User, +) -> Result { + let config = config().await; + if !config.production { + return Err(create_error!(NoEffect)); + } + + let bot = bot_id.as_bot(db).await?; + if (bot.owner != user.id && bot.id != user.id) && !user.privileged { + return Err(create_error!(NotOwner)); + } + + if db + .get_discover_ban(DiscoverRequestType::Bot, &bot.id) + .await + .is_ok() + { + return Err(create_error!(Banned)); + } + + let ret = db + .fetch_discover_request_by_item_id(DiscoverRequestType::Bot, &bot.id) + .await?; + + match ret.status { + DiscoverRequestStatus::Approved(_) => Err(create_error!(ContactSupport { + msg: "Contact support to have your bot removed from Discover".to_string() + })), + DiscoverRequestStatus::Denied(_) => Err(create_error!(NoEffect)), + DiscoverRequestStatus::Pending | DiscoverRequestStatus::UnderReview => { + db.delete_discover_request(ret.request_type, &ret.request_id) + .await?; + Ok(EmptyResponse) + } + } +} diff --git a/crates/delta/src/routes/bots/discover/mod.rs b/crates/delta/src/routes/bots/discover/mod.rs new file mode 100644 index 00000000..4a7087b3 --- /dev/null +++ b/crates/delta/src/routes/bots/discover/mod.rs @@ -0,0 +1,3 @@ +pub mod discover_add_bot; +pub mod discover_get_bot; +pub mod discover_remove_bot; diff --git a/crates/delta/src/routes/bots/mod.rs b/crates/delta/src/routes/bots/mod.rs index 0ae1a954..f0c68c40 100644 --- a/crates/delta/src/routes/bots/mod.rs +++ b/crates/delta/src/routes/bots/mod.rs @@ -3,6 +3,7 @@ use rocket::Route; mod create; mod delete; +mod discover; mod edit; mod fetch; mod fetch_owned; @@ -18,5 +19,8 @@ pub fn routes() -> (Vec, OpenApi) { fetch_owned::fetch_owned_bots, edit::edit_bot, delete::delete_bot, + discover::discover_add_bot::discover_add_bot, + discover::discover_get_bot::discover_get_bot, + discover::discover_remove_bot::discover_remove_bot, ] } diff --git a/crates/delta/src/routes/servers/discover/discover_add.rs b/crates/delta/src/routes/servers/discover/discover_add.rs new file mode 100644 index 00000000..05867a37 --- /dev/null +++ b/crates/delta/src/routes/servers/discover/discover_add.rs @@ -0,0 +1,41 @@ +use revolt_config::config; +use revolt_database::{util::reference::Reference, Database, DiscoverRequestType, User}; +use rocket_empty::EmptyResponse; + +use revolt_result::{create_error, Result}; +use rocket::State; + +/// # Add server to Discover +/// +/// This puts your server into the Discover request queue. +/// This endpoint is ONLY USEFUL in production on stoat.chat/app . +#[openapi(tag = "Discover")] +#[put("//discover")] +pub async fn discover_add( + db: &State, + server: Reference<'_>, + user: User, +) -> Result { + let config = config().await; + if !config.production { + return Err(create_error!(NoEffect)); + } + + let server = server.as_server(db).await?; + if server.owner != user.id && !user.privileged { + return Err(create_error!(NotOwner)); + } + + if db + .get_discover_ban(DiscoverRequestType::Server, &server.id) + .await + .is_ok() + { + return Err(create_error!(Banned)); + } + + db.insert_discover_request(DiscoverRequestType::Server, &server.id) + .await?; + + Ok(EmptyResponse) +} diff --git a/crates/delta/src/routes/servers/discover/discover_get.rs b/crates/delta/src/routes/servers/discover/discover_get.rs new file mode 100644 index 00000000..e4aa0b85 --- /dev/null +++ b/crates/delta/src/routes/servers/discover/discover_get.rs @@ -0,0 +1,43 @@ +use revolt_config::config; +use revolt_database::{util::reference::Reference, Database, DiscoverRequestType, User}; +use revolt_models::v0; + +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; + +/// # Get Discover request status +/// +/// Fetches the status of your Discover request. +/// If it has been approved or denied, the reason will be provided (if applicable). +/// This endpoint is ONLY USEFUL in production on stoat.chat/app . +#[openapi(tag = "Discover")] +#[get("//discover")] +pub async fn discover_get( + db: &State, + server: Reference<'_>, + user: User, +) -> Result> { + let config = config().await; + if !config.production { + return Err(create_error!(NoEffect)); + } + + let server = server.as_server(db).await?; + if server.owner != user.id && !user.privileged { + return Err(create_error!(NotOwner)); + } + + if db + .get_discover_ban(DiscoverRequestType::Server, &server.id) + .await + .is_ok() + { + return Err(create_error!(Banned)); + } + + let ret = db + .fetch_discover_request_by_item_id(DiscoverRequestType::Server, &server.id) + .await?; + + Ok(Json(ret.into())) +} diff --git a/crates/delta/src/routes/servers/discover/discover_remove.rs b/crates/delta/src/routes/servers/discover/discover_remove.rs new file mode 100644 index 00000000..42d8a065 --- /dev/null +++ b/crates/delta/src/routes/servers/discover/discover_remove.rs @@ -0,0 +1,55 @@ +use revolt_config::config; +use revolt_database::{ + util::reference::Reference, Database, DiscoverRequestStatus, DiscoverRequestType, User, +}; + +use revolt_result::{create_error, Result}; +use rocket::State; +use rocket_empty::EmptyResponse; + +/// # Delete Discover request +/// +/// This cannot be used if your request is no longer in the queue (ie approved or rejected). +/// If you wish to reapply after a rejection, submit another POST. +/// This endpoint is ONLY USEFUL in production on stoat.chat/app . +#[openapi(tag = "Discover")] +#[delete("//discover")] +pub async fn discover_remove( + db: &State, + server: Reference<'_>, + user: User, +) -> Result { + let config = config().await; + if !config.production { + return Err(create_error!(NoEffect)); + } + + let server = server.as_server(db).await?; + if server.owner != user.id && !user.privileged { + return Err(create_error!(NotOwner)); + } + + if db + .get_discover_ban(DiscoverRequestType::Server, &server.id) + .await + .is_ok() + { + return Err(create_error!(Banned)); + } + + let ret = db + .fetch_discover_request_by_item_id(DiscoverRequestType::Server, &server.id) + .await?; + + match ret.status { + DiscoverRequestStatus::Approved(_) => Err(create_error!(ContactSupport { + msg: "Contact support to have your server removed from Discover".to_string() + })), + DiscoverRequestStatus::Denied(_) => Err(create_error!(NoEffect)), + DiscoverRequestStatus::Pending | DiscoverRequestStatus::UnderReview => { + db.delete_discover_request(ret.request_type, &ret.request_id) + .await?; + Ok(EmptyResponse) + } + } +} diff --git a/crates/delta/src/routes/servers/discover/mod.rs b/crates/delta/src/routes/servers/discover/mod.rs new file mode 100644 index 00000000..fa50717f --- /dev/null +++ b/crates/delta/src/routes/servers/discover/mod.rs @@ -0,0 +1,3 @@ +pub mod discover_add; +pub mod discover_get; +pub mod discover_remove; diff --git a/crates/delta/src/routes/servers/mod.rs b/crates/delta/src/routes/servers/mod.rs index 55e6cae6..811e2695 100644 --- a/crates/delta/src/routes/servers/mod.rs +++ b/crates/delta/src/routes/servers/mod.rs @@ -6,6 +6,7 @@ mod ban_create; mod ban_list; mod ban_remove; mod channel_create; +mod discover; mod emoji_list; mod invites_fetch; mod member_edit; @@ -52,5 +53,8 @@ pub fn routes() -> (Vec, OpenApi) { emoji_list::list_emoji, roles_edit_positions::edit_role_ranks, audit_log_query::query, + discover::discover_add::discover_add, + discover::discover_get::discover_get, + discover::discover_remove::discover_remove, ] }