chore(thanos): strip down codebase to just API routes

This commit is contained in:
Paul Makles
2022-01-27 14:16:30 +00:00
parent ab4c3f913e
commit 0fdb749199
116 changed files with 893 additions and 10025 deletions

View File

@@ -1,13 +1,9 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use crate::util::variables::MAX_BOT_COUNT;
use crate::util::regex::RE_USERNAME;
use mongodb::bson::{doc, to_document};
use revolt_quark::Result;
use rocket::serde::json::{Json, Value};
use serde::{Deserialize, Serialize};
use ulid::Ulid;
use nanoid::nanoid;
use validator::Validate;
#[derive(Validate, Serialize, Deserialize)]
@@ -17,73 +13,6 @@ pub struct Data {
}
#[post("/create", data = "<info>")]
pub async fn create_bot(user: User, info: Json<Data>) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let info = info.into_inner();
info.validate()
.map_err(|error| Error::FailedValidation { error })?;
if get_collection("bots")
.count_documents(
doc! {
"owner": &user.id
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "count_documents",
with: "bots",
})? as usize >= *MAX_BOT_COUNT {
return Err(Error::ReachedMaximumBots)
}
let id = Ulid::new().to_string();
let token = nanoid!(64);
let bot = Bot {
id: id.clone(),
owner: user.id.clone(),
token,
public: false,
analytics: false,
discoverable: false,
interactions_url: None
};
if User::is_username_taken(&info.name).await? {
return Err(Error::UsernameTaken);
}
get_collection("users")
.insert_one(
doc! {
"_id": &id,
"username": &info.name,
"bot": {
"owner": &user.id
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "insert_one",
with: "user",
})?;
get_collection("bots")
.insert_one(
to_document(&bot).map_err(|_| Error::DatabaseError { with: "bot", operation: "to_document" })?,
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "insert_one",
with: "user",
})?;
Ok(json!(bot))
pub async fn create_bot(/* user: User ,*/ info: Json<Data>) -> Result<Value> {
todo!()
}

View File

