refactor(quark): port invite_delete, invite_fetch, invite_join
This commit is contained in:
@@ -307,6 +307,13 @@ impl Channel {
|
|||||||
return Err(create_error!(AlreadyInGroup));
|
return Err(create_error!(AlreadyInGroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let config = config().await;
|
||||||
|
if recipients.len() >= config.features.limits.default.group_size {
|
||||||
|
return Err(create_error!(GroupTooLarge {
|
||||||
|
max: config.features.limits.default.group_size
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
recipients.push(String::from(&user.id));
|
recipients.push(String::from(&user.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,6 +703,9 @@ impl Channel {
|
|||||||
pub async fn delete(&self, db: &Database) -> Result<()> {
|
pub async fn delete(&self, db: &Database) -> Result<()> {
|
||||||
let id = self.id().to_string();
|
let id = self.id().to_string();
|
||||||
EventV1::ChannelDelete { id: id.clone() }.p(id).await;
|
EventV1::ChannelDelete { id: id.clone() }.p(id).await;
|
||||||
|
// TODO: missing functionality:
|
||||||
|
// - group invites
|
||||||
|
// - channels list / categories list on server
|
||||||
db.delete_channel(self).await
|
db.delete_channel(self).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::AbstractChannels;
|
use super::AbstractChannels;
|
||||||
use crate::{Channel, FieldsChannel, IntoDocumentPath, MongoDb, PartialChannel};
|
use crate::{AbstractServers, Channel, FieldsChannel, IntoDocumentPath, MongoDb, PartialChannel};
|
||||||
use bson::Document;
|
use bson::{Bson, Document};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use revolt_permissions::OverrideField;
|
use revolt_permissions::OverrideField;
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
@@ -188,6 +188,125 @@ impl AbstractChannels for MongoDb {
|
|||||||
|
|
||||||
// Delete a channel
|
// Delete a channel
|
||||||
async fn delete_channel(&self, channel: &Channel) -> Result<()> {
|
async fn delete_channel(&self, channel: &Channel) -> Result<()> {
|
||||||
|
let id = channel.id().to_string();
|
||||||
|
let server_id = match channel {
|
||||||
|
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
|
||||||
|
Some(server)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete invites and unreads.
|
||||||
|
self.delete_associated_channel_objects(Bson::String(id.to_string()))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Delete messages.
|
||||||
|
self.delete_bulk_messages(doc! {
|
||||||
|
"channel": &id
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Remove from server object.
|
||||||
|
if let Some(server) = server_id {
|
||||||
|
let server = self.fetch_server(server).await?;
|
||||||
|
let mut update = doc! {
|
||||||
|
"$pull": {
|
||||||
|
"channels": &id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(sys) = &server.system_messages {
|
||||||
|
let mut unset = doc! {};
|
||||||
|
|
||||||
|
if let Some(cid) = &sys.user_joined {
|
||||||
|
if &id == cid {
|
||||||
|
unset.insert("system_messages.user_joined", 1_i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(cid) = &sys.user_left {
|
||||||
|
if &id == cid {
|
||||||
|
unset.insert("system_messages.user_left", 1_i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(cid) = &sys.user_kicked {
|
||||||
|
if &id == cid {
|
||||||
|
unset.insert("system_messages.user_kicked", 1_i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(cid) = &sys.user_banned {
|
||||||
|
if &id == cid {
|
||||||
|
unset.insert("system_messages.user_banned", 1_i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !unset.is_empty() {
|
||||||
|
update.insert("$unset", unset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.col::<Document>("servers")
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": server.id
|
||||||
|
},
|
||||||
|
update,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("update_one", "servers"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete associated attachments
|
||||||
|
self.delete_many_attachments(doc! {
|
||||||
|
"object_id": &id
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Delete the channel itself
|
||||||
query!(self, delete_one_by_id, COL, &channel.id()).map(|_| ())
|
query!(self, delete_one_by_id, COL, &channel.id()).map(|_| ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MongoDb {
|
||||||
|
pub async fn delete_associated_channel_objects(&self, id: Bson) -> Result<()> {
|
||||||
|
// Delete all invites to these channels.
|
||||||
|
self.col::<Document>("channel_invites")
|
||||||
|
.delete_many(
|
||||||
|
doc! {
|
||||||
|
"channel": &id
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("delete_many", "channel_invites"))?;
|
||||||
|
|
||||||
|
// Delete unread message objects on channels.
|
||||||
|
self.col::<Document>("channel_unreads")
|
||||||
|
.delete_many(
|
||||||
|
doc! {
|
||||||
|
"_id.channel": &id
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("delete_many", "channel_unreads"))
|
||||||
|
.map(|_| ())?;
|
||||||
|
|
||||||
|
// update many attachments with parent id
|
||||||
|
|
||||||
|
// Delete all webhooks on this channel.
|
||||||
|
self.col::<Document>("webhooks")
|
||||||
|
.delete_many(
|
||||||
|
doc! {
|
||||||
|
"channel": &id
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("delete_many", "webhooks"))
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -115,3 +115,21 @@ impl AbstractAttachments for MongoDb {
|
|||||||
.map_err(|_| create_database_error!("update_one", COL))
|
.map_err(|_| create_database_error!("update_one", COL))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MongoDb {
|
||||||
|
pub async fn delete_many_attachments(&self, projection: Document) -> Result<()> {
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.update_many(
|
||||||
|
projection,
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"deleted": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("update_many", COL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ use mongodb::options::FindOptions;
|
|||||||
use revolt_models::v0::MessageSort;
|
use revolt_models::v0::MessageSort;
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
use crate::{AppendMessage, Message, MessageQuery, MessageTimePeriod, MongoDb, PartialMessage};
|
use crate::{
|
||||||
|
AppendMessage, DocumentId, Message, MessageQuery, MessageTimePeriod, MongoDb, PartialMessage,
|
||||||
|
};
|
||||||
|
|
||||||
use super::AbstractMessages;
|
use super::AbstractMessages;
|
||||||
|
|
||||||
@@ -293,3 +295,57 @@ impl AbstractMessages for MongoDb {
|
|||||||
.map_err(|_| create_database_error!("delete_many", COL))
|
.map_err(|_| create_database_error!("delete_many", COL))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MongoDb {
|
||||||
|
pub async fn delete_bulk_messages(&self, projection: Document) -> Result<()> {
|
||||||
|
let mut for_attachments = projection.clone();
|
||||||
|
for_attachments.insert(
|
||||||
|
"attachments",
|
||||||
|
doc! {
|
||||||
|
"$exists": 1_i32
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if there are any attachments we need to delete.
|
||||||
|
let message_ids_with_attachments = self
|
||||||
|
.find_with_options::<_, DocumentId>(
|
||||||
|
COL,
|
||||||
|
for_attachments,
|
||||||
|
FindOptions::builder()
|
||||||
|
.projection(doc! { "_id": 1_i32 })
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("find_many", "attachments"))?
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.id)
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
// If we found any, mark them as deleted.
|
||||||
|
if !message_ids_with_attachments.is_empty() {
|
||||||
|
self.col::<Document>("attachments")
|
||||||
|
.update_many(
|
||||||
|
doc! {
|
||||||
|
"message_id": {
|
||||||
|
"$in": message_ids_with_attachments
|
||||||
|
}
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"deleted": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("update_many", "attachments"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And then delete said messages.
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.delete_many(projection, None)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("delete_many", COL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -214,6 +214,8 @@ impl Server {
|
|||||||
.p(self.id.clone())
|
.p(self.id.clone())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
// TODO: missing functionality
|
||||||
|
|
||||||
db.delete_server(&self.id).await
|
db.delete_server(&self.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -701,6 +701,77 @@ impl crate::User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn into_known<'a, P>(self, perspective: P) -> User
|
||||||
|
where
|
||||||
|
P: Into<Option<&'a crate::User>>,
|
||||||
|
{
|
||||||
|
let perspective = perspective.into();
|
||||||
|
let (relationship, can_see_profile) = if self.bot.is_some() {
|
||||||
|
(RelationshipStatus::None, true)
|
||||||
|
} else if let Some(perspective) = perspective {
|
||||||
|
if perspective.id == self.id {
|
||||||
|
(RelationshipStatus::User, true)
|
||||||
|
} else {
|
||||||
|
let relationship = perspective
|
||||||
|
.relations
|
||||||
|
.as_ref()
|
||||||
|
.map(|relations| {
|
||||||
|
relations
|
||||||
|
.iter()
|
||||||
|
.find(|relationship| relationship.id == self.id)
|
||||||
|
.map(|relationship| relationship.status.clone().into())
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// TODO: refactor all of this to be less code? (as in with the method above)
|
||||||
|
// TODO: also not sure if this is the best solution, but we don't want to
|
||||||
|
// TODO: fetch mutual information again here
|
||||||
|
let can_see_profile = relationship == RelationshipStatus::Friend;
|
||||||
|
(relationship, can_see_profile)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(RelationshipStatus::None, false)
|
||||||
|
};
|
||||||
|
|
||||||
|
User {
|
||||||
|
username: self.username,
|
||||||
|
discriminator: self.discriminator,
|
||||||
|
display_name: self.display_name,
|
||||||
|
avatar: self.avatar.map(|file| file.into()),
|
||||||
|
relations: if let Some(crate::User { id, .. }) = perspective {
|
||||||
|
if id == &self.id {
|
||||||
|
self.relations
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|relation| relation.into())
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
badges: self.badges.unwrap_or_default() as u32,
|
||||||
|
status: if can_see_profile {
|
||||||
|
self.status.map(|status| status.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
profile: if can_see_profile {
|
||||||
|
self.profile.map(|profile| profile.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
flags: self.flags.unwrap_or_default() as u32,
|
||||||
|
privileged: self.privileged,
|
||||||
|
bot: self.bot.map(|bot| bot.into()),
|
||||||
|
relationship,
|
||||||
|
online: can_see_profile && revolt_presence::is_online(&self.id).await,
|
||||||
|
id: self.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn into_self(self) -> User {
|
pub async fn into_self(self) -> User {
|
||||||
User {
|
User {
|
||||||
username: self.username,
|
username: self.username,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use schemars::{
|
|||||||
JsonSchema,
|
JsonSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Bot, Channel, Database, Emoji, Message, Server, User, Webhook};
|
use crate::{Bot, Channel, Database, Emoji, Invite, Message, Server, User, Webhook};
|
||||||
|
|
||||||
/// Reference to some object in the database
|
/// Reference to some object in the database
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@@ -37,6 +37,11 @@ impl Reference {
|
|||||||
db.fetch_channel(&self.id).await
|
db.fetch_channel(&self.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch invite from Ref
|
||||||
|
pub async fn as_invite(&self, db: &Database) -> Result<Invite> {
|
||||||
|
db.fetch_invite(&self.id).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch message from Ref
|
/// Fetch message from Ref
|
||||||
pub async fn as_message(&self, db: &Database) -> Result<Message> {
|
pub async fn as_message(&self, db: &Database) -> Result<Message> {
|
||||||
db.fetch_message(&self.id).await
|
db.fetch_message(&self.id).await
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use super::{Channel, File, Server, User};
|
||||||
|
|
||||||
auto_derived!(
|
auto_derived!(
|
||||||
/// Invite
|
/// Invite
|
||||||
pub enum Invite {
|
pub enum Invite {
|
||||||
@@ -24,4 +26,76 @@ auto_derived!(
|
|||||||
channel: String,
|
channel: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Public invite response
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum InviteResponse {
|
||||||
|
/// Server channel invite
|
||||||
|
Server {
|
||||||
|
/// Invite code
|
||||||
|
code: String,
|
||||||
|
/// Id of the server
|
||||||
|
server_id: String,
|
||||||
|
/// Name of the server
|
||||||
|
server_name: String,
|
||||||
|
/// Attachment for server icon
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
server_icon: Option<File>,
|
||||||
|
/// Attachment for server banner
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
server_banner: Option<File>,
|
||||||
|
/// Enum of server flags
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
server_flags: Option<i32>,
|
||||||
|
/// Id of server channel
|
||||||
|
channel_id: String,
|
||||||
|
/// Name of server channel
|
||||||
|
channel_name: String,
|
||||||
|
/// Description of server channel
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
channel_description: Option<String>,
|
||||||
|
/// Name of user who created the invite
|
||||||
|
user_name: String,
|
||||||
|
/// Avatar of the user who created the invite
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
user_avatar: Option<File>,
|
||||||
|
/// Number of members in this server
|
||||||
|
member_count: i64,
|
||||||
|
},
|
||||||
|
/// Group channel invite
|
||||||
|
Group {
|
||||||
|
/// Invite code
|
||||||
|
code: String,
|
||||||
|
/// Id of group channel
|
||||||
|
channel_id: String,
|
||||||
|
/// Name of group channel
|
||||||
|
channel_name: String,
|
||||||
|
/// Description of group channel
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
channel_description: Option<String>,
|
||||||
|
/// Name of user who created the invite
|
||||||
|
user_name: String,
|
||||||
|
/// Avatar of the user who created the invite
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
user_avatar: Option<File>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invite join response
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum InviteJoinResponse {
|
||||||
|
Server {
|
||||||
|
/// Channels in the server
|
||||||
|
channels: Vec<Channel>,
|
||||||
|
/// Server we are joining
|
||||||
|
server: Server,
|
||||||
|
},
|
||||||
|
Group {
|
||||||
|
/// Group channel we are joining
|
||||||
|
channel: Channel,
|
||||||
|
/// Members of this group
|
||||||
|
users: Vec<User>,
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ use rocket::{serde::json::Json, State};
|
|||||||
/// # Fetch Group Members
|
/// # Fetch Group Members
|
||||||
///
|
///
|
||||||
/// Retrieves all users who are part of this group.
|
/// Retrieves all users who are part of this group.
|
||||||
|
///
|
||||||
|
/// This may not return full user information if users are not friends but have mutual connections.
|
||||||
#[openapi(tag = "Groups")]
|
#[openapi(tag = "Groups")]
|
||||||
#[get("/<target>/members")]
|
#[get("/<target>/members")]
|
||||||
pub async fn fetch_members(
|
pub async fn fetch_members(
|
||||||
@@ -30,7 +32,7 @@ pub async fn fetch_members(
|
|||||||
db.fetch_users(&recipients)
|
db.fetch_users(&recipients)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|other_user| other_user.into(db, &user)),
|
.map(|other_user| other_user.into_known(&user)),
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
use revolt_quark::{
|
use revolt_database::{
|
||||||
models::{Invite, User},
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
perms, Db, EmptyResponse, Permission, Ref, Result,
|
Database, Invite, User,
|
||||||
};
|
};
|
||||||
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
|
use revolt_result::Result;
|
||||||
|
use rocket::State;
|
||||||
|
use rocket_empty::EmptyResponse;
|
||||||
|
|
||||||
/// # Delete Invite
|
/// # Delete Invite
|
||||||
///
|
///
|
||||||
/// Delete an invite by its id.
|
/// Delete an invite by its id.
|
||||||
#[openapi(tag = "Invites")]
|
#[openapi(tag = "Invites")]
|
||||||
#[delete("/<target>")]
|
#[delete("/<target>")]
|
||||||
pub async fn req(db: &Db, user: User, target: Ref) -> Result<EmptyResponse> {
|
pub async fn delete(db: &State<Database>, user: User, target: Reference) -> Result<EmptyResponse> {
|
||||||
let invite = target.as_invite(db).await?;
|
let invite = target.as_invite(db).await?;
|
||||||
|
|
||||||
if user.id == invite.creator() {
|
if user.id == invite.creator() {
|
||||||
@@ -17,10 +21,10 @@ pub async fn req(db: &Db, user: User, target: Ref) -> Result<EmptyResponse> {
|
|||||||
match invite {
|
match invite {
|
||||||
Invite::Server { code, server, .. } => {
|
Invite::Server { code, server, .. } => {
|
||||||
let server = db.fetch_server(&server).await?;
|
let server = db.fetch_server(&server).await?;
|
||||||
perms(&user)
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||||
.server(&server)
|
calculate_server_permissions(&mut query)
|
||||||
.throw_permission(db, Permission::ManageServer)
|
.await
|
||||||
.await?;
|
.throw_if_lacking_channel_permission(ChannelPermission::ManageServer)?;
|
||||||
|
|
||||||
db.delete_invite(&code).await
|
db.delete_invite(&code).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +1,14 @@
|
|||||||
use revolt_quark::models::{Channel, File, Invite};
|
use revolt_database::{util::reference::Reference, Channel, Database, Invite};
|
||||||
use revolt_quark::{Db, Ref, Result};
|
use revolt_models::v0;
|
||||||
|
use revolt_result::Result;
|
||||||
use rocket::serde::json::Json;
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
/// # Invite
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
|
||||||
#[derive(Serialize, Debug, Clone, JsonSchema)]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum InviteResponse {
|
|
||||||
/// Server channel invite
|
|
||||||
Server {
|
|
||||||
/// Invite code
|
|
||||||
code: String,
|
|
||||||
/// Id of the server
|
|
||||||
server_id: String,
|
|
||||||
/// Name of the server
|
|
||||||
server_name: String,
|
|
||||||
/// Attachment for server icon
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
server_icon: Option<File>,
|
|
||||||
/// Attachment for server banner
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
server_banner: Option<File>,
|
|
||||||
/// Enum of server flags
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
server_flags: Option<i32>,
|
|
||||||
/// Id of server channel
|
|
||||||
channel_id: String,
|
|
||||||
/// Name of server channel
|
|
||||||
channel_name: String,
|
|
||||||
/// Description of server channel
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
channel_description: Option<String>,
|
|
||||||
/// Name of user who created the invite
|
|
||||||
user_name: String,
|
|
||||||
/// Avatar of the user who created the invite
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
user_avatar: Option<File>,
|
|
||||||
/// Number of members in this server
|
|
||||||
member_count: i64,
|
|
||||||
},
|
|
||||||
/// Group channel invite
|
|
||||||
Group {
|
|
||||||
/// Invite code
|
|
||||||
code: String,
|
|
||||||
/// Id of group channel
|
|
||||||
channel_id: String,
|
|
||||||
/// Name of group channel
|
|
||||||
channel_name: String,
|
|
||||||
/// Description of group channel
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
channel_description: Option<String>,
|
|
||||||
/// Name of user who created the invite
|
|
||||||
user_name: String,
|
|
||||||
/// Avatar of the user who created the invite
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
user_avatar: Option<File>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Fetch Invite
|
/// # Fetch Invite
|
||||||
///
|
///
|
||||||
/// Fetch an invite by its id.
|
/// Fetch an invite by its id.
|
||||||
#[openapi(tag = "Invites")]
|
#[openapi(tag = "Invites")]
|
||||||
#[get("/<target>")]
|
#[get("/<target>")]
|
||||||
pub async fn req(db: &Db, target: Ref) -> Result<Json<InviteResponse>> {
|
pub async fn fetch(db: &State<Database>, target: Reference) -> Result<Json<v0::InviteResponse>> {
|
||||||
Ok(Json(match target.as_invite(db).await? {
|
Ok(Json(match target.as_invite(db).await? {
|
||||||
Invite::Server {
|
Invite::Server {
|
||||||
channel, creator, ..
|
channel, creator, ..
|
||||||
@@ -90,19 +33,19 @@ pub async fn req(db: &Db, target: Ref) -> Result<Json<InviteResponse>> {
|
|||||||
} => {
|
} => {
|
||||||
let server = db.fetch_server(&server).await?;
|
let server = db.fetch_server(&server).await?;
|
||||||
|
|
||||||
InviteResponse::Server {
|
v0::InviteResponse::Server {
|
||||||
code: target.id,
|
code: target.id,
|
||||||
member_count: db.fetch_member_count(&server.id).await? as i64,
|
member_count: db.fetch_member_count(&server.id).await? as i64,
|
||||||
server_id: server.id,
|
server_id: server.id,
|
||||||
server_name: server.name,
|
server_name: server.name,
|
||||||
server_icon: server.icon,
|
server_icon: server.icon.map(|f| f.into()),
|
||||||
server_banner: server.banner,
|
server_banner: server.banner.map(|f| f.into()),
|
||||||
server_flags: server.flags,
|
server_flags: server.flags,
|
||||||
channel_id: id,
|
channel_id: id,
|
||||||
channel_name: name,
|
channel_name: name,
|
||||||
channel_description: description,
|
channel_description: description,
|
||||||
user_name: user.username,
|
user_name: user.username,
|
||||||
user_avatar: user.avatar,
|
user_avatar: user.avatar.map(|f| f.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -120,13 +63,13 @@ pub async fn req(db: &Db, target: Ref) -> Result<Json<InviteResponse>> {
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
..
|
..
|
||||||
} => InviteResponse::Group {
|
} => v0::InviteResponse::Group {
|
||||||
code: target.id,
|
code: target.id,
|
||||||
channel_id: id,
|
channel_id: id,
|
||||||
channel_name: name,
|
channel_name: name,
|
||||||
channel_description: description,
|
channel_description: description,
|
||||||
user_name: user.username,
|
user_name: user.username,
|
||||||
user_avatar: user.avatar,
|
user_avatar: user.avatar.map(|f| f.into()),
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,54 @@
|
|||||||
use revolt_quark::{
|
use futures::future::join_all;
|
||||||
models::{Channel, Invite, Server, User},
|
use revolt_database::{util::reference::Reference, Channel, Database, Invite, Member, User};
|
||||||
variables::delta::MAX_SERVER_COUNT,
|
use revolt_models::v0::{self, InviteJoinResponse};
|
||||||
Db, Error, Ref, Result,
|
use revolt_result::{create_error, Result};
|
||||||
};
|
use rocket::{serde::json::Json, State};
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
/// # Join Response
|
|
||||||
#[derive(Serialize, JsonSchema)]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum InviteJoinResponse {
|
|
||||||
Server {
|
|
||||||
/// Channels in the server
|
|
||||||
channels: Vec<Channel>,
|
|
||||||
/// Server we are joining
|
|
||||||
server: Server,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Join Invite
|
/// # Join Invite
|
||||||
///
|
///
|
||||||
/// Join an invite by its ID.
|
/// Join an invite by its ID
|
||||||
#[openapi(tag = "Invites")]
|
#[openapi(tag = "Invites")]
|
||||||
#[post("/<target>")]
|
#[post("/<target>")]
|
||||||
pub async fn req(db: &Db, user: User, target: Ref) -> Result<Json<InviteJoinResponse>> {
|
pub async fn join(
|
||||||
|
db: &State<Database>,
|
||||||
|
user: User,
|
||||||
|
target: Reference,
|
||||||
|
) -> Result<Json<v0::InviteJoinResponse>> {
|
||||||
if user.bot.is_some() {
|
if user.bot.is_some() {
|
||||||
return Err(Error::IsBot);
|
return Err(create_error!(IsBot));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.can_acquire_server(db).await? {
|
user.can_acquire_server(db).await?;
|
||||||
return Err(Error::TooManyServers {
|
|
||||||
max: *MAX_SERVER_COUNT,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let invite = target.as_invite(db).await?;
|
let invite = target.as_invite(db).await?;
|
||||||
match &invite {
|
match &invite {
|
||||||
Invite::Server { server, .. } => {
|
Invite::Server { server, .. } => {
|
||||||
let server = db.fetch_server(server).await?;
|
let server = db.fetch_server(server).await?;
|
||||||
let channels = server.create_member(db, user, None).await?;
|
let channels = Member::create(db, &server, &user, None).await?;
|
||||||
Ok(Json(InviteJoinResponse::Server { channels, server }))
|
Ok(Json(InviteJoinResponse::Server {
|
||||||
|
channels: channels.into_iter().map(|c| c.into()).collect(),
|
||||||
|
server: server.into(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
Invite::Group {
|
||||||
|
channel, creator, ..
|
||||||
|
} => {
|
||||||
|
let mut channel = db.fetch_channel(channel).await?;
|
||||||
|
channel.add_user_to_group(db, &user, creator).await?;
|
||||||
|
if let Channel::Group { recipients, .. } = &channel {
|
||||||
|
Ok(Json(InviteJoinResponse::Group {
|
||||||
|
users: join_all(
|
||||||
|
db.fetch_users(&recipients)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|other_user| other_user.into_known(&user)),
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
channel: channel.into(),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,9 @@ mod invite_fetch;
|
|||||||
mod invite_join;
|
mod invite_join;
|
||||||
|
|
||||||
pub fn routes() -> (Vec<Route>, OpenApi) {
|
pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||||
openapi_get_routes_spec![invite_fetch::req, invite_join::req, invite_delete::req]
|
openapi_get_routes_spec![
|
||||||
|
invite_fetch::fetch,
|
||||||
|
invite_join::join,
|
||||||
|
invite_delete::delete
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user