Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Showing
with 829 additions and 73 deletions
use crate::database::*;
use crate::util::result::{Error, Result};
use mongodb::bson::doc;
use nanoid::nanoid;
use rocket_contrib::json::JsonValue;
lazy_static! {
static ref ALPHABET: [char; 54] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'
];
}
#[post("/<target>/invites")]
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let target = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_invite_others() {
return Err(Error::MissingPermission);
}
let code = nanoid!(8, &*ALPHABET);
match &target {
Channel::Group { .. } => {
unimplemented!()
}
Channel::TextChannel { id, server, .. }
| Channel::VoiceChannel { id, server, .. } => {
Invite::Server {
code: code.clone(),
creator: user.id,
server: server.clone(),
channel: id.clone(),
}
.save()
.await?;
Ok(json!({ "code": code }))
}
_ => Err(Error::InvalidOperation),
}
}
......@@ -6,6 +6,7 @@ use mongodb::bson::doc;
#[delete("/<target>/messages/<msg>")]
pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> {
let channel = target.fetch_channel().await?;
channel.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel)
......
......@@ -2,7 +2,7 @@ use crate::database::*;
use crate::util::result::{Error, Result};
use chrono::Utc;
use mongodb::bson::{doc, Bson, DateTime};
use mongodb::bson::{doc, Bson, DateTime, Document};
use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize};
use validator::Validate;
......@@ -19,6 +19,8 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
.map_err(|error| Error::FailedValidation { error })?;
let channel = target.fetch_channel().await?;
channel.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel)
.for_channel()
......@@ -27,22 +29,41 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
Err(Error::MissingPermission)?
}
let message = msg.fetch_message(&channel).await?;
let mut message = msg.fetch_message(&channel).await?;
if message.author != user.id {
Err(Error::CannotEditMessage)?
}
let edited = Utc::now();
let mut set = doc! {
"content": &edit.content,
"edited": Bson::DateTime(edited)
};
message.content = Content::Text(edit.content.clone());
let mut update = json!({ "content": edit.content, "edited": DateTime(edited) });
if let Some(embeds) = &message.embeds {
let new_embeds: Vec<Document> = vec![];
for embed in embeds {
match embed {
Embed::Website(_) | Embed::Image(_) | Embed::None => {} // Otherwise push to new_embeds.
}
}
let obj = update.as_object_mut().unwrap();
obj.insert("embeds".to_string(), json!(new_embeds).0);
set.insert("embeds", new_embeds);
}
get_collection("messages")
.update_one(
doc! {
"_id": &message.id
},
doc! {
"$set": {
"content": &edit.content,
"edited": Bson::DateTime(edited)
}
"$set": set
},
None,
)
......@@ -52,7 +73,5 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
with: "message",
})?;
message
.publish_update(json!({ "content": edit.content, "edited": DateTime(edited) }))
.await
message.publish_update(update).await
}
......@@ -6,6 +6,7 @@ use rocket_contrib::json::JsonValue;
#[get("/<target>/messages/<msg>")]
pub async fn req(user: User, target: Ref, msg: Ref) -> Result<JsonValue> {
let channel = target.fetch_channel().await?;
channel.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel)
......
......@@ -3,7 +3,7 @@ use std::collections::HashSet;
use crate::database::*;
use crate::util::result::{Error, Result};
use futures::StreamExt;
use futures::{StreamExt, try_join};
use mongodb::{
bson::{doc, from_document},
options::FindOptions,
......@@ -28,7 +28,12 @@ pub struct Options {
#[validate(length(min = 26, max = 26))]
after: Option<String>,
sort: Option<Sort>,
include_users: Option<bool>
// Specifying 'nearby' ignores 'before', 'after' and 'sort'.
// It will also take half of limit rounded as the limits to each side.
// It also fetches the message ID specified.
#[validate(length(min = 26, max = 26))]
nearby: Option<String>,
include_users: Option<bool>,
}
#[get("/<target>/messages?<options..>")]
......@@ -38,6 +43,7 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
.map_err(|error| Error::FailedValidation { error })?;
let target = target.fetch_channel().await?;
target.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
......@@ -47,46 +53,109 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
Err(Error::MissingPermission)?
}
let mut query = doc! { "channel": target.id() };
if let Some(before) = &options.before {
query.insert("_id", doc! { "$lt": before });
}
if let Some(after) = &options.after {
query.insert("_id", doc! { "$gt": after });
}
let mut messages = vec![];
let sort = if let Sort::Latest = options.sort.as_ref().unwrap_or_else(|| &Sort::Latest) {
-1
} else {
1
};
let mut cursor = get_collection("messages")
.find(
query,
FindOptions::builder()
.limit(options.limit.unwrap_or(50))
.sort(doc! {
"_id": sort
})
.build(),
let collection = get_collection("messages");
let limit = options.limit.unwrap_or(50);
let channel = target.id();
if let Some(nearby) = &options.nearby {
let mut cursors = try_join!(
collection.find(
doc! {
"channel": channel,
"_id": {
"$gte": &nearby
}
},
FindOptions::builder()
.limit(limit / 2 + 1)
.sort(doc! {
"_id": 1
})
.build(),
),
collection.find(
doc! {
"channel": channel,
"_id": {
"$lt": &nearby
}
},
FindOptions::builder()
.limit(limit / 2)
.sort(doc! {
"_id": -1
})
.build(),
)
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "messages",
})?;
let mut messages = vec![];
while let Some(result) = cursor.next().await {
if let Ok(doc) = result {
messages.push(
from_document::<Message>(doc).map_err(|_| Error::DatabaseError {
operation: "from_document",
with: "message",
})?,
);
while let Some(result) = cursors.0.next().await {
if let Ok(doc) = result {
messages.push(
from_document::<Message>(doc).map_err(|_| Error::DatabaseError {
operation: "from_document",
with: "message",
})?,
);
}
}
while let Some(result) = cursors.1.next().await {
if let Ok(doc) = result {
messages.push(
from_document::<Message>(doc).map_err(|_| Error::DatabaseError {
operation: "from_document",
with: "message",
})?,
);
}
}
} else {
let mut query = doc! { "channel": target.id() };
if let Some(before) = &options.before {
query.insert("_id", doc! { "$lt": before });
}
if let Some(after) = &options.after {
query.insert("_id", doc! { "$gt": after });
}
let sort: i32 = if let Sort::Latest = options.sort.as_ref().unwrap_or_else(|| &Sort::Latest) {
-1
} else {
1
};
let mut cursor = collection
.find(
query,
FindOptions::builder()
.limit(limit)
.sort(doc! {
"_id": sort
})
.build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "messages",
})?;
while let Some(result) = cursor.next().await {
if let Ok(doc) = result {
messages.push(
from_document::<Message>(doc).map_err(|_| Error::DatabaseError {
operation: "from_document",
with: "message",
})?,
);
}
}
}
......@@ -96,12 +165,22 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
ids.insert(message.author.clone());
}
ids.remove(&user.id);
let user_ids = ids.into_iter().collect();
let users = user.fetch_multiple_users(user_ids).await?;
Ok(json!({
"messages": messages,
"users": user.fetch_multiple_users(user_ids).await?
}))
if let Channel::TextChannel { server, .. } = target {
Ok(json!({
"messages": messages,
"users": users,
"members": Server::fetch_members(&server).await?
}))
} else {
Ok(json!({
"messages": messages,
"users": users,
}))
}
} else {
Ok(json!(messages))
}
......
......@@ -18,6 +18,7 @@ pub async fn req(user: User, target: Ref, data: Json<Options>) -> Result<JsonVal
}
let target = target.fetch_channel().await?;
target.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
......
use std::collections::HashSet;
use crate::database::*;
use crate::util::result::{Error, Result};
use futures::StreamExt;
use mongodb::{
bson::{doc, from_document},
options::FindOptions,
};
use rocket_contrib::json::{Json, JsonValue};
use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Serialize, Deserialize, FromFormValue)]
pub enum Sort {
Relevance,
Latest,
Oldest,
}
impl Default for Sort {
fn default() -> Sort {
Sort::Relevance
}
}
#[derive(Validate, Serialize, Deserialize, FromForm)]
pub struct Options {
#[validate(length(min = 1, max = 64))]
query: String,
#[validate(range(min = 1, max = 100))]
limit: Option<i64>,
#[validate(length(min = 26, max = 26))]
before: Option<String>,
#[validate(length(min = 26, max = 26))]
after: Option<String>,
#[serde(default = "Sort::default")]
sort: Sort,
include_users: Option<bool>,
}
#[post("/<target>/search", data = "<options>")]
pub async fn req(user: User, target: Ref, options: Json<Options>) -> Result<JsonValue> {
options
.validate()
.map_err(|error| Error::FailedValidation { error })?;
let target = target.fetch_channel().await?;
target.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
let mut messages = vec![];
let limit = options.limit.unwrap_or(50);
let mut filter = doc! {
"channel": target.id(),
"$text": {
"$search": &options.query
}
};
if let Some(doc) = match (&options.before, &options.after) {
(Some(before), Some(after)) => Some(doc! {
"lt": before,
"gt": after
}),
(Some(before), _) => Some(doc! {
"lt": before
}),
(_, Some(after)) => Some(doc! {
"gt": after
}),
_ => None
} {
filter.insert("_id", doc);
}
let mut cursor = get_collection("messages")
.find(
filter,
FindOptions::builder()
.projection(
if let Sort::Relevance = &options.sort {
doc! {
"score": {
"$meta": "textScore"
}
}
} else {
doc! {}
}
)
.limit(limit)
.sort(
match &options.sort {
Sort::Relevance => doc! {
"score": {
"$meta": "textScore"
}
},
Sort::Latest => doc! {
"_id": -1
},
Sort::Oldest => doc! {
"_id": 1
}
}
)
.build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "messages",
})?;
while let Some(result) = cursor.next().await {
if let Ok(doc) = result {
messages.push(
from_document::<Message>(doc).map_err(|_| Error::DatabaseError {
operation: "from_document",
with: "message",
})?,
);
}
}
if options.include_users.unwrap_or_else(|| false) {
let mut ids = HashSet::new();
for message in &messages {
ids.insert(message.author.clone());
}
ids.remove(&user.id);
let user_ids = ids.into_iter().collect();
let users = user.fetch_multiple_users(user_ids).await?;
if let Channel::TextChannel { server, .. } = target {
Ok(json!({
"messages": messages,
"users": users,
"members": Server::fetch_members(&server).await?
}))
} else {
Ok(json!({
"messages": messages,
"users": users,
}))
}
} else {
Ok(json!(messages))
}
}
use std::collections::HashSet;
use crate::database::*;
use crate::util::result::{Error, Result};
use mongodb::{bson::doc, options::FindOneOptions};
use regex::Regex;
use rocket_contrib::json::{Json, JsonValue};
use serde::{Deserialize, Serialize};
use ulid::Ulid;
use validator::Validate;
#[derive(Serialize, Deserialize)]
pub struct Reply {
id: String,
mention: bool
}
#[derive(Validate, Serialize, Deserialize)]
pub struct Data {
#[validate(length(min = 0, max = 2000))]
......@@ -15,27 +24,37 @@ pub struct Data {
#[validate(length(min = 1, max = 36))]
nonce: String,
#[validate(length(min = 1, max = 128))]
attachment: Option<String>,
attachments: Option<Vec<String>>,
replies: Option<Vec<Reply>>,
}
lazy_static! {
static ref RE_ULID: Regex = Regex::new(r"<@([0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26})>").unwrap();
}
#[post("/<target>/messages", data = "<message>")]
pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonValue> {
let message = message.into_inner();
message
.validate()
.map_err(|error| Error::FailedValidation { error })?;
if message.content.len() == 0 && message.attachment.is_none() {
if message.content.len() == 0
&& (message.attachments.is_none() || message.attachments.as_ref().unwrap().len() == 0)
{
return Err(Error::EmptyMessage);
}
let target = target.fetch_channel().await?;
target.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_send_message() {
Err(Error::MissingPermission)?
return Err(Error::MissingPermission)
}
if get_collection("messages")
......@@ -58,11 +77,49 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
}
let id = Ulid::new().to_string();
let attachments = if let Some(attachment_id) = &message.attachment {
Some(vec![ File::find_and_use(attachment_id, "attachments", "message", &id).await? ])
} else {
None
};
let mut mentions = HashSet::new();
if let Some(captures) = RE_ULID.captures_iter(&message.content).next() {
// ! FIXME: in the future, verify in group so we can send out push
mentions.insert(captures[1].to_string());
}
let mut replies = HashSet::new();
if let Some(entries) = message.replies {
// ! FIXME: move this to app config
if entries.len() >= 5 {
return Err(Error::TooManyReplies)
}
for Reply { id, mention } in entries {
let message = Ref::from_unchecked(id)
.fetch_message(&target)
.await?;
replies.insert(message.id);
if mention {
mentions.insert(message.author);
}
}
}
let mut attachments = vec![];
if let Some(ids) = &message.attachments {
if ids.len() > 0 && !perm.get_upload_files() {
return Err(Error::MissingPermission)
}
// ! FIXME: move this to app config
if ids.len() >= 5 {
return Err(Error::TooManyAttachments)
}
for attachment_id in ids {
attachments
.push(File::find_and_use(attachment_id, "attachments", "message", &id).await?);
}
}
let msg = Message {
id,
......@@ -70,13 +127,24 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
author: user.id,
content: Content::Text(message.content.clone()),
attachments,
nonce: Some(message.nonce.clone()),
edited: None,
embeds: None,
attachments: if attachments.len() > 0 { Some(attachments) } else { None },
mentions: if mentions.len() > 0 {
Some(mentions.into_iter().collect::<Vec<String>>())
} else {
None
},
replies: if replies.len() > 0 {
Some(replies.into_iter().collect::<Vec<String>>())
} else {
None
},
};
msg.clone().publish(&target).await?;
msg.clone().publish(&target, perm.get_embed_links()).await?;
Ok(json!(msg))
}
use rocket::Route;
mod delete_channel;
mod edit_channel;
mod fetch_channel;
mod fetch_members;
mod channel_ack;
mod channel_delete;
mod channel_edit;
mod channel_fetch;
mod members_fetch;
mod group_add_member;
mod group_create;
mod group_remove_member;
mod join_call;
mod invite_create;
mod voice_join;
mod message_delete;
mod message_edit;
mod message_fetch;
mod message_query;
mod message_search;
mod message_query_stale;
mod message_send;
mod permissions_set;
mod permissions_set_default;
pub fn routes() -> Vec<Route> {
routes![
fetch_channel::req,
fetch_members::req,
delete_channel::req,
edit_channel::req,
channel_ack::req,
channel_fetch::req,
members_fetch::req,
channel_delete::req,
channel_edit::req,
invite_create::req,
message_send::req,
message_query::req,
message_search::req,
message_query_stale::req,
message_fetch::req,
message_edit::req,
......@@ -30,6 +38,8 @@ pub fn routes() -> Vec<Route> {
group_create::req,
group_add_member::req,
group_remove_member::req,
join_call::req,
voice_join::req,
permissions_set::req,
permissions_set_default::req,
]
}
use mongodb::bson::doc;
use rocket_contrib::json::Json;
use serde::{Serialize, Deserialize};
use validator::Contains;
use crate::database::*;
use crate::database::permissions::channel::ChannelPermission;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
#[derive(Serialize, Deserialize)]
pub struct Data {
permissions: u32
}
#[put("/<target>/permissions/<role>", data = "<data>", rank = 2)]
pub async fn req(user: User, target: Ref, role: String, data: Json<Data>) -> Result<()> {
let target = target.fetch_channel().await?;
match target {
Channel::TextChannel { id, server, mut role_permissions, .. }
| Channel::VoiceChannel { id, server, mut role_permissions, .. } => {
let target = Ref::from_unchecked(server).fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_manage_roles() {
return Err(Error::MissingPermission);
}
if !target.roles.has_element(&role) {
return Err(Error::NotFound);
}
let permissions: u32 = ChannelPermission::View as u32 | data.permissions;
get_collection("channels")
.update_one(
doc! { "_id": &id },
doc! {
"$set": {
"role_permissions.".to_owned() + &role: permissions as i32
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channel"
})?;
role_permissions.insert(role, permissions as i32);
ClientboundNotification::ChannelUpdate {
id: id.clone(),
data: json!({
"role_permissions": role_permissions
}),
clear: None
}
.publish(id);
Ok(())
}
_ => Err(Error::InvalidOperation)
}
}
use mongodb::bson::doc;
use rocket_contrib::json::Json;
use serde::{Serialize, Deserialize};
use crate::database::*;
use crate::database::permissions::channel::{ ChannelPermission, DEFAULT_PERMISSION_DM };
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
#[derive(Serialize, Deserialize)]
pub struct Data {
permissions: u32
}
#[put("/<target>/permissions/default", data = "<data>", rank = 1)]
pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
let target = target.fetch_channel().await?;
match target {
Channel::Group { id, owner, .. } => {
if user.id == owner {
let permissions: u32 = ChannelPermission::View as u32 | (data.permissions & *DEFAULT_PERMISSION_DM);
get_collection("channels")
.update_one(
doc! { "_id": &id },
doc! {
"$set": {
"permissions": permissions as i32
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channel"
})?;
ClientboundNotification::ChannelUpdate {
id: id.clone(),
data: json!({
"permissions": permissions as i32
}),
clear: None
}
.publish(id);
Ok(())
} else {
Err(Error::MissingPermission)
}
}
Channel::TextChannel { id, server, .. }
| Channel::VoiceChannel { id, server, .. } => {
let target = Ref::from_unchecked(server).fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_manage_roles() {
return Err(Error::MissingPermission);
}
let permissions: u32 = ChannelPermission::View as u32 | data.permissions;
get_collection("channels")
.update_one(
doc! { "_id": &id },
doc! {
"$set": {
"default_permissions": permissions as i32
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channel"
})?;
ClientboundNotification::ChannelUpdate {
id: id.clone(),
data: json!({
"default_permissions": permissions as i32
}),
clear: None
}
.publish(id);
Ok(())
}
_ => Err(Error::InvalidOperation)
}
}
......@@ -17,6 +17,13 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
}
let target = target.fetch_channel().await?;
match target {
Channel::SavedMessages { .. } | Channel::TextChannel { .. } => {
return Err(Error::CannotJoinCall)
}
_ => {}
}
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
......
use crate::database::*;
use crate::util::result::{Error, Result};
#[delete("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<()> {
let target = target.fetch_invite().await?;
if target.creator() == &user.id {
target.delete().await
} else {
match &target {
Invite::Server { server, .. } => {
let server = Ref::from_unchecked(server.clone()).fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&server)
.for_server()
.await?;
if !perm.get_manage_server() {
return Err(Error::MissingPermission);
}
target.delete().await
}
_ => unreachable!(),
}
}
}
use crate::database::*;
use crate::util::result::Result;
use rocket_contrib::json::JsonValue;
use serde::Serialize;
#[derive(Serialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum InviteResponse {
Server {
server_id: String,
server_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
server_icon: Option<File>,
#[serde(skip_serializing_if = "Option::is_none")]
server_banner: Option<File>,
channel_id: String,
channel_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
channel_description: Option<String>,
user_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
user_avatar: Option<File>,
member_count: i64,
},
}
#[get("/<target>")]
pub async fn req(target: Ref) -> Result<JsonValue> {
let target = target.fetch_invite().await?;
match target {
Invite::Server {
channel, creator, ..
} => {
let channel = Ref::from_unchecked(channel).fetch_channel().await?;
let creator = Ref::from_unchecked(creator).fetch_user().await?;
match channel {
Channel::TextChannel { id, server, name, description, .. }
| Channel::VoiceChannel { id, server, name, description, .. } => {
let server = Ref::from_unchecked(server).fetch_server().await?;
Ok(json!(InviteResponse::Server {
member_count: Server::get_member_count(&server.id).await?,
server_id: server.id,
server_name: server.name,
server_icon: server.icon,
server_banner: server.banner,
channel_id: id,
channel_name: name,
channel_description: description,
user_name: creator.username,
user_avatar: creator.avatar
}))
}
_ => unreachable!()
}
}
_ => unreachable!(),
}
}
use crate::database::*;
use crate::util::result::Result;
use rocket_contrib::json::JsonValue;
#[post("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let target = target.fetch_invite().await?;
match target {
Invite::Server { channel, .. } => {
let channel = Ref::from_unchecked(channel).fetch_channel().await?;
let server = match &channel {
Channel::TextChannel { server, .. }
| Channel::VoiceChannel { server, .. } => {
Ref::from_unchecked(server.clone()).fetch_server().await?
}
_ => unreachable!()
};
server.join_member(&user.id).await?;
Ok(json!({
"type": "Server",
"channel": channel,
"server": server
}))
}
_ => unreachable!(),
}
}
use rocket::Route;
mod invite_delete;
mod invite_fetch;
mod invite_join;
pub fn routes() -> Vec<Route> {
routes![invite_fetch::req, invite_join::req, invite_delete::req]
}
......@@ -3,10 +3,12 @@ pub use rocket::response::Redirect;
use rocket::Rocket;
mod channels;
mod guild;
mod invites;
mod onboard;
mod push;
mod root;
mod servers;
mod sync;
mod users;
pub fn mount(rocket: Rocket) -> Rocket {
......@@ -15,6 +17,8 @@ pub fn mount(rocket: Rocket) -> Rocket {
.mount("/onboard", onboard::routes())
.mount("/users", users::routes())
.mount("/channels", channels::routes())
.mount("/guild", guild::routes())
.mount("/servers", servers::routes())
.mount("/invites", invites::routes())
.mount("/push", push::routes())
.mount("/sync", sync::routes())
}
use crate::util::variables::{
APP_URL, JANUARY_URL, AUTUMN_URL, DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY,
USE_AUTUMN, USE_JANUARY, USE_EMAIL, USE_HCAPTCHA, USE_VOSO, VAPID_PUBLIC_KEY, VOSO_URL, VOSO_WS_HOST,
APP_URL, AUTUMN_URL, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY, JANUARY_URL, USE_AUTUMN,
USE_EMAIL, USE_HCAPTCHA, USE_JANUARY, USE_VOSO, VAPID_PUBLIC_KEY, VOSO_URL, VOSO_WS_HOST,
};
use mongodb::bson::doc;
......@@ -11,7 +11,6 @@ pub async fn root() -> JsonValue {
json!({
"revolt": crate::version::VERSION,
"features": {
"registration": !*DISABLE_REGISTRATION,
"captcha": {
"enabled": *USE_HCAPTCHA,
"key": HCAPTCHA_SITEKEY.to_string()
......
use crate::database::*;
use crate::util::result::{Error, Result};
use mongodb::bson::doc;
use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Validate, Serialize, Deserialize)]
pub struct Data {
#[validate(length(min = 1, max = 1024))]
reason: Option<String>,
}
#[put("/<server>/bans/<target>", data = "<data>")]
pub async fn req(user: User, server: Ref, target: Ref, data: Json<Data>) -> Result<()> {
let data = data.into_inner();
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
let server = server.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&server)
.for_server()
.await?;
if !perm.get_ban_members() {
Err(Error::MissingPermission)?
}
let target = target.fetch_user().await?;
if target.id == user.id {
return Err(Error::InvalidOperation);
}
if target.id == server.owner {
return Err(Error::MissingPermission);
}
let mut document = doc! {
"_id": {
"server": &server.id,
"user": &target.id
}
};
if let Some(reason) = data.reason {
document.insert("reason", reason);
}
get_collection("server_bans")
.insert_one(document, None)
.await
.map_err(|_| Error::DatabaseError {
operation: "insert_one",
with: "server_ban",
})?;
server.remove_member(&target.id, RemoveMember::Ban).await
}