@@ -1,67 +1,7 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, EmptyResponse, Result};
use mongodb::bson::doc;
use revolt_quark::Result;
use rauth::util::EmptyResponse;
#[delete("/<target>")]
pub async fn delete_bot(user: User, target: Ref) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let bot = target.fetch_bot().await?;
if bot.owner != user.id {
return Err(Error::MissingPermission);
}
let username = format!("Deleted User {}", &bot.id);
get_collection("users")
.update_one(
doc! {
"_id": &bot.id
},
doc! {
"$set": {
"username": &username,
"flags": 2
},
"$unset": {
"avatar": 1,
"status": 1,
"profile": 1
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
with: "user",
operation: "update_one"
})?;
ClientboundNotification::UserUpdate {
id: target.id.clone(),
data: json!({
"username": username,
"flags": 2
}),
clear: None,
}
.publish_as_user(target.id.clone());
get_collection("bots")
.delete_one(
doc! {
"_id": &bot.id
},
None
)
.await
.map_err(|_| Error::DatabaseError {
with: "bot",
operation: "delete_one"
})?;
Ok(EmptyResponse {})
pub async fn delete_bot(/*user: UserRef, target: Ref*/ target: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,8 +1,7 @@
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use crate::{database::*, notifications::events::RemoveBotField};
use crate::util::regex::RE_USERNAME;
use revolt_quark::{Result, models::bot::FieldsBot, EmptyResponse};
use mongodb::bson::doc;
use rocket::serde::json::Json;
use serde::{Deserialize, Serialize};
@@ -16,105 +15,10 @@ pub struct Data {
public: Option<bool>,
analytics: Option<bool>,
interactions_url: Option<String>,
remove: Option<RemoveBotField>,
remove: Option<FieldsBot>,
}
#[patch("/<target>", data = "<data>")]
pub async fn edit_bot(user: User, target: Ref, data: Json<Data>) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let data = data.into_inner();
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
if data.name.is_none()
&& data.public.is_none()
&& data.analytics.is_none()
&& data.interactions_url.is_none()
&& data.remove.is_none()
{
return Ok(EmptyResponse {});
}
let bot = target.fetch_bot().await?;
if bot.owner != user.id {
return Err(Error::MissingPermission);
}
if let Some(name) = &data.name {
if User::is_username_taken(&name).await? {
return Err(Error::UsernameTaken);
}
get_collection("users")
.update_one(
doc! { "_id": &target.id },
doc! {
"$set": {
"username": name
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "user",
})?;
ClientboundNotification::UserUpdate {
id: target.id.clone(),
data: json!({
"username": name
}),
clear: None,
}
.publish_as_user(target.id.clone());
}
let mut set = doc! {};
let mut unset = doc! {};
if let Some(remove) = &data.remove {
match remove {
RemoveBotField::InteractionsURL => {
unset.insert("interactions_url", 1);
}
}
}
if let Some(public) = &data.public {
set.insert("public", public);
}
if let Some(analytics) = &data.analytics {
set.insert("analytics", analytics);
}
if let Some(interactions_url) = &data.interactions_url {
set.insert("interactions_url", interactions_url);
}
let mut operations = doc! {};
if set.len() > 0 {
operations.insert("$set", &set);
}
if unset.len() > 0 {
operations.insert("$unset", unset);
}
if operations.len() > 0 {
get_collection("bots")
.update_one(doc! { "_id": &target.id }, operations, None)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "bot",
})?;
}
Ok(EmptyResponse {})
pub async fn edit_bot(/*user: UserRef, target: Ref,*/ target: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,26 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::Result;
use serde_json::Value;
#[get("/<target>")]
pub async fn fetch_bot(user: User, target: Ref) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let bot = target.fetch_bot().await?;
if !bot.public {
if bot.owner != user.id {
return Err(Error::BotIsPrivate);
}
}
let user = Ref::from_unchecked(bot.id.clone()).fetch_user().await?;
Ok(json!({
"bot": bot,
"user": user
}))
pub async fn fetch_bot(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,56 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::Result;
use futures::StreamExt;
use mongodb::bson::{Document, doc, from_document};
use serde_json::Value;
#[get("/@me")]
pub async fn fetch_owned_bots(user: User) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let bots = get_collection("bots")
.find(
doc! {
"owner": &user.id
},
None
)
.await
.map_err(|_| Error::DatabaseError {
with: "bots",
operation: "find"
})?
.filter_map(async move |s| s.ok())
.collect::<Vec<Document>>()
.await
.into_iter()
.filter_map(|x| from_document(x).ok())
.collect::<Vec<Bot>>();
let users = get_collection("users")
.find(
doc! {
"bot.owner": &user.id
},
None
)
.await
.map_err(|_| Error::DatabaseError {
with: "users",
operation: "find"
})?
.filter_map(async move |s| s.ok())
.collect::<Vec<Document>>()
.await
.into_iter()
.filter_map(|x| from_document(x).ok())
.collect::<Vec<User>>();
Ok(json!({
"bots": bots,
"users": users
}))
pub async fn fetch_owned_bots(/*user: UserRef*/) -> Result<Value> {
todo!()
}

View File

@@ -1,28 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::Result;
use serde_json::Value;
#[get("/<target>/invite")]
pub async fn fetch_public_bot(user: User, target: Ref) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let bot = target.fetch_bot().await?;
if !bot.public {
if bot.owner != user.id {
return Err(Error::BotIsPrivate);
}
}
let user = Ref::from_unchecked(bot.id.clone()).fetch_user().await?;
Ok(json!({
"_id": bot.id,
"username": user.username,
"avatar": user.avatar,
"description": user.profile.map(|p| p.content)
}))
pub async fn fetch_public_bot(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,5 +1,4 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
use rocket::serde::json::Json;
use serde::Deserialize;
@@ -22,49 +21,6 @@ pub enum Destination {
}
#[post("/<target>/invite", data = "<dest>")]
pub async fn invite_bot(user: User, target: Ref, dest: Json<Destination>) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let bot = target.fetch_bot().await?;
if !bot.public {
if bot.owner != user.id {
return Err(Error::BotIsPrivate);
}
}
match dest.into_inner() {
Destination::Server(ServerId { server }) => {
let server = Ref::from(server)?.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&server)
.for_server()
.await?;
if !perm.get_manage_server() {
Err(Error::MissingPermission)?
}
server.join_member(&bot.id).await?;
Ok(EmptyResponse {})
}
Destination::Group(GroupId { group }) => {
let channel = Ref::from(group)?.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel)
.for_channel()
.await?;
if !perm.get_invite_others() {
Err(Error::MissingPermission)?
}
channel.add_to_group(bot.id, user.id).await?;
Ok(EmptyResponse {})
}
}
pub async fn invite_bot(/*user: UserRef, target: Ref,*/ target: String, dest: Json<Destination>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,38 +1,6 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
#[put("/<target>/ack/<message>")]
pub async fn req(user: User, target: Ref, message: Ref) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let target = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
crate::task_queue::task_ack::queue(
target.id().into(),
user.id.clone(),
crate::task_queue::task_ack::AckEvent::AckMessage {
id: message.id.clone()
}
).await;
ClientboundNotification::ChannelAck {
id: target.id().into(),
user: user.id.clone(),
message_id: message.id,
}
.publish(user.id);
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref, message: Ref*/ target: String, message: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,119 +1,8 @@
use crate::util::result::{Error, Result, EmptyResponse};
use crate::{database::*, notifications::events::ClientboundNotification};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
#[delete("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<EmptyResponse> {
let target = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
match &target {
Channel::SavedMessages { .. } => Err(Error::NoEffect),
Channel::DirectMessage { .. } => {
get_collection("channels")
.update_one(
doc! {
"_id": target.id()
},
doc! {
"$set": {
"active": false
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channel",
})?;
Ok(EmptyResponse {})
}
Channel::Group {
id,
owner,
recipients,
..
} => {
if &user.id == owner {
if let Some(new_owner) = recipients.iter().find(|x| *x != &user.id) {
get_collection("channels")
.update_one(
doc! {
"_id": &id
},
doc! {
"$set": {
"owner": new_owner
},
"$pull": {
"recipients": &user.id
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channel",
})?;
target.publish_update(json!({ "owner": new_owner })).await?;
} else {
target.delete().await?;
return Ok(EmptyResponse {});
}
} else {
get_collection("channels")
.update_one(
doc! {
"_id": &id
},
doc! {
"$pull": {
"recipients": &user.id
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channel",
})?;
}
ClientboundNotification::ChannelGroupLeave {
id: id.clone(),
user: user.id.clone(),
}
.publish(id.clone());
Content::SystemMessage(SystemMessage::UserLeft { id: user.id })
.send_as_system(&target)
.await
.ok();
Ok(EmptyResponse {})
}
Channel::TextChannel { .. } |
Channel::VoiceChannel { .. } => {
if perm.get_manage_channel() {
target.delete().await?;
Ok(EmptyResponse {})
} else {
Err(Error::MissingPermission)
}
}
}
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,8 +1,6 @@
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use crate::{database::*, notifications::events::RemoveChannelField};
use revolt_quark::{EmptyResponse, Result, models::channel::FieldsChannel};
use mongodb::bson::{doc, to_document};
use mongodb::bson::doc;
use rocket::serde::json::Json;
use serde::{Deserialize, Serialize};
use validator::Validate;
@@ -17,144 +15,12 @@ pub struct Data {
description: Option<String>,
#[validate(length(min = 1, max = 128))]
icon: Option<String>,
remove: Option<RemoveChannelField>,
remove: Option<FieldsChannel>,
#[serde(skip_serializing_if = "Option::is_none")]
nsfw: Option<bool>
}
#[patch("/<target>", data = "<data>")]
pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<EmptyResponse> {
let data = data.into_inner();
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
if data.name.is_none()
&& data.description.is_none()
&& data.icon.is_none()
&& data.remove.is_none()
&& data.nsfw.is_none()
{
return Ok(EmptyResponse {});
}
let target = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_manage_channel() {
Err(Error::MissingPermission)?
}
match &target {
Channel::Group { id, icon, .. }
| Channel::TextChannel { id, icon, .. }
| Channel::VoiceChannel { id, icon, .. } => {
let mut set = doc! {};
let mut unset = doc! {};
let mut remove_icon = false;
if let Some(remove) = &data.remove {
match remove {
RemoveChannelField::Icon => {
unset.insert("icon", 1);
remove_icon = true;
}
RemoveChannelField::Description => {
unset.insert("description", 1);
}
}
}
if let Some(name) = &data.name {
set.insert("name", name);
}
if let Some(description) = &data.description {
set.insert("description", description);
}
if let Some(attachment_id) = &data.icon {
let attachment =
File::find_and_use(&attachment_id, "icons", "object", target.id()).await?;
set.insert(
"icon",
to_document(&attachment).map_err(|_| Error::DatabaseError {
operation: "to_document",
with: "attachment",
})?,
);
remove_icon = true;
}
if let Some(nsfw) = &data.nsfw {
set.insert("nsfw", nsfw);
}
let mut operations = doc! {};
if set.len() > 0 {
operations.insert("$set", &set);
}
if unset.len() > 0 {
operations.insert("$unset", unset);
}
if operations.len() > 0 {
get_collection("channels")
.update_one(doc! { "_id": &id }, operations, None)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channel",
})?;
}
ClientboundNotification::ChannelUpdate {
id: id.clone(),
data: json!(set),
clear: data.remove,
}
.publish(id.clone());
if let Channel::Group { .. } = &target {
if let Some(name) = data.name {
Content::SystemMessage(SystemMessage::ChannelRenamed {
name,
by: user.id.clone(),
})
.send_as_system(&target)
.await
.ok();
}
if let Some(_) = data.description {
Content::SystemMessage(SystemMessage::ChannelDescriptionChanged {
by: user.id.clone(),
})
.send_as_system(&target)
.await
.ok();
}
if let Some(_) = data.icon {
Content::SystemMessage(SystemMessage::ChannelIconChanged { by: user.id })
.send_as_system(&target)
.await
.ok();
}
}
if remove_icon {
if let Some(old_icon) = icon {
old_icon.delete().await?;
}
}
Ok(EmptyResponse {})
}
_ => Err(Error::InvalidOperation),
}
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,20 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::Result;
use rocket::serde::json::Value;
#[get("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let target = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
Ok(json!(target))
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,24 +1,8 @@
use crate::util::result::{Error, Result, EmptyResponse};
use crate::database::*;
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
#[put("/<target>/recipients/<member>")]
pub async fn req(user: User, target: Ref, member: Ref) -> Result<EmptyResponse> {
if get_relationship(&user, &member.id) != RelationshipStatus::Friend {
Err(Error::NotFriends)?
}
let channel = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel)
.for_channel()
.await?;
if !perm.get_invite_others() {
Err(Error::MissingPermission)?
}
channel.add_to_group(member.id, user.id).await?;
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref, member: Ref*/ target: String, member: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,14 +1,8 @@
use crate::database::*;
use crate::util::idempotency::IdempotencyKey;
use crate::util::result::{Error, Result};
use crate::util::variables::MAX_GROUP_SIZE;
use revolt_quark::Result;
use mongodb::bson::doc;
use rocket::serde::json::{Json, Value};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::iter::FromIterator;
use ulid::Ulid;
use validator::Validate;
#[derive(Validate, Serialize, Deserialize)]
@@ -23,47 +17,6 @@ pub struct Data {
}
#[post("/create", data = "<info>")]
pub async fn req(_idempotency: IdempotencyKey, user: User, info: Json<Data>) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let info = info.into_inner();
info.validate()
.map_err(|error| Error::FailedValidation { error })?;
let mut set: HashSet<String> = HashSet::from_iter(info.users.iter().cloned());
set.insert(user.id.clone());
if set.len() > *MAX_GROUP_SIZE {
Err(Error::GroupTooLarge {
max: *MAX_GROUP_SIZE,
})?
}
for target in &set {
match get_relationship(&user, target) {
RelationshipStatus::Friend | RelationshipStatus::User => {}
_ => {
return Err(Error::NotFriends);
}
}
}
let id = Ulid::new().to_string();
let channel = Channel::Group {
id,
name: info.name,
description: info.description,
owner: user.id,
recipients: set.into_iter().collect::<Vec<String>>(),
icon: None,
last_message_id: None,
permissions: None,
nsfw: info.nsfw.unwrap_or_default()
};
channel.clone().publish().await?;
Ok(json!(channel))
pub async fn req(/*_idempotency: IdempotencyKey, user: User,*/ info: Json<Data>) -> Result<Value> {
todo!();
}

View File

@@ -1,66 +1,8 @@
use crate::util::result::{Error, Result, EmptyResponse};
use crate::{database::*, notifications::events::ClientboundNotification};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
#[delete("/<target>/recipients/<member>")]
pub async fn req(user: User, target: Ref, member: Ref) -> Result<EmptyResponse> {
if &user.id == &member.id {
Err(Error::CannotRemoveYourself)?
}
let channel = target.fetch_channel().await?;
if let Channel::Group {
id,
owner,
recipients,
..
} = &channel
{
if &user.id != owner {
// figure out if we want to use perm system here
Err(Error::MissingPermission)?
}
if recipients.iter().find(|x| *x == &member.id).is_none() {
Err(Error::NotInGroup)?
}
get_collection("channels")
.update_one(
doc! {
"_id": &id
},
doc! {
"$pull": {
"recipients": &member.id
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channel",
})?;
ClientboundNotification::ChannelGroupLeave {
id: id.clone(),
user: member.id.clone(),
}
.publish(id.clone());
Content::SystemMessage(SystemMessage::UserRemove {
id: member.id,
by: user.id,
})
.send_as_system(&channel)
.await
.ok();
Ok(EmptyResponse {})
} else {
Err(Error::InvalidOperation)
}
pub async fn req(/*user: UserRef, target: Ref, member: Ref*/ target: String, member: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,8 +1,6 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::Result;
use mongodb::bson::doc;
use nanoid::nanoid;
use rocket::serde::json::Value;
lazy_static! {
@@ -14,35 +12,6 @@ lazy_static! {
}
#[post("/<target>/invites")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
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),
}
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,23 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::Result;
use rocket::serde::json::Value;
#[get("/<target>/members")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let target = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
if let Channel::Group { recipients, .. } = target {
Ok(json!(user.fetch_multiple_users(&recipients).await?))
} else {
Err(Error::InvalidOperation)
}
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,29 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
#[delete("/<target>/messages/<msg>")]
pub async fn req(user: User, target: Ref, msg: Ref) -> Result<EmptyResponse> {
let channel = target.fetch_channel().await?;
channel.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel)
.for_channel()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
let message = msg.fetch_message(&channel).await?;
if message.author != user.id && !perm.get_manage_messages() {
match channel {
Channel::SavedMessages { .. } => unreachable!(),
_ => Err(Error::CannotEditMessage)?,
}
}
message.delete().await?;
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref, msg: Ref*/ target: String, msg: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,6 +1,4 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use crate::routes::channels::message_send::SendableEmbed;
use revolt_quark::{EmptyResponse, Result};
use chrono::Utc;
use mongodb::bson::{Bson, Document, doc, to_document};
@@ -12,95 +10,11 @@ use validator::Validate;
pub struct Data {
#[validate(length(min = 1, max = 2000))]
content: Option<String>,
#[validate(length(min = 0, max = 10))]
embeds: Option<Vec<SendableEmbed>>
// #[validate(length(min = 0, max = 10))]
// embeds: Option<Vec<SendableEmbed>>
}
#[patch("/<target>/messages/<msg>", data = "<edit>")]
pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<EmptyResponse> {
edit.validate()
.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()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
let mut message = msg.fetch_message(&channel).await?;
if message.author != user.id {
Err(Error::CannotEditMessage)?
}
let edited = Utc::now();
let mut set = doc! { "edited": Bson::DateTime(edited) };
let mut unset = doc! {};
let mut update = json!({ "edited": Bson::DateTime(edited) });
if let Some(new_content) = &edit.content {
set.insert("content", new_content.clone());
update.as_object_mut().unwrap().insert("content".to_string(), json!(new_content.clone()));
message.content = Content::Text(new_content.clone());
}
let mut new_embeds: Vec<Embed> = vec![];
if let Some(embeds) = &message.embeds {
for embed in embeds {
match embed {
Embed::Text(embed) => new_embeds.push(Embed::Text(embed.clone())),
_ => {}
}
}
}
if let Some(edited_embeds) = &edit.embeds {
new_embeds.clear();
for embed in edited_embeds {
new_embeds.push(embed.clone().into_embed(message.id.clone()).await?);
}
}
if new_embeds.len() > 0 {
let embed_docs: Vec<Document> = new_embeds
.clone()
.into_iter()
.map(|embed| to_document(&embed).unwrap())
.collect();
let obj = update.as_object_mut().unwrap();
obj.insert("embeds".to_string(), json!(embed_docs));
set.insert("embeds", embed_docs);
message.embeds = Some(new_embeds)
} else if edit.embeds.is_some() {
let obj = update.as_object_mut().unwrap();
obj.insert("embeds".to_string(), json!([]));
unset.insert("embeds", 1 as u32);
}
get_collection("messages")
.update_one(
doc! {
"_id": &message.id
},
doc! {
"$set": set,
"$unset": unset
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "message",
})?;
message.publish_update(update).await?;
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref, msg: Ref,*/ target: String, msg: String, edit: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,21 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::Result;
use rocket::serde::json::Value;
#[get("/<target>/messages/<msg>")]
pub async fn req(user: User, target: Ref, msg: Ref) -> Result<Value> {
let channel = target.fetch_channel().await?;
channel.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel)
.for_channel()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
let message = msg.fetch_message(&channel).await?;
Ok(json!(message))
pub async fn req(/*user: UserRef, target: Ref, msg: Ref*/ target: String, msg: String) -> Result<Value> {
todo!()
}

View File

@@ -1,7 +1,6 @@
use std::collections::HashSet;
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::{StreamExt, try_join};
use mongodb::{
@@ -36,151 +35,6 @@ pub struct Options {
}
#[get("/<target>/messages?<options..>")]
pub async fn req(user: User, target: Ref, options: Options) -> Result<Value> {
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 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(),
)
)
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "messages",
})?;
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",
})?,
);
}
}
}
if options.include_users.unwrap_or_else(|| false) {
let mut ids = HashSet::new();
for message in &messages {
message.add_associated_user_ids(&mut ids);
}
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_with_ids(&server, &user_ids).await?
}))
} else {
Ok(json!({
"messages": messages,
"users": users,
}))
}
} else {
Ok(json!(messages))
}
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, options: Options) -> Result<Value> {
todo!()
}

