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; ...@@ -6,6 +6,7 @@ use mongodb::bson::doc;
#[delete("/<target>/messages/<msg>")] #[delete("/<target>/messages/<msg>")]
pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> { pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> {
let channel = target.fetch_channel().await?; let channel = target.fetch_channel().await?;
channel.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel) .with_channel(&channel)
......
...@@ -2,7 +2,7 @@ use crate::database::*; ...@@ -2,7 +2,7 @@ use crate::database::*;
use crate::util::result::{Error, Result}; use crate::util::result::{Error, Result};
use chrono::Utc; use chrono::Utc;
use mongodb::bson::{doc, Bson, DateTime}; use mongodb::bson::{doc, Bson, DateTime, Document};
use rocket_contrib::json::Json; use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use validator::Validate; use validator::Validate;
...@@ -19,6 +19,8 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result< ...@@ -19,6 +19,8 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
.map_err(|error| Error::FailedValidation { error })?; .map_err(|error| Error::FailedValidation { error })?;
let channel = target.fetch_channel().await?; let channel = target.fetch_channel().await?;
channel.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel) .with_channel(&channel)
.for_channel() .for_channel()
...@@ -27,22 +29,41 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result< ...@@ -27,22 +29,41 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
Err(Error::MissingPermission)? Err(Error::MissingPermission)?
} }
let message = msg.fetch_message(&channel).await?; let mut message = msg.fetch_message(&channel).await?;
if message.author != user.id { if message.author != user.id {
Err(Error::CannotEditMessage)? Err(Error::CannotEditMessage)?
} }
let edited = Utc::now(); 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") get_collection("messages")
.update_one( .update_one(
doc! { doc! {
"_id": &message.id "_id": &message.id
}, },
doc! { doc! {
"$set": { "$set": set
"content": &edit.content,
"edited": Bson::DateTime(edited)
}
}, },
None, None,
) )
...@@ -52,7 +73,5 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result< ...@@ -52,7 +73,5 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
with: "message", with: "message",
})?; })?;
message message.publish_update(update).await
.publish_update(json!({ "content": edit.content, "edited": DateTime(edited) }))
.await
} }
...@@ -6,6 +6,7 @@ use rocket_contrib::json::JsonValue; ...@@ -6,6 +6,7 @@ use rocket_contrib::json::JsonValue;
#[get("/<target>/messages/<msg>")] #[get("/<target>/messages/<msg>")]
pub async fn req(user: User, target: Ref, msg: Ref) -> Result<JsonValue> { pub async fn req(user: User, target: Ref, msg: Ref) -> Result<JsonValue> {
let channel = target.fetch_channel().await?; let channel = target.fetch_channel().await?;
channel.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel) .with_channel(&channel)
......
...@@ -3,7 +3,7 @@ use std::collections::HashSet; ...@@ -3,7 +3,7 @@ use std::collections::HashSet;
use crate::database::*; use crate::database::*;
use crate::util::result::{Error, Result}; use crate::util::result::{Error, Result};
use futures::StreamExt; use futures::{StreamExt, try_join};
use mongodb::{ use mongodb::{
bson::{doc, from_document}, bson::{doc, from_document},
options::FindOptions, options::FindOptions,
...@@ -28,7 +28,12 @@ pub struct Options { ...@@ -28,7 +28,12 @@ pub struct Options {
#[validate(length(min = 26, max = 26))] #[validate(length(min = 26, max = 26))]
after: Option<String>, after: Option<String>,
sort: Option<Sort>, 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..>")] #[get("/<target>/messages?<options..>")]
...@@ -38,6 +43,7 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json ...@@ -38,6 +43,7 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
.map_err(|error| Error::FailedValidation { error })?; .map_err(|error| Error::FailedValidation { error })?;
let target = target.fetch_channel().await?; let target = target.fetch_channel().await?;
target.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target) .with_channel(&target)
...@@ -47,46 +53,109 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json ...@@ -47,46 +53,109 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
Err(Error::MissingPermission)? Err(Error::MissingPermission)?
} }
let mut query = doc! { "channel": target.id() }; let mut messages = vec![];
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 = if let Sort::Latest = options.sort.as_ref().unwrap_or_else(|| &Sort::Latest) { let collection = get_collection("messages");
-1 let limit = options.limit.unwrap_or(50);
} else { let channel = target.id();
1 if let Some(nearby) = &options.nearby {
}; let mut cursors = try_join!(
let mut cursor = get_collection("messages") collection.find(
.find( doc! {
query, "channel": channel,
FindOptions::builder() "_id": {
.limit(options.limit.unwrap_or(50)) "$gte": &nearby
.sort(doc! { }
"_id": sort },
}) FindOptions::builder()
.build(), .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 { .map_err(|_| Error::DatabaseError {
operation: "find", operation: "find",
with: "messages", with: "messages",
})?; })?;
let mut messages = vec![]; while let Some(result) = cursors.0.next().await {
while let Some(result) = cursor.next().await { if let Ok(doc) = result {
if let Ok(doc) = result { messages.push(
messages.push( from_document::<Message>(doc).map_err(|_| Error::DatabaseError {
from_document::<Message>(doc).map_err(|_| Error::DatabaseError { operation: "from_document",
operation: "from_document", with: "message",
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 ...@@ -96,12 +165,22 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
ids.insert(message.author.clone()); ids.insert(message.author.clone());
} }
ids.remove(&user.id);
let user_ids = ids.into_iter().collect(); let user_ids = ids.into_iter().collect();
let users = user.fetch_multiple_users(user_ids).await?;
Ok(json!({ if let Channel::TextChannel { server, .. } = target {
"messages": messages, Ok(json!({
"users": user.fetch_multiple_users(user_ids).await? "messages": messages,
})) "users": users,
"members": Server::fetch_members(&server).await?
}))
} else {
Ok(json!({
"messages": messages,
"users": users,
}))
}
} else { } else {
Ok(json!(messages)) Ok(json!(messages))
} }
......
...@@ -18,6 +18,7 @@ pub async fn req(user: User, target: Ref, data: Json<Options>) -> Result<JsonVal ...@@ -18,6 +18,7 @@ pub async fn req(user: User, target: Ref, data: Json<Options>) -> Result<JsonVal
} }
let target = target.fetch_channel().await?; let target = target.fetch_channel().await?;
target.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target) .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::database::*;
use crate::util::result::{Error, Result}; use crate::util::result::{Error, Result};
use mongodb::{bson::doc, options::FindOneOptions}; use mongodb::{bson::doc, options::FindOneOptions};
use regex::Regex;
use rocket_contrib::json::{Json, JsonValue}; use rocket_contrib::json::{Json, JsonValue};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ulid::Ulid; use ulid::Ulid;
use validator::Validate; use validator::Validate;
#[derive(Serialize, Deserialize)]
pub struct Reply {
id: String,
mention: bool
}
#[derive(Validate, Serialize, Deserialize)] #[derive(Validate, Serialize, Deserialize)]
pub struct Data { pub struct Data {
#[validate(length(min = 0, max = 2000))] #[validate(length(min = 0, max = 2000))]
...@@ -15,27 +24,37 @@ pub struct Data { ...@@ -15,27 +24,37 @@ pub struct Data {
#[validate(length(min = 1, max = 36))] #[validate(length(min = 1, max = 36))]
nonce: String, nonce: String,
#[validate(length(min = 1, max = 128))] #[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>")] #[post("/<target>/messages", data = "<message>")]
pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonValue> { pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonValue> {
let message = message.into_inner();
message message
.validate() .validate()
.map_err(|error| Error::FailedValidation { error })?; .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); return Err(Error::EmptyMessage);
} }
let target = target.fetch_channel().await?; let target = target.fetch_channel().await?;
target.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target) .with_channel(&target)
.for_channel() .for_channel()
.await?; .await?;
if !perm.get_send_message() { if !perm.get_send_message() {
Err(Error::MissingPermission)? return Err(Error::MissingPermission)
} }
if get_collection("messages") if get_collection("messages")
...@@ -58,11 +77,49 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal ...@@ -58,11 +77,49 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
} }
let id = Ulid::new().to_string(); 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? ]) let mut mentions = HashSet::new();
} else { if let Some(captures) = RE_ULID.captures_iter(&message.content).next() {
None // ! 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 { let msg = Message {
id, id,
...@@ -70,13 +127,24 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal ...@@ -70,13 +127,24 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
author: user.id, author: user.id,
content: Content::Text(message.content.clone()), content: Content::Text(message.content.clone()),
attachments,
nonce: Some(message.nonce.clone()), nonce: Some(message.nonce.clone()),
edited: None, edited: None,
embeds: 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)) Ok(json!(msg))
} }
use rocket::Route; use rocket::Route;
mod delete_channel; mod channel_ack;
mod edit_channel; mod channel_delete;
mod fetch_channel; mod channel_edit;
mod fetch_members; mod channel_fetch;
mod members_fetch;
mod group_add_member; mod group_add_member;
mod group_create; mod group_create;
mod group_remove_member; mod group_remove_member;
mod join_call; mod invite_create;
mod voice_join;
mod message_delete; mod message_delete;
mod message_edit; mod message_edit;
mod message_fetch; mod message_fetch;
mod message_query; mod message_query;
mod message_search;
mod message_query_stale; mod message_query_stale;
mod message_send; mod message_send;
mod permissions_set;
mod permissions_set_default;
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![ routes![
fetch_channel::req, channel_ack::req,
fetch_members::req, channel_fetch::req,
delete_channel::req, members_fetch::req,
edit_channel::req, channel_delete::req,
channel_edit::req,
invite_create::req,
message_send::req, message_send::req,
message_query::req, message_query::req,
message_search::req,
message_query_stale::req, message_query_stale::req,
message_fetch::req, message_fetch::req,
message_edit::req, message_edit::req,
...@@ -30,6 +38,8 @@ pub fn routes() -> Vec<Route> { ...@@ -30,6 +38,8 @@ pub fn routes() -> Vec<Route> {
group_create::req, group_create::req,
group_add_member::req, group_add_member::req,
group_remove_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> { ...@@ -17,6 +17,13 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
} }
let target = target.fetch_channel().await?; let target = target.fetch_channel().await?;
match target {
Channel::SavedMessages { .. } | Channel::TextChannel { .. } => {
return Err(Error::CannotJoinCall)
}
_ => {}
}
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target) .with_channel(&target)
.for_channel() .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; ...@@ -3,10 +3,12 @@ pub use rocket::response::Redirect;
use rocket::Rocket; use rocket::Rocket;
mod channels; mod channels;
mod guild; mod invites;
mod onboard; mod onboard;
mod push; mod push;
mod root; mod root;
mod servers;
mod sync;
mod users; mod users;
pub fn mount(rocket: Rocket) -> Rocket { pub fn mount(rocket: Rocket) -> Rocket {
...@@ -15,6 +17,8 @@ pub fn mount(rocket: Rocket) -> Rocket { ...@@ -15,6 +17,8 @@ pub fn mount(rocket: Rocket) -> Rocket {
.mount("/onboard", onboard::routes()) .mount("/onboard", onboard::routes())
.mount("/users", users::routes()) .mount("/users", users::routes())
.mount("/channels", channels::routes()) .mount("/channels", channels::routes())
.mount("/guild", guild::routes()) .mount("/servers", servers::routes())
.mount("/invites", invites::routes())
.mount("/push", push::routes()) .mount("/push", push::routes())
.mount("/sync", sync::routes())
} }
use crate::util::variables::{ use crate::util::variables::{
APP_URL, JANUARY_URL, AUTUMN_URL, DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY, APP_URL, AUTUMN_URL, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY, JANUARY_URL, USE_AUTUMN,
USE_AUTUMN, USE_JANUARY, USE_EMAIL, USE_HCAPTCHA, USE_VOSO, VAPID_PUBLIC_KEY, VOSO_URL, VOSO_WS_HOST, USE_EMAIL, USE_HCAPTCHA, USE_JANUARY, USE_VOSO, VAPID_PUBLIC_KEY, VOSO_URL, VOSO_WS_HOST,
}; };
use mongodb::bson::doc; use mongodb::bson::doc;
...@@ -11,7 +11,6 @@ pub async fn root() -> JsonValue { ...@@ -11,7 +11,6 @@ pub async fn root() -> JsonValue {
json!({ json!({
"revolt": crate::version::VERSION, "revolt": crate::version::VERSION,
"features": { "features": {
"registration": !*DISABLE_REGISTRATION,
"captcha": { "captcha": {
"enabled": *USE_HCAPTCHA, "enabled": *USE_HCAPTCHA,
"key": HCAPTCHA_SITEKEY.to_string() "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
}