View File

@@ -1,5 +1,4 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::StreamExt;
use mongodb::bson::{doc, from_document};
@@ -12,63 +11,6 @@ pub struct Options {
}
#[post("/<target>/messages/stale", data = "<data>")]
pub async fn req(user: User, target: Ref, data: Json<Options>) -> Result<Value> {
if data.ids.len() > 150 {
return Err(Error::TooManyIds);
}
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 cursor = get_collection("messages")
.find(
doc! {
"_id": {
"$in": &data.ids
},
"channel": target.id()
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "messages",
})?;
let mut updated = vec![];
let mut found_ids = vec![];
while let Some(result) = cursor.next().await {
if let Ok(doc) = result {
let msg = from_document::<Message>(doc).map_err(|_| Error::DatabaseError {
operation: "from_document",
with: "message",
})?;
found_ids.push(msg.id.clone());
if msg.edited.is_some() {
updated.push(msg);
}
}
}
let mut deleted = vec![];
for id in &data.ids {
if found_ids.iter().find(|x| *x == id).is_none() {
deleted.push(id);
}
}
Ok(json!({
"updated": updated,
"deleted": deleted
}))
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, data: Json<Options>) -> Result<Value> {
todo!()
}

View File

@@ -1,7 +1,4 @@
use std::collections::HashSet;
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::Result;
use futures::StreamExt;
use mongodb::{
@@ -42,121 +39,6 @@ pub struct Options {
}
#[post("/<target>/search", data = "<options>")]
pub async fn req(user: User, target: Ref, options: Json<Options>) -> Result<Value> {
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 as i32
},
Sort::Oldest => doc! {
"_id": 1 as i32
}
}
)
.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 {
message.add_associated_user_ids(&mut ids);
}
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_with_ids(&server, &user_ids).await?
}))
} else {
Ok(json!({
"messages": messages,
"users": users,
}))
}
} else {
Ok(json!(messages))
}
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, options: Json<Options>) -> Result<Value> {
todo!()
}

View File

@@ -1,15 +1,9 @@
use std::collections::HashSet;
use crate::database::*;
use crate::util::idempotency::IdempotencyKey;
use crate::util::ratelimit::{Ratelimiter, RatelimitResponse};
use crate::util::result::{Error, Result};
use revolt_quark::{Result, models::message::Masquerade};
use mongodb::bson::doc;
use regex::Regex;
use rocket::serde::json::{Json, Value};
use serde::{Deserialize, Serialize};
use ulid::Ulid;
use validator::Validate;
#[derive(Serialize, Deserialize)]
@@ -29,24 +23,6 @@ pub struct SendableEmbed {
media: Option<String>,
colour: Option<String>,
}
impl SendableEmbed {
pub async fn into_embed(self, message_id: String) -> Result<Embed> {
let media = if let Some(id) = self.media {
Some(File::find_and_use(&id, "attachments", "message", &message_id).await?)
} else { None };
Ok(Embed::Text(Text {
icon_url: self.icon_url,
url: self.url,
title: self.title,
description: self.description,
media,
colour: self.colour
}))
}
}
#[derive(Validate, Serialize, Deserialize)]
pub struct Data {
#[validate(length(min = 0, max = 2000))]
@@ -55,7 +31,7 @@ pub struct Data {
attachments: Option<Vec<String>>,
nonce: Option<String>,
replies: Option<Vec<Reply>>,
#[validate]
//#[validate]
masquerade: Option<Masquerade>,
#[validate(length(min = 1, max = 10))]
embeds: Option<Vec<SendableEmbed>>
@@ -67,115 +43,6 @@ lazy_static! {
}
#[post("/<target>/messages", data = "<message>")]
pub async fn message_send(user: User, _r: Ratelimiter, mut idempotency: IdempotencyKey, target: Ref, message: Json<Data>) -> Result<RatelimitResponse<Value>> {
let message = message.into_inner();
idempotency.consume_nonce(message.nonce.clone());
message
.validate()
.map_err(|error| Error::FailedValidation { error })?;
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() {
return Err(Error::MissingPermission)
}
let mut mentions = HashSet::new();
for capture in RE_MENTION.captures_iter(&message.content) {
if let Some(mention) = capture.get(1) {
mentions.insert(mention.as_str().to_string());
}
}
if let Some(_) = &message.masquerade {
if !perm.get_masquerade() {
return Err(Error::MissingPermission)
}
}
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 id = Ulid::new().to_string();
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 mut embeds = vec![];
if let Some(sendable_embeds) = message.embeds {
for sendable_embed in sendable_embeds {
embeds.push(sendable_embed.into_embed(id.clone()).await?)
}
}
let msg = Message {
id,
channel: target.id().to_string(),
author: user.id,
content: Content::Text(message.content.clone()),
nonce: Some(idempotency.key),
edited: None,
embeds: if embeds.len() > 0 { Some(embeds) } else { 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
},
masquerade: message.masquerade
};
msg.clone().publish(&target, perm.get_embed_links()).await?;
Ok(RatelimitResponse(json!(msg)))
pub async fn message_send(/*user: UserRef, _r: Ratelimiter, mut idempotency: IdempotencyKey, target: Ref,*/ target: String, message: Json<Data>) -> /*Result<RatelimitResponse<Value>>*/ Result<Value> {
todo!()
}

View File

@@ -3,9 +3,7 @@ use rocket::serde::json::Json;
use serde::{Serialize, Deserialize};
use validator::Contains;
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use crate::notifications::events::ClientboundNotification;
use revolt_quark::{EmptyResponse, Result};
#[derive(Serialize, Deserialize)]
pub struct Data {
@@ -13,56 +11,6 @@ pub struct Data {
}
#[put("/<target>/permissions/<role>", data = "<data>", rank = 2)]
pub async fn req(user: User, target: Ref, role: String, data: Json<Data>) -> Result<EmptyResponse> {
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 = 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(EmptyResponse {})
}
_ => Err(Error::InvalidOperation)
}
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, role: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -2,10 +2,7 @@ use mongodb::bson::doc;
use rocket::serde::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, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
#[derive(Serialize, Deserialize)]
pub struct Data {
@@ -13,85 +10,6 @@ pub struct Data {
}
#[put("/<target>/permissions/default", data = "<data>", rank = 1)]
pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<EmptyResponse> {
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(EmptyResponse {})
} 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 = 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(EmptyResponse {})
}
_ => Err(Error::InvalidOperation)
}
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,6 +1,4 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use crate::util::variables::{USE_VOSO, VOSO_MANAGE_TOKEN, VOSO_URL};
use revolt_quark::{Error, Result};
use rocket::serde::json::Value;
use serde::{Deserialize, Serialize};
@@ -11,81 +9,6 @@ struct CreateUserResponse {
}
#[post("/<target>/join_call")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
if !*USE_VOSO {
return Err(Error::VosoUnavailable);
}
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()
.await?;
if !perm.get_voice_call() {
return Err(Error::MissingPermission);
}
// To join a call:
// - Check if the room exists.
// - If not, create it.
let client = reqwest::Client::new();
let result = client
.get(&format!("{}/room/{}", *VOSO_URL, target.id()))
.header(
reqwest::header::AUTHORIZATION,
VOSO_MANAGE_TOKEN.to_string(),
)
.send()
.await;
match result {
Err(_) => return Err(Error::VosoUnavailable),
Ok(result) => match result.status() {
reqwest::StatusCode::OK => (),
reqwest::StatusCode::NOT_FOUND => {
if let Err(_) = client
.post(&format!("{}/room/{}", *VOSO_URL, target.id()))
.header(
reqwest::header::AUTHORIZATION,
VOSO_MANAGE_TOKEN.to_string(),
)
.send()
.await
{
return Err(Error::VosoUnavailable);
}
}
_ => return Err(Error::VosoUnavailable),
},
}
// Then create a user for the room.
if let Ok(response) = client
.post(&format!(
"{}/room/{}/user/{}",
*VOSO_URL,
target.id(),
user.id
))
.header(
reqwest::header::AUTHORIZATION,
VOSO_MANAGE_TOKEN.to_string(),
)
.send()
.await
{
let res: CreateUserResponse = response.json().await.map_err(|_| Error::InvalidOperation)?;
Ok(json!(res))
} else {
Err(Error::VosoUnavailable)
}
pub async fn req(/*user: UserRef, target: Ref,*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,30 +1,6 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
#[delete("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<EmptyResponse> {
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!(),
}
}
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,5 +1,5 @@
use crate::database::*;
use crate::util::result::Result;
use revolt_quark::Result;
use revolt_quark::models::File;
use rocket::serde::json::Value;
use serde::Serialize;
@@ -26,37 +26,6 @@ pub enum InviteResponse {
}
#[get("/<target>")]
pub async fn req(target: Ref) -> Result<Value> {
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!(),
}
pub async fn req(/*target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,42 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use crate::util::variables::MAX_SERVER_COUNT;
use revolt_quark::{Error, Result};
use rocket::serde::json::Value;
#[post("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
if !User::can_acquire_server(&user.id).await? {
Err(Error::TooManyServers {
max: *MAX_SERVER_COUNT,
})?
}
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!(),
}
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,5 +1,4 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
use crate::util::regex::RE_USERNAME;
use mongodb::bson::doc;
@@ -15,31 +14,6 @@ pub struct Data {
}
#[post("/complete", data = "<data>")]
pub async fn req(session: Session, user: Option<User>, data: Json<Data>) -> Result<EmptyResponse> {
if user.is_some() {
Err(Error::AlreadyOnboarded)?
}
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
if User::is_username_taken(&data.username).await? {
return Err(Error::UsernameTaken);
}
get_collection("users")
.insert_one(
doc! {
"_id": session.user_id,
"username": &data.username
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "insert_one",
with: "user",
})?;
Ok(EmptyResponse {})
pub async fn req(/*session: Session, user: Option<User>,*/ data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,11 +1,7 @@
use crate::database::*;
use rauth::entities::Session;
use rocket::serde::json::Value;
#[get("/hello")]
pub async fn req(_session: Session, user: Option<User>) -> Value {
json!({
"onboarding": user.is_none()
})
pub async fn req(/*_session: Session, user: Option<User>*/) -> Value {
todo!()
}

View File

@@ -1,19 +1,10 @@
use crate::database::*;
use crate::util::result::{EmptyResponse, Error, Result};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
use rauth::entities::{Model, Session, WebPushSubscription};
use rocket::serde::json::Json;
#[post("/subscribe", data = "<data>")]
pub async fn req(mut session: Session, data: Json<WebPushSubscription>) -> Result<EmptyResponse> {
session.subscription = Some(data.into_inner());
session
.save(&get_db(), None)
.await
.map(|_| EmptyResponse)
.map_err(|_| Error::DatabaseError {
operation: "save",
with: "session",
})
#[post("/subscribe"/*, data = "<data>"*/)]
pub async fn req(/*mut session: Session, data: Json<WebPushSubscription>*/) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,18 +1,9 @@
use crate::database::*;
use crate::util::result::{EmptyResponse, Error, Result};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
use rauth::entities::{Model, Session};
#[post("/unsubscribe")]
pub async fn req(mut session: Session) -> Result<EmptyResponse> {
session.subscription = None;
session
.save(&get_db(), None)
.await
.map(|_| EmptyResponse)
.map_err(|_| Error::DatabaseError {
operation: "save",
with: "session",
})
pub async fn req(/*mut session: Session*/) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,14 +1,15 @@
use crate::util::{ratelimit::Ratelimiter, variables::{
/*use crate::util::{ratelimit::Ratelimiter, variables::{
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;
use rocket::{http::Status, serde::json::Value};
#[get("/")]
pub async fn root() -> Value {
json!({
todo!();
/*json!({
"revolt": crate::version::VERSION,
"features": {
"captcha": {
@@ -34,10 +35,10 @@ pub async fn root() -> Value {
"ws": *EXTERNAL_WS_URL,
"app": *APP_URL,
"vapid": *VAPID_PUBLIC_KEY
})
})*/
}
#[get("/ping")]
pub async fn ping(_limitguard: Ratelimiter) -> Status {
pub async fn ping(/*_limitguard: Ratelimiter*/) -> Status {
Status::Ok
}

View File

@@ -1,5 +1,4 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
use rocket::serde::json::Json;
@@ -13,50 +12,6 @@ pub struct Data {
}
#[put("/<server>/bans/<target>", data = "<data>")]
pub async fn req(user: User, server: Ref, target: Ref, data: Json<Data>) -> Result<EmptyResponse> {
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?;
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, server: Ref, target: Ref,*/ server: String, target: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,5 +1,5 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use revolt_quark::models::File;
use futures::StreamExt;
use mongodb::options::FindOptions;
@@ -15,73 +15,6 @@ struct BannedUser {
}
#[get("/<target>/bans")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_ban_members() {
return Err(Error::MissingPermission);
}
let mut cursor = get_collection("server_bans")
.find(
doc! {
"_id.server": target.id
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "server_bans",
})?;
let mut bans = vec![];
let mut user_ids = vec![];
while let Some(result) = cursor.next().await {
if let Ok(doc) = result {
if let Ok(ban) = from_document::<Ban>(doc) {
user_ids.push(ban.id.user.clone());
bans.push(ban);
}
}
}
let mut cursor = get_collection("users")
.find(
doc! {
"_id": {
"$in": user_ids
}
},
FindOptions::builder()
.projection(doc! {
"username": 1,
"avatar": 1
})
.build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "users",
})?;
let mut users = vec![];
while let Some(result) = cursor.next().await {
if let Ok(doc) = result {
if let Ok(user) = from_document::<BannedUser>(doc) {
users.push(user);
}
}
}
Ok(json!({
"users": users,
"bans": bans
}))
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,43 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
#[delete("/<server>/bans/<target>")]
pub async fn req(user: User, server: Ref, target: Ref) -> Result<EmptyResponse> {
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)?
}
if target.id == user.id {
return Err(Error::InvalidOperation);
}
if target.id == server.owner {
return Err(Error::MissingPermission);
}
let target = target.fetch_ban(&server.id).await?;
get_collection("server_bans")
.delete_one(
doc! {
"_id.server": &server.id,
"_id.user": &target.id.user
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "delete_one",
with: "server_ban",
})?;
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, server: Ref, target: Ref*/ server: String, target: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,8 +1,6 @@
use std::collections::HashMap;
use crate::database::*;
use crate::util::idempotency::IdempotencyKey;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use mongodb::bson::doc;
use rocket::serde::json::{Json, Value};
@@ -35,70 +33,6 @@ pub struct Data {
}
#[post("/<target>/channels", data = "<info>")]
pub async fn req(_idempotency: IdempotencyKey, user: User, target: Ref, info: Json<Data>) -> Result<Value> {
let info = info.into_inner();
info.validate()
.map_err(|error| Error::FailedValidation { error })?;
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_manage_channels() {
Err(Error::MissingPermission)?
}
let id = Ulid::new().to_string();
let channel = match info.channel_type {
ChannelType::Text => Channel::TextChannel {
id: id.clone(),
server: target.id.clone(),
name: info.name,
description: info.description,
icon: None,
last_message_id: None,
default_permissions: None,
role_permissions: HashMap::new(),
nsfw: info.nsfw.unwrap_or_default(),
},
ChannelType::Voice => Channel::VoiceChannel {
id: id.clone(),
server: target.id.clone(),
name: info.name,
description: info.description,
icon: None,
default_permissions: None,
role_permissions: HashMap::new(),
nsfw: info.nsfw.unwrap_or_default()
}
};
channel.clone().publish().await?;
get_collection("servers")
.update_one(
doc! {
"_id": target.id
},
doc! {
"$addToSet": {
"channels": id
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "server",
})?;
Ok(json!(channel))
pub async fn req(/*_idempotency: IdempotencyKey, user: User, target: Ref,*/ target: String, info: Json<Data>) -> Result<Value> {
todo!()
}

View File

@@ -1,5 +1,4 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::StreamExt;
use mongodb::bson::{doc, from_document};
@@ -15,39 +14,6 @@ pub struct ServerInvite {
}
#[get("/<target>/invites")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_manage_server() {
Err(Error::MissingPermission)?
}
let mut cursor = get_collection("channel_invites")
.find(
doc! {
"server": target.id
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "channel_invites",
})?;
let mut invites = vec![];
while let Some(result) = cursor.next().await {
if let Ok(doc) = result {
if let Ok(invite) = from_document::<Invite>(doc) {
invites.push(invite);
}
}
}
Ok(json!(invites))
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,10 +1,5 @@
use std::collections::HashSet;
use revolt_quark::{EmptyResponse, Result, models::server_member::FieldsMember};
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use crate::{database::*, notifications::events::RemoveMemberField};
use mongodb::bson::{doc, to_document};
use rocket::serde::json::Json;
use serde::{Deserialize, Serialize};
use validator::Validate;
@@ -15,144 +10,10 @@ pub struct Data {
nickname: Option<String>,
avatar: Option<String>,
roles: Option<Vec<String>>,
remove: Option<RemoveMemberField>,
remove: Option<FieldsMember>,
}
#[patch("/<server>/members/<target>", data = "<data>")]
pub async fn req(user: User, server: Ref, target: String, data: Json<Data>) -> Result<EmptyResponse> {
let data = data.into_inner();
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
if data.nickname.is_none() && data.avatar.is_none() && data.roles.is_none() && data.remove.is_none() {
return Ok(EmptyResponse {});
}
let server = server.fetch_server().await?;
let target = Ref::from(target)?.fetch_member(&server.id).await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&server)
.for_server()
.await?;
if data.roles.is_some() && !perm.get_manage_roles() {
return Err(Error::MissingPermission);
}
if target.id.user == user.id {
if (data.nickname.is_some() && !perm.get_change_nickname())
|| (data.avatar.is_some() && !perm.get_change_avatar())
{
return Err(Error::MissingPermission);
}
if let Some(remove) = &data.remove {
if match remove {
RemoveMemberField::Avatar => !perm.get_change_avatar(),
RemoveMemberField::Nickname => !perm.get_change_nickname(),
} {
return Err(Error::MissingPermission);
}
}
} else {
if data.avatar.is_some() || (data.nickname.is_some() && !perm.get_manage_nicknames()) {
return Err(Error::MissingPermission);
}
if let Some(remove) = &data.remove {
if match remove {
RemoveMemberField::Avatar => !perm.get_remove_avatars(),
RemoveMemberField::Nickname => !perm.get_manage_nicknames(),
} {
return Err(Error::MissingPermission);
}
}
}
let mut set = doc! {};
let mut unset = doc! {};
let mut remove_avatar = false;
if let Some(remove) = &data.remove {
match remove {
RemoveMemberField::Avatar => {
unset.insert("avatar", 1);
remove_avatar = true;
}
RemoveMemberField::Nickname => {
unset.insert("nickname", 1);
}
}
}
if let Some(name) = &data.nickname {
set.insert("nickname", name);
}
if let Some(attachment_id) = &data.avatar {
let attachment =
File::find_and_use(&attachment_id, "avatars", "user", &target.id.user).await?;
set.insert(
"avatar",
to_document(&attachment).map_err(|_| Error::DatabaseError {
operation: "to_document",
with: "attachment",
})?,
);
remove_avatar = true;
}
if let Some(role_ids) = &data.roles {
let mut ids = HashSet::new();
for role in role_ids {
if server.roles.contains_key(role) {
ids.insert(role.clone());
}
}
set.insert("roles", ids.into_iter().collect::<Vec<String>>());
}
let mut operations = doc! {};
if set.len() > 0 {
operations.insert("$set", &set);
}
if unset.len() > 0 {
operations.insert("$unset", unset);
}
if operations.len() > 0 {
get_collection("server_members")
.update_one(
doc! { "_id.server": &server.id, "_id.user": &target.id.user },
operations,
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "server_member",
})?;
}
ClientboundNotification::ServerMemberUpdate {
id: target.id.clone(),
data: json!(set),
clear: data.remove,
}
.publish(server.id.clone());
let Member { avatar, .. } = target;
if remove_avatar {
if let Some(old_avatar) = avatar {
old_avatar.delete().await?;
}
}
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, server: Ref,*/ server: String, target: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,21 +1,9 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use mongodb::bson::doc;
use rocket::serde::json::Value;
#[get("/<target>/members/<member>")]
pub async fn req(user: User, target: Ref, member: String) -> Result<Value> {
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
Ok(json!(Ref::from(member)?.fetch_member(&target.id).await?))
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, member: String) -> Result<Value> {
todo!()
}

View File

@@ -1,51 +1,9 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::StreamExt;
use mongodb::bson::{doc, from_document, Document};
use rocket::serde::json::Value;
// ! FIXME: this is a temporary route while permissions are being worked on.
#[get("/<target>/members")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
let members = get_collection("server_members")
.find(
doc! {
"_id.server": target.id
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "server_members",
})?
.filter_map(async move |s| s.ok())
.collect::<Vec<Document>>()
.await
.into_iter()
.filter_map(|x| from_document(x).ok())
.collect::<Vec<Member>>();
let member_ids = members
.iter()
.map(|m| m.id.user.clone())
.collect::<Vec<String>>();
Ok(json!({
"members": members,
"users": user.fetch_multiple_users(&member_ids).await?
}))
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,33 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
#[delete("/<target>/members/<member>")]
pub async fn req(user: User, target: Ref, member: String) -> Result<EmptyResponse> {
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_kick_members() {
return Err(Error::MissingPermission);
}
let member = Ref::from(member)?.fetch_member(&target.id).await?;
if member.id.user == user.id {
return Err(Error::InvalidOperation);
}
if member.id.user == target.owner {
return Err(Error::MissingPermission);
}
target
.remove_member(&member.id.user, RemoveMember::Kick)
.await?;
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, member: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -2,9 +2,7 @@ use mongodb::bson::doc;
use rocket::serde::json::Json;
use serde::{Serialize, Deserialize};
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
#[derive(Serialize, Deserialize)]
pub struct Values {
@@ -18,56 +16,6 @@ pub struct Data {
}
#[put("/<target>/permissions/<role_id>", data = "<data>", rank = 2)]
pub async fn req(user: User, target: Ref, role_id: String, data: Json<Data>) -> Result<EmptyResponse> {
let target = target.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.contains_key(&role_id) {
return Err(Error::NotFound);
}
let server_permissions: u32 = data.permissions.server;
let channel_permissions: u32 = data.permissions.channel;
get_collection("servers")
.update_one(
doc! { "_id": &target.id },
doc! {
"$set": {
"roles.".to_owned() + &role_id + &".permissions": [
server_permissions as i32,
channel_permissions as i32
]
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "server"
})?;
ClientboundNotification::ServerRoleUpdate {
id: target.id.clone(),
role_id,
data: json!({
"permissions": [
server_permissions as i32,
channel_permissions as i32
]
}),
clear: None
}
.publish(target.id);
Ok(EmptyResponse)
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, role_id: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -2,9 +2,7 @@ use mongodb::bson::doc;
use rocket::serde::json::Json;
use serde::{Serialize, Deserialize};
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
#[derive(Serialize, Deserialize)]
pub struct Values {
@@ -18,51 +16,6 @@ pub struct Data {
}
#[put("/<target>/permissions/default", data = "<data>", rank = 1)]
pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<EmptyResponse> {
let target = target.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 server_permissions: u32 = data.permissions.server;
let channel_permissions: u32 = data.permissions.channel;
get_collection("servers")
.update_one(
doc! { "_id": &target.id },
doc! {
"$set": {
"default_permissions": [
server_permissions as i32,
channel_permissions as i32
]
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "server"
})?;
ClientboundNotification::ServerUpdate {
id: target.id.clone(),
data: json!({
"default_permissions": [
server_permissions as i32,
channel_permissions as i32
]
}),
clear: None
}
.publish(target.id);
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,6 +1,4 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use ulid::Ulid;
use mongodb::bson::doc;
@@ -15,62 +13,6 @@ pub struct Data {
}
#[post("/<target>/roles", data = "<data>")]
pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<Value> {
let data = data.into_inner();
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_manage_roles() {
Err(Error::MissingPermission)?
}
let id = Ulid::new().to_string();
let perm_tuple = (
*permissions::server::DEFAULT_PERMISSION as i32,
*permissions::channel::DEFAULT_PERMISSION_SERVER as i32
);
get_collection("servers")
.update_one(
doc! {
"_id": &target.id
},
doc! {
"$set": {
"roles.".to_owned() + &id: {
"name": &data.name,
"permissions": [
&perm_tuple.0,
&perm_tuple.1
]
}
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "servers"
})?;
ClientboundNotification::ServerRoleUpdate {
id: target.id.clone(),
role_id: id.clone(),
data: json!({
"name": data.name,
"permissions": &perm_tuple
}),
clear: None
}
.publish(target.id);
Ok(json!({ "id": id, "permissions": perm_tuple }))
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, data: Json<Data>) -> Result<Value> {
todo!()
}

View File

@@ -1,81 +1,6 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use mongodb::bson::doc;
use revolt_quark::{EmptyResponse, Result};
#[delete("/<target>/roles/<role_id>")]
pub async fn req(user: User, target: Ref, role_id: String) -> Result<EmptyResponse> {
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_manage_roles() {
Err(Error::MissingPermission)?
}
get_collection("servers")
.update_one(
doc! {
"_id": &target.id
},
doc! {
"$unset": {
"roles.".to_owned() + &role_id: 1
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "servers"
})?;
get_collection("channels")
.update_one(
doc! {
"server": &target.id
},
doc! {
"$unset": {
"role_permissions.".to_owned() + &role_id: 1
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channels"
})?;
get_collection("server_members")
.update_many(
doc! {
"_id.server": &target.id
},
doc! {
"$pull": {
"roles": &role_id
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_many",
with: "server_members"
})?;
ClientboundNotification::ServerRoleDelete {
id: target.id.clone(),
role_id
}
.publish(target.id);
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, role_id: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,6 +1,4 @@
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use crate::{database::*, notifications::events::RemoveRoleField};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
use rocket::serde::json::Json;
@@ -15,96 +13,10 @@ pub struct Data {
colour: Option<String>,
hoist: Option<bool>,
rank: Option<i64>,
remove: Option<RemoveRoleField>,
// remove: Option<FieldsRole>,
}
#[patch("/<target>/roles/<role_id>", data = "<data>")]
pub async fn req(user: User, target: Ref, role_id: String, data: Json<Data>) -> Result<EmptyResponse> {
let data = data.into_inner();
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
if data.name.is_none() && data.colour.is_none() && data.hoist.is_none() && data.rank.is_none() && data.remove.is_none()
{
return Ok(EmptyResponse {});
}
let target = target.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.contains_key(&role_id) {
return Err(Error::InvalidRole)
}
let mut set = doc! {};
let mut unset = doc! {};
// ! FIXME: we should probably just require clients to support basic MQL incl. $set / $unset
let mut set_update = doc! {};
let role_key = "roles.".to_owned() + &role_id;
if let Some(remove) = &data.remove {
match remove {
RemoveRoleField::Colour => {
unset.insert(role_key.clone() + ".colour", 1);
}
}
}
if let Some(name) = &data.name {
set.insert(role_key.clone() + ".name", name);
set_update.insert("name", name);
}
if let Some(colour) = &data.colour {
set.insert(role_key.clone() + ".colour", colour);
set_update.insert("colour", colour);
}
if let Some(hoist) = &data.hoist {
set.insert(role_key.clone() + ".hoist", hoist);
set_update.insert("hoist", hoist);
}
if let Some(rank) = &data.rank {
set.insert(role_key.clone() + ".rank", rank);
set_update.insert("rank", rank);
}
let mut operations = doc! {};
if set.len() > 0 {
operations.insert("$set", &set);
}
if unset.len() > 0 {
operations.insert("$unset", unset);
}
if operations.len() > 0 {
get_collection("servers")
.update_one(doc! { "_id": &target.id }, operations, None)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "server",
})?;
}
ClientboundNotification::ServerRoleUpdate {
id: target.id.clone(),
role_id,
data: json!(set_update),
clear: data.remove,
}
.publish(target.id.clone());
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, role_id: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,23 +1,6 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
#[put("/<target>/ack")]
pub async fn req(user: User, target: Ref) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
target.mark_as_read(&user.id).await?;
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,9 +1,6 @@
use std::collections::HashMap;
use crate::database::*;
use crate::util::idempotency::IdempotencyKey;
use crate::util::result::{Error, Result};
use crate::util::variables::MAX_SERVER_COUNT;
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
use rocket::serde::json::{Json, Value};
@@ -22,73 +19,6 @@ pub struct Data {
}
#[post("/create", data = "<info>")]
pub async fn req(_idempotency: IdempotencyKey, user: User, info: Json<Data>) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
if !User::can_acquire_server(&user.id).await? {
Err(Error::TooManyServers {
max: *MAX_SERVER_COUNT,
})?
}
let info = info.into_inner();
info.validate()
.map_err(|error| Error::FailedValidation { error })?;
let id = Ulid::new().to_string();
let cid = Ulid::new().to_string();
let server = Server {
id: id.clone(),
owner: user.id.clone(),
name: info.name,
description: info.description,
channels: vec![cid.clone()],
categories: None,
system_messages: Some(SystemMessageChannels {
user_joined: Some(cid.clone()),
user_left: Some(cid.clone()),
user_kicked: Some(cid.clone()),
user_banned: Some(cid.clone()),
}),
roles: HashMap::new(),
default_permissions: (
*permissions::server::DEFAULT_PERMISSION as i32,
*permissions::channel::DEFAULT_PERMISSION_SERVER as i32
),
icon: None,
banner: None,
flags: None,
nsfw: info.nsfw.unwrap_or_default(),
analytics: false,
discoverable: false,
};
Channel::TextChannel {
id: cid,
server: id,
name: "general".to_string(),
description: None,
icon: None,
last_message_id: None,
default_permissions: None,
role_permissions: HashMap::new(),
nsfw: false
}
.publish()
.await?;
server.clone().create().await?;
server.join_member(&user.id).await?;
Ok(json!(server))
pub async fn req(/*_idempotency: IdempotencyKey, user: User,*/ info: Json<Data>) -> Result<Value> {
todo!()
}

View File

@@ -1,25 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
use mongodb::bson::doc;
#[delete("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<EmptyResponse> {
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_view() {
return Err(Error::MissingPermission);
}
if user.id == target.owner {
target.delete().await?;
} else {
target.remove_member(&user.id, RemoveMember::Leave).await?;
}
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,8 +1,5 @@
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use crate::{database::*, notifications::events::RemoveServerField};
use revolt_quark::{EmptyResponse, Result, models::server::{FieldsServer, SystemMessageChannels, Category}};
use mongodb::bson::{doc, to_bson, to_document};
use rocket::serde::json::Json;
use serde::{Deserialize, Serialize};
use validator::Validate;
@@ -17,143 +14,12 @@ pub struct Data {
banner: Option<String>,
categories: Option<Vec<Category>>,
system_messages: Option<SystemMessageChannels>,
remove: Option<RemoveServerField>,
remove: Option<FieldsServer>,
nsfw: Option<bool>,
analytics: Option<bool>,
}
#[patch("/<target>", data = "<data>")]
pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<EmptyResponse> {
let data = data.into_inner();
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
if data.name.is_none() && data.description.is_none() && data.icon.is_none() && data.banner.is_none() && data.remove.is_none() && data.categories.is_none() && data.system_messages.is_none() && data.nsfw.is_none() && data.analytics.is_none()
{
return Ok(EmptyResponse {});
}
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_manage_server() {
Err(Error::MissingPermission)?
}
let mut set = doc! {};
let mut unset = doc! {};
let mut remove_icon = false;
let mut remove_banner = false;
if let Some(remove) = &data.remove {
match remove {
RemoveServerField::Icon => {
unset.insert("icon", 1);
remove_icon = true;
}
RemoveServerField::Banner => {
unset.insert("banner", 1);
remove_banner = true;
}
RemoveServerField::Description => {
unset.insert("description", 1);
}
}
}
if let Some(name) = &data.name {
set.insert("name", name);
}
if let Some(description) = &data.description {
set.insert("description", description);
}
if let Some(attachment_id) = &data.icon {
let attachment = File::find_and_use(&attachment_id, "icons", "object", &target.id).await?;
set.insert(
"icon",
to_document(&attachment).map_err(|_| Error::DatabaseError {
operation: "to_document",
with: "attachment",
})?,
);
remove_icon = true;
}
if let Some(attachment_id) = &data.banner {
let attachment =
File::find_and_use(&attachment_id, "banners", "server", &target.id).await?;
set.insert(
"banner",
to_document(&attachment).map_err(|_| Error::DatabaseError {
operation: "to_document",
with: "attachment",
})?,
);
remove_banner = true;
}
if let Some(categories) = &data.categories {
set.insert("categories", to_bson(&categories).map_err(|_| Error::DatabaseError { operation: "to_document", with: "categories" })?);
}
if let Some(system_messages) = &data.system_messages {
set.insert("system_messages", to_bson(&system_messages).map_err(|_| Error::DatabaseError { operation: "to_document", with: "system_messages" })?);
}
if let Some(nsfw) = &data.nsfw {
set.insert("nsfw", nsfw);
}
if let Some(analytics) = &data.analytics {
set.insert("analytics", analytics);
}
let mut operations = doc! {};
if set.len() > 0 {
operations.insert("$set", &set);
}
if unset.len() > 0 {
operations.insert("$unset", unset);
}
if operations.len() > 0 {
get_collection("servers")
.update_one(doc! { "_id": &target.id }, operations, None)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "server",
})?;
}
ClientboundNotification::ServerUpdate {
id: target.id.clone(),
data: json!(set),
clear: data.remove,
}
.publish(target.id.clone());
let Server { icon, banner, .. } = target;
if remove_icon {
if let Some(old_icon) = icon {
old_icon.delete().await?;
}
}
if remove_banner {
if let Some(old_banner) = banner {
old_banner.delete().await?;
}
}
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef, target: Ref,*/ target: String, data: Json<Data>) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,20 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use rocket::serde::json::Value;
#[get("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let target = target.fetch_server().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_server(&target)
.for_server()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
Ok(json!(target))
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,5 +1,4 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use mongodb::bson::doc;
use mongodb::options::FindOneOptions;
@@ -12,35 +11,6 @@ pub struct Options {
}
#[post("/settings/fetch", data = "<options>")]
pub async fn req(user: User, options: Json<Options>) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot);
}
let options = options.into_inner();
let mut projection = doc! {
"_id": 0,
};
for key in options.keys {
projection.insert(key, 1);
}
if let Some(doc) = get_collection("user_settings")
.find_one(
doc! {
"_id": user.id
},
FindOneOptions::builder().projection(projection).build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find_one",
with: "user_settings",
})?
{
Ok(json!(doc))
} else {
Ok(json!({}))
}
pub async fn req(/*user: UserRef,*/ options: Json<Options>) -> Result<Value> {
todo!()
}

View File

@@ -1,14 +1,9 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use mongodb::bson::doc;
use rocket::serde::json::Value;
#[get("/unreads")]
pub async fn req(user: User) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot);
}
Ok(json!(User::fetch_unreads(&user.id).await?))
pub async fn req(/*user: UserRef*/) -> Result<Value> {
todo!()
}

View File

@@ -1,6 +1,4 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{EmptyResponse, Error, Result};
use revolt_quark::{EmptyResponse, Result};
use chrono::prelude::*;
use mongodb::bson::{doc, to_bson};
@@ -18,57 +16,6 @@ pub struct Options {
}
#[post("/settings/set?<options..>", data = "<data>")]
pub async fn req(user: User, data: Json<Data>, options: Options) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(Error::IsBot);
}
let data = data.into_inner();
let current_time = Utc::now().timestamp_millis();
let timestamp = if let Some(timestamp) = options.timestamp {
if timestamp > current_time {
current_time
} else {
timestamp
}
} else {
current_time
};
let mut set = doc! {};
for (key, data) in &data {
set.insert(
key.clone(),
vec![
to_bson(&timestamp).unwrap(),
to_bson(&data.clone()).unwrap(),
],
);
}
if set.len() > 0 {
get_collection("user_settings")
.update_one(
doc! {
"_id": &user.id
},
doc! {
"$set": &set
},
UpdateOptions::builder().upsert(true).build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "user_settings",
})?;
}
ClientboundNotification::UserSettingsUpdate {
id: user.id.clone(),
update: json!(set),
}
.publish(user.id);
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef,*/ data: Json<Data>, options: Options) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,6 +1,4 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::try_join;
use mongodb::bson::doc;
@@ -8,161 +6,6 @@ use mongodb::options::{Collation, FindOneOptions};
use rocket::serde::json::Value;
#[put("/<username>/friend")]
pub async fn req(user: User, username: String) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let col = get_collection("users");
let doc = col
.find_one(
doc! {
"username": username
},
FindOneOptions::builder()
.collation(Collation::builder().locale("en").strength(2).build())
.build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find_one",
with: "user",
})?
.ok_or_else(|| Error::UnknownUser)?;
let target_id = doc.get_str("_id").map_err(|_| Error::DatabaseError {
operation: "get_str(_id)",
with: "user",
})?;
let target_user = Ref::from(target_id.to_string())?.fetch_user().await?;
if target_user.bot.is_some() {
return Err(Error::IsBot)
}
match get_relationship(&user, &target_id) {
RelationshipStatus::User => return Err(Error::NoEffect),
RelationshipStatus::Friend => return Err(Error::AlreadyFriends),
RelationshipStatus::Outgoing => return Err(Error::AlreadySentRequest),
RelationshipStatus::Blocked => return Err(Error::Blocked),
RelationshipStatus::BlockedOther => return Err(Error::BlockedByOther),
RelationshipStatus::Incoming => {
match try_join!(
col.update_one(
doc! {
"_id": &user.id,
"relations._id": target_id
},
doc! {
"$set": {
"relations.$.status": "Friend"
}
},
None
),
col.update_one(
doc! {
"_id": target_id,
"relations._id": &user.id
},
doc! {
"$set": {
"relations.$.status": "Friend"
}
},
None
)
) {
Ok(_) => {
let target_user = target_user
.from_override(&user, RelationshipStatus::Friend)
.await?;
let user = user
.from_override(&target_user, RelationshipStatus::Friend)
.await?;
ClientboundNotification::UserRelationship {
id: user.id.clone(),
user: target_user,
status: RelationshipStatus::Friend,
}
.publish(user.id.clone());
ClientboundNotification::UserRelationship {
id: target_id.to_string(),
user,
status: RelationshipStatus::Friend,
}
.publish(target_id.to_string());
Ok(json!({ "status": "Friend" }))
}
Err(_) => Err(Error::DatabaseError {
operation: "update_one",
with: "user",
}),
}
}
RelationshipStatus::None => {
match try_join!(
col.update_one(
doc! {
"_id": &user.id
},
doc! {
"$push": {
"relations": {
"_id": target_id,
"status": "Outgoing"
}
}
},
None
),
col.update_one(
doc! {
"_id": target_id
},
doc! {
"$push": {
"relations": {
"_id": &user.id,
"status": "Incoming"
}
}
},
None
)
) {
Ok(_) => {
let target_user = target_user
.from_override(&user, RelationshipStatus::Outgoing)
.await?;
let user = user
.from_override(&target_user, RelationshipStatus::Incoming)
.await?;
ClientboundNotification::UserRelationship {
id: user.id.clone(),
user: target_user,
status: RelationshipStatus::Outgoing,
}
.publish(user.id.clone());
ClientboundNotification::UserRelationship {
id: target_id.to_string(),
user,
status: RelationshipStatus::Incoming,
}
.publish(target_id.to_string());
Ok(json!({ "status": "Outgoing" }))
}
Err(_) => Err(Error::DatabaseError {
operation: "update_one",
with: "user",
}),
}
}
}
pub async fn req(/*user: UserRef,*/ username: String) -> Result<Value> {
todo!()
}

View File

@@ -1,171 +1,10 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::try_join;
use mongodb::bson::doc;
use rocket::serde::json::Value;
#[put("/<target>/block")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let col = get_collection("users");
let target = target.fetch_user().await?;
match get_relationship(&user, &target.id) {
RelationshipStatus::User | RelationshipStatus::Blocked => Err(Error::NoEffect),
RelationshipStatus::BlockedOther => {
col.update_one(
doc! {
"_id": &user.id,
"relations._id": &target.id
},
doc! {
"$set": {
"relations.$.status": "Blocked"
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "user",
})?;
ClientboundNotification::UserRelationship {
id: user.id.clone(),
user: target,
status: RelationshipStatus::Blocked,
}
.publish(user.id.clone());
Ok(json!({ "status": "Blocked" }))
}
RelationshipStatus::None => {
match try_join!(
col.update_one(
doc! {
"_id": &user.id
},
doc! {
"$push": {
"relations": {
"_id": &target.id,
"status": "Blocked"
}
}
},
None
),
col.update_one(
doc! {
"_id": &target.id
},
doc! {
"$push": {
"relations": {
"_id": &user.id,
"status": "BlockedOther"
}
}
},
None
)
) {
Ok(_) => {
let target = target
.from_override(&user, RelationshipStatus::Blocked)
.await?;
let user = user
.from_override(&target, RelationshipStatus::BlockedOther)
.await?;
let target_id = target.id.clone();
ClientboundNotification::UserRelationship {
id: user.id.clone(),
user: target,
status: RelationshipStatus::Blocked,
}
.publish(user.id.clone());
ClientboundNotification::UserRelationship {
id: target_id.clone(),
user,
status: RelationshipStatus::BlockedOther,
}
.publish(target_id);
Ok(json!({ "status": "Blocked" }))
}
Err(_) => Err(Error::DatabaseError {
operation: "update_one",
with: "user",
}),
}
}
RelationshipStatus::Friend
| RelationshipStatus::Incoming
| RelationshipStatus::Outgoing => {
match try_join!(
col.update_one(
doc! {
"_id": &user.id,
"relations._id": &target.id
},
doc! {
"$set": {
"relations.$.status": "Blocked"
}
},
None
),
col.update_one(
doc! {
"_id": &target.id,
"relations._id": &user.id
},
doc! {
"$set": {
"relations.$.status": "BlockedOther"
}
},
None
)
) {
Ok(_) => {
let target = target
.from_override(&user, RelationshipStatus::Blocked)
.await?;
let user = user
.from_override(&target, RelationshipStatus::BlockedOther)
.await?;
let target_id = target.id.clone();
ClientboundNotification::UserRelationship {
id: user.id.clone(),
user: target,
status: RelationshipStatus::Blocked,
}
.publish(user.id.clone());
ClientboundNotification::UserRelationship {
id: target_id.clone(),
user,
status: RelationshipStatus::BlockedOther,
}
.publish(target_id);
Ok(json!({ "status": "Blocked" }))
}
Err(_) => Err(Error::DatabaseError {
operation: "update_one",
with: "user",
}),
}
}
}
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,6 +1,4 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use revolt_quark::{EmptyResponse, Result};
use crate::util::regex::RE_USERNAME;
use mongodb::bson::doc;
use rauth::entities::Account;
@@ -19,45 +17,9 @@ pub struct Data {
#[patch("/<_ignore_id>/username", data = "<data>")]
pub async fn req(
account: Account,
user: User,
//user: UserRef,
data: Json<Data>,
_ignore_id: String,
) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
account.verify_password(&data.password)
.map_err(|_| Error::InvalidCredentials)?;
let mut set = doc! {};
if let Some(username) = &data.username {
if (username.to_lowercase() != user.username.to_lowercase()) && User::is_username_taken(&username).await? {
return Err(Error::UsernameTaken);
}
set.insert("username", username.clone());
}
get_collection("users")
.update_one(doc! { "_id": &user.id }, doc! { "$set": set }, None)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "user",
})?;
ClientboundNotification::UserUpdate {
id: user.id.clone(),
data: json!({
"username": data.username
}),
clear: None,
}
.publish_as_user(user.id.clone());
Ok(EmptyResponse {})
todo!()
}

View File

@@ -1,8 +1,7 @@
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result, EmptyResponse};
use crate::{database::*, notifications::events::RemoveUserField};
use revolt_quark::{EmptyResponse, Result, models::user::FieldsUser};
use mongodb::bson::{doc, to_document};
use revolt_quark::models::user::UserStatus;
use rocket::serde::json::Json;
use serde::{Deserialize, Serialize};
use validator::Validate;
@@ -19,138 +18,16 @@ pub struct UserProfileData {
#[derive(Validate, Serialize, Deserialize)]
pub struct Data {
#[validate]
// #[validate]
status: Option<UserStatus>,
#[validate]
profile: Option<UserProfileData>,
#[validate(length(min = 1, max = 128))]
avatar: Option<String>,
remove: Option<RemoveUserField>,
remove: Option<FieldsUser>,
}
#[patch("/<_ignore_id>", data = "<data>")]
pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<EmptyResponse> {
let mut data = data.into_inner();
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
if data.status.is_none()
&& data.profile.is_none()
&& data.avatar.is_none()
&& data.remove.is_none()
{
return Ok(EmptyResponse {});
}
let mut unset = doc! {};
let mut set = doc! {};
let mut remove_background = false;
let mut remove_avatar = false;
if let Some(remove) = &data.remove {
match remove {
RemoveUserField::ProfileContent => {
unset.insert("profile.content", 1);
}
RemoveUserField::ProfileBackground => {
unset.insert("profile.background", 1);
remove_background = true;
}
RemoveUserField::StatusText => {
unset.insert("status.text", 1);
}
RemoveUserField::Avatar => {
unset.insert("avatar", 1);
remove_avatar = true;
}
}
}
if let Some(status) = &data.status {
set.insert(
"status",
to_document(&status).map_err(|_| Error::DatabaseError {
operation: "to_document",
with: "status",
})?,
);
}
if let Some(profile) = data.profile {
if let Some(content) = profile.content {
set.insert("profile.content", content);
}
if let Some(attachment_id) = profile.background {
let attachment =
File::find_and_use(&attachment_id, "backgrounds", "user", &user.id).await?;
set.insert(
"profile.background",
to_document(&attachment).map_err(|_| Error::DatabaseError {
operation: "to_document",
with: "attachment",
})?,
);
remove_background = true;
}
}
let avatar = std::mem::replace(&mut data.avatar, None);
if let Some(attachment_id) = avatar {
let attachment = File::find_and_use(&attachment_id, "avatars", "user", &user.id).await?;
set.insert(
"avatar",
to_document(&attachment).map_err(|_| Error::DatabaseError {
operation: "to_document",
with: "attachment",
})?,
);
remove_avatar = true;
}
let mut operations = doc! {};
if set.len() > 0 {
operations.insert("$set", &set);
}
if unset.len() > 0 {
operations.insert("$unset", unset);
}
if operations.len() > 0 {
get_collection("users")
.update_one(doc! { "_id": &user.id }, operations, None)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "user",
})?;
}
ClientboundNotification::UserUpdate {
id: user.id.clone(),
data: json!(set),
clear: data.remove,
}
.publish_as_user(user.id.clone());
if remove_avatar {
if let Some(old_avatar) = user.avatar {
old_avatar.delete().await?;
}
}
if remove_background {
if let Some(profile) = user.profile {
if let Some(old_background) = profile.background {
old_background.delete().await?;
}
}
}
Ok(EmptyResponse {})
pub async fn req(/*user: UserRef,*/ data: Json<Data>, _ignore_id: String) -> Result<EmptyResponse> {
todo!()
}

View File

@@ -1,40 +1,9 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::StreamExt;
use mongodb::bson::doc;
use rocket::serde::json::Value;
#[get("/dms")]
pub async fn req(user: User) -> Result<Value> {
let mut cursor = get_collection("channels")
.find(
doc! {
"$or": [
{
"channel_type": "DirectMessage",
"active": true
},
{
"channel_type": "Group"
}
],
"recipients": user.id
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "channels",
})?;
let mut channels = vec![];
while let Some(result) = cursor.next().await {
if let Ok(doc) = result {
channels.push(doc);
}
}
Ok(json!(channels))
pub async fn req(/*user: UserRef*/) -> Result<Value> {
todo!()
}

View File

@@ -1,24 +1,9 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use mongodb::bson::doc;
use rocket::serde::json::Value;
#[get("/<target>/profile")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let target = target.fetch_user().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_user(&target)
.for_user_given()
.await?;
if !perm.get_view_profile() {
Err(Error::MissingPermission)?
}
if target.profile.is_some() {
Ok(json!(target.profile))
} else {
Ok(json!({}))
}
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,13 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use rocket::serde::json::Value;
#[get("/<target>/relationship")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
Ok(json!({ "status": get_relationship(&user, &target.id) }))
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,17 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use rocket::serde::json::Value;
#[get("/relationships")]
pub async fn req(user: User) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
Ok(if let Some(vec) = user.relations {
json!(vec)
} else {
json!([])
})
pub async fn req(/*user: UserRef*/) -> Result<Value> {
todo!()
}

View File

@@ -1,9 +1,8 @@
use crate::database::*;
use crate::util::result::{Result};
use revolt_quark::Result;
use rocket::serde::json::Value;
#[get("/@me")]
pub async fn req(user: User) -> Result<Value> {
Ok(json!(user))
pub async fn req(/*user: UserRef*/) -> Result<Value> {
todo!()
}

View File

@@ -1,20 +1,8 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use rocket::serde::json::Value;
#[get("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let target = target.fetch_user().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_user(&target)
.for_user_given()
.await?;
if !perm.get_access() {
Err(Error::MissingPermission)?
}
Ok(json!(target.from(&user).with(perm)))
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,5 +1,4 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::StreamExt;
use mongodb::bson::{doc, Document};
@@ -7,56 +6,6 @@ use mongodb::options::FindOptions;
use rocket::serde::json::Value;
#[get("/<target>/mutual")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let users = get_collection("users")
.find(
doc! {
"$and": [
{ "relations": { "$elemMatch": { "_id": &user.id, "status": "Friend" } } },
{ "relations": { "$elemMatch": { "_id": &target.id, "status": "Friend" } } }
]
},
FindOptions::builder().projection(doc! { "_id": 1 }).build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "users",
})?
.filter_map(async move |s| s.ok())
.collect::<Vec<Document>>()
.await
.into_iter()
.filter_map(|x| x.get_str("_id").ok().map(|x| x.to_string()))
.collect::<Vec<String>>();
let server_ids = User::fetch_server_ids(&user.id).await?;
let servers = get_collection("server_members")
.find(
doc! {
"_id.user": &target.id,
"_id.server": {
"$in": server_ids
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find_one",
with: "server_members",
})?
.filter_map(async move |s| s.ok())
.collect::<Vec<Document>>()
.await
.into_iter()
.filter_map(|x| {
x.get_document("_id")
.ok()
.map(|i| i.get_str("server").ok().map(|x| x.to_string()))
})
.flatten()
.collect::<Vec<String>>();
Ok(json!({ "users": users, "servers": servers }))
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -3,8 +3,6 @@ use rocket::response::{self, Responder};
use rocket::fs::NamedFile;
use std::path::Path;
use crate::database::Ref;
pub struct CachedFile(NamedFile);
pub static CACHE_CONTROL: &'static str = "public, max-age=31536000, immutable";
@@ -18,8 +16,8 @@ impl<'r> Responder<'r, 'static> for CachedFile {
}
#[get("/<target>/default_avatar")]
pub async fn req(target: Ref) -> Option<CachedFile> {
match target.id.chars().nth(25).unwrap() {
pub async fn req(target: String) -> Option<CachedFile> {
match target.chars().nth(25).unwrap() {
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' => {
NamedFile::open(Path::new("assets/user_red.png")).await.ok().map(|n| CachedFile(n))
}

View File

@@ -1,50 +1,10 @@
use crate::database::*;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use mongodb::bson::doc;
use rocket::serde::json::Value;
use ulid::Ulid;
#[get("/<target>/dm")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
let query = if user.id == target.id {
doc! {
"channel_type": "SavedMessages",
"user": &user.id
}
} else {
doc! {
"channel_type": "DirectMessage",
"recipients": {
"$all": [ &user.id, &target.id ]
}
}
};
let existing_channel = get_collection("channels")
.find_one(query, None)
.await
.map_err(|_| Error::DatabaseError {
operation: "find_one",
with: "channel",
})?;
if let Some(doc) = existing_channel {
Ok(json!(doc))
} else {
let id = Ulid::new().to_string();
let channel = if user.id == target.id {
Channel::SavedMessages { id, user: user.id }
} else {
Channel::DirectMessage {
id,
active: false,
recipients: vec![user.id, target.id],
last_message_id: None,
}
};
channel.clone().publish().await?;
Ok(json!(channel))
}
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,83 +1,10 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::try_join;
use mongodb::bson::doc;
use rocket::serde::json::Value;
#[delete("/<target>/friend")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let col = get_collection("users");
let target = target.fetch_user().await?;
match get_relationship(&user, &target.id) {
RelationshipStatus::Friend
| RelationshipStatus::Outgoing
| RelationshipStatus::Incoming => {
match try_join!(
col.update_one(
doc! {
"_id": &user.id
},
doc! {
"$pull": {
"relations": {
"_id": &target.id
}
}
},
None
),
col.update_one(
doc! {
"_id": &target.id
},
doc! {
"$pull": {
"relations": {
"_id": &user.id
}
}
},
None
)
) {
Ok(_) => {
let target = target
.from_override(&user, RelationshipStatus::None)
.await?;
let user = user
.from_override(&target, RelationshipStatus::None)
.await?;
let target_id = target.id.clone();
ClientboundNotification::UserRelationship {
id: user.id.clone(),
user: target,
status: RelationshipStatus::None,
}
.publish(user.id.clone());
ClientboundNotification::UserRelationship {
id: target_id.clone(),
user,
status: RelationshipStatus::None,
}
.publish(target_id);
Ok(json!({ "status": "None" }))
}
Err(_) => Err(Error::DatabaseError {
operation: "update_one",
with: "user",
}),
}
}
_ => Err(Error::NoEffect),
}
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}

View File

@@ -1,115 +1,10 @@
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
use revolt_quark::{Error, Result};
use futures::try_join;
use mongodb::bson::doc;
use rocket::serde::json::Value;
#[delete("/<target>/block")]
pub async fn req(user: User, target: Ref) -> Result<Value> {
if user.bot.is_some() {
return Err(Error::IsBot)
}
let col = get_collection("users");
let target = target.fetch_user().await?;
match get_relationship(&user, &target.id) {
RelationshipStatus::Blocked => match get_relationship(&target, &user.id) {
RelationshipStatus::Blocked => {
col.update_one(
doc! {
"_id": &user.id,
"relations._id": &target.id
},
doc! {
"$set": {
"relations.$.status": "BlockedOther"
}
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "user",
})?;
let target = target
.from_override(&user, RelationshipStatus::BlockedOther)
.await?;
ClientboundNotification::UserRelationship {
id: user.id.clone(),
user: target,
status: RelationshipStatus::BlockedOther,
}
.publish(user.id.clone());
Ok(json!({ "status": "BlockedOther" }))
}
RelationshipStatus::BlockedOther => {
match try_join!(
col.update_one(
doc! {
"_id": &user.id
},
doc! {
"$pull": {
"relations": {
"_id": &target.id
}
}
},
None
),
col.update_one(
doc! {
"_id": &target.id
},
doc! {
"$pull": {
"relations": {
"_id": &user.id
}
}
},
None
)
) {
Ok(_) => {
let target = target
.from_override(&user, RelationshipStatus::None)
.await?;
let user = user
.from_override(&target, RelationshipStatus::None)
.await?;
let target_id = target.id.clone();
ClientboundNotification::UserRelationship {
id: user.id.clone(),
user: target,
status: RelationshipStatus::None,
}
.publish(user.id.clone());
ClientboundNotification::UserRelationship {
id: target_id.clone(),
user: user,
status: RelationshipStatus::None,
}
.publish(target_id);
Ok(json!({ "status": "None" }))
}
Err(_) => Err(Error::DatabaseError {
operation: "update_one",
with: "user",
}),
}
}
_ => Err(Error::InternalError),
},
_ => Err(Error::NoEffect),
}
pub async fn req(/*user: UserRef, target: Ref*/ target: String) -> Result<Value> {
todo!()
}