Skip to content
Snippets Groups Projects

Compare revisions

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

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Showing
with 660 additions and 254 deletions
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
use crate::{database::*, notifications::events::RemoveServerField};
use mongodb::bson::{doc, to_bson, to_document};
use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Validate, Serialize, Deserialize)]
pub struct Data {
#[validate(length(min = 1, max = 32))]
name: Option<String>,
#[validate(length(min = 0, max = 1024))]
description: Option<String>,
icon: Option<String>,
banner: Option<String>,
categories: Option<Vec<Category>>,
system_messages: Option<SystemMessageChannels>,
remove: Option<RemoveServerField>,
}
#[patch("/<target>", data = "<data>")]
pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
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()
{
return Ok(());
}
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" })?);
}
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(())
}
use crate::database::*;
use crate::util::result::{Error, Result};
use rocket_contrib::json::JsonValue;
#[get("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
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))
}
use crate::database::*;
use crate::util::result::{Error, Result};
use mongodb::bson::doc;
use mongodb::options::FindOneOptions;
use rocket_contrib::json::{Json, JsonValue};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Options {
keys: Vec<String>,
}
#[post("/settings/fetch", data = "<options>")]
pub async fn req(user: User, options: Json<Options>) -> Result<JsonValue> {
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!({}))
}
}
use crate::database::*;
use crate::util::result::Result;
use mongodb::bson::doc;
use rocket_contrib::json::JsonValue;
#[get("/unreads")]
pub async fn req(user: User) -> Result<JsonValue> {
Ok(json!(User::fetch_unreads(&user.id).await?))
}
use rocket::Route; use rocket::Route;
mod get_settings;
mod get_unreads;
mod set_settings;
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![] routes![get_settings::req, set_settings::req, get_unreads::req]
} }
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
use chrono::prelude::*;
use mongodb::bson::{doc, to_bson};
use mongodb::options::UpdateOptions;
use rocket::request::Form;
use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
type Data = HashMap<String, String>;
#[derive(Serialize, Deserialize, FromForm)]
pub struct Options {
timestamp: Option<i64>,
}
#[post("/settings/set?<options..>", data = "<data>")]
pub async fn req(user: User, data: Json<Data>, options: Form<Options>) -> Result<()> {
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(())
}
...@@ -67,24 +67,26 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> { ...@@ -67,24 +67,26 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
) )
) { ) {
Ok(_) => { Ok(_) => {
let target_user = target_user.from_override(&user, RelationshipStatus::Friend).await?; let target_user = target_user
let user = user.from_override(&target_user, RelationshipStatus::Friend).await?; .from_override(&user, RelationshipStatus::Friend)
.await?;
let user = user
.from_override(&target_user, RelationshipStatus::Friend)
.await?;
try_join!( ClientboundNotification::UserRelationship {
ClientboundNotification::UserRelationship { id: user.id.clone(),
id: user.id.clone(), user: target_user,
user: target_user, status: RelationshipStatus::Friend,
status: RelationshipStatus::Friend }
} .publish(user.id.clone());
.publish(user.id.clone()),
ClientboundNotification::UserRelationship { ClientboundNotification::UserRelationship {
id: target_id.to_string(), id: target_id.to_string(),
user, user,
status: RelationshipStatus::Friend status: RelationshipStatus::Friend,
} }
.publish(target_id.to_string()) .publish(target_id.to_string());
)
.ok();
Ok(json!({ "status": "Friend" })) Ok(json!({ "status": "Friend" }))
} }
...@@ -126,23 +128,26 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> { ...@@ -126,23 +128,26 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
) )
) { ) {
Ok(_) => { Ok(_) => {
let target_user = target_user.from_override(&user, RelationshipStatus::Outgoing).await?; let target_user = target_user
let user = user.from_override(&target_user, RelationshipStatus::Incoming).await?; .from_override(&user, RelationshipStatus::Outgoing)
try_join!( .await?;
ClientboundNotification::UserRelationship { let user = user
id: user.id.clone(), .from_override(&target_user, RelationshipStatus::Incoming)
user: target_user, .await?;
status: RelationshipStatus::Outgoing
} ClientboundNotification::UserRelationship {
.publish(user.id.clone()), id: user.id.clone(),
ClientboundNotification::UserRelationship { user: target_user,
id: target_id.to_string(), status: RelationshipStatus::Outgoing,
user, }
status: RelationshipStatus::Incoming .publish(user.id.clone());
}
.publish(target_id.to_string()) ClientboundNotification::UserRelationship {
) id: target_id.to_string(),
.ok(); user,
status: RelationshipStatus::Incoming,
}
.publish(target_id.to_string());
Ok(json!({ "status": "Outgoing" })) Ok(json!({ "status": "Outgoing" }))
} }
......
...@@ -38,9 +38,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ...@@ -38,9 +38,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
user: target, user: target,
status: RelationshipStatus::Blocked, status: RelationshipStatus::Blocked,
} }
.publish(user.id.clone()) .publish(user.id.clone());
.await
.ok();
Ok(json!({ "status": "Blocked" })) Ok(json!({ "status": "Blocked" }))
} }
...@@ -76,25 +74,27 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ...@@ -76,25 +74,27 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
) )
) { ) {
Ok(_) => { Ok(_) => {
let target = target.from_override(&user, RelationshipStatus::Friend).await?; let target = target
let user = user.from_override(&target, RelationshipStatus::Friend).await?; .from_override(&user, RelationshipStatus::Blocked)
.await?;
let user = user
.from_override(&target, RelationshipStatus::BlockedOther)
.await?;
let target_id = target.id.clone(); let target_id = target.id.clone();
try_join!( ClientboundNotification::UserRelationship {
ClientboundNotification::UserRelationship { id: user.id.clone(),
id: user.id.clone(), user: target,
user: target, status: RelationshipStatus::Blocked,
status: RelationshipStatus::Blocked }
} .publish(user.id.clone());
.publish(user.id.clone()),
ClientboundNotification::UserRelationship { ClientboundNotification::UserRelationship {
id: target_id.clone(), id: target_id.clone(),
user, user,
status: RelationshipStatus::BlockedOther status: RelationshipStatus::BlockedOther,
} }
.publish(target_id) .publish(target_id);
)
.ok();
Ok(json!({ "status": "Blocked" })) Ok(json!({ "status": "Blocked" }))
} }
...@@ -134,25 +134,27 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ...@@ -134,25 +134,27 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
) )
) { ) {
Ok(_) => { Ok(_) => {
let target = target.from_override(&user, RelationshipStatus::Blocked).await?; let target = target
let user = user.from_override(&target, RelationshipStatus::BlockedOther).await?; .from_override(&user, RelationshipStatus::Blocked)
.await?;
let user = user
.from_override(&target, RelationshipStatus::BlockedOther)
.await?;
let target_id = target.id.clone(); let target_id = target.id.clone();
try_join!( ClientboundNotification::UserRelationship {
ClientboundNotification::UserRelationship { id: user.id.clone(),
id: user.id.clone(), user: target,
user: target, status: RelationshipStatus::Blocked,
status: RelationshipStatus::Blocked }
} .publish(user.id.clone());
.publish(user.id.clone()),
ClientboundNotification::UserRelationship { ClientboundNotification::UserRelationship {
id: target_id.clone(), id: target_id.clone(),
user, user,
status: RelationshipStatus::BlockedOther status: RelationshipStatus::BlockedOther,
} }
.publish(target_id) .publish(target_id);
)
.ok();
Ok(json!({ "status": "Blocked" })) Ok(json!({ "status": "Blocked" }))
} }
......
...@@ -57,11 +57,12 @@ pub async fn req( ...@@ -57,11 +57,12 @@ pub async fn req(
ClientboundNotification::UserUpdate { ClientboundNotification::UserUpdate {
id: user.id.clone(), id: user.id.clone(),
data: json!(data.0), data: json!({
"username": data.username
}),
clear: None,
} }
.publish(user.id.clone()) .publish_as_user(user.id.clone());
.await
.ok();
Ok(()) Ok(())
} }
use crate::database::*;
use crate::notifications::events::ClientboundNotification; use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result}; use crate::util::result::{Error, Result};
use crate::{database::*, notifications::events::RemoveUserField};
use mongodb::bson::{doc, to_document}; use mongodb::bson::{doc, to_document};
use rocket_contrib::json::Json; use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use validator::Validate; use validator::Validate;
#[derive(Validate, Serialize, Deserialize, Debug)]
pub struct UserProfileData {
#[validate(length(min = 0, max = 2000))]
#[serde(skip_serializing_if = "Option::is_none")]
content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(min = 1, max = 128))]
background: Option<String>,
}
#[derive(Validate, Serialize, Deserialize)] #[derive(Validate, Serialize, Deserialize)]
pub struct Data { pub struct Data {
#[serde(skip_serializing_if = "Option::is_none")]
#[validate] #[validate]
status: Option<UserStatus>, status: Option<UserStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate] #[validate]
profile: Option<UserProfile>, profile: Option<UserProfileData>,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(min = 1, max = 128))] #[validate(length(min = 1, max = 128))]
avatar: Option<String>, avatar: Option<String>,
remove: Option<RemoveUserField>,
} }
#[patch("/<_ignore_id>", data = "<data>")] #[patch("/<_ignore_id>", data = "<data>")]
pub async fn req(user: User, mut data: Json<Data>, _ignore_id: String) -> Result<()> { pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> {
if data.0.status.is_none() && data.0.profile.is_none() && data.0.avatar.is_none() { let mut data = data.into_inner();
return Ok(())
}
data.validate() data.validate()
.map_err(|error| Error::FailedValidation { error })?; .map_err(|error| Error::FailedValidation { error })?;
let mut set = to_document(&data.0).map_err(|_| Error::DatabaseError { operation: "to_document", with: "data" })?; if data.status.is_none()
&& data.profile.is_none()
&& data.avatar.is_none()
&& data.remove.is_none()
{
return Ok(());
}
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",
})?,
);
let avatar = std::mem::replace(&mut data.0.avatar, None); remove_background = true;
let attachment = if let Some(attachment_id) = avatar { }
}
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?; 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" })?); set.insert(
Some(attachment) "avatar",
} else { to_document(&attachment).map_err(|_| Error::DatabaseError {
None operation: "to_document",
}; with: "attachment",
})?,
get_collection("users") );
.update_one(
doc! { "_id": &user.id }, remove_avatar = true;
doc! { "$set": set }, }
None
) let mut operations = doc! {};
.await if set.len() > 0 {
.map_err(|_| Error::DatabaseError { operation: "update_one", with: "user" })?; operations.insert("$set", &set);
}
if let Some(status) = data.0.status {
ClientboundNotification::UserUpdate { if unset.len() > 0 {
id: user.id.clone(), operations.insert("$unset", unset);
data: json!({ "status": status }), }
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?;
} }
.publish(user.id.clone())
.await
.ok();
} }
if let Some(avatar) = attachment { if remove_background {
ClientboundNotification::UserUpdate { if let Some(profile) = user.profile {
id: user.id.clone(), if let Some(old_background) = profile.background {
data: json!({ "avatar": avatar }), old_background.delete().await?;
}
} }
.publish(user.id.clone())
.await
.ok();
} }
Ok(()) Ok(())
......
...@@ -12,8 +12,8 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ...@@ -12,8 +12,8 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
.find( .find(
doc! { doc! {
"$and": [ "$and": [
{ "relations._id": &user.id, "relations.status": "Friend" }, { "relations": { "$elemMatch": { "_id": &user.id, "status": "Friend" } } },
{ "relations._id": &target.id, "relations.status": "Friend" } { "relations": { "$elemMatch": { "_id": &target.id, "status": "Friend" } } }
] ]
}, },
FindOptions::builder().projection(doc! { "_id": 1 }).build(), FindOptions::builder().projection(doc! { "_id": 1 }).build(),
......
use rocket::response::NamedFile; use rocket::{Request, Response};
use rocket::response::{self, NamedFile, Responder};
use std::path::Path; use std::path::Path;
use crate::database::Ref; use crate::database::Ref;
pub struct CachedFile(NamedFile);
pub static CACHE_CONTROL: &'static str = "public, max-age=31536000, immutable";
impl<'r> Responder<'r, 'static> for CachedFile {
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
Response::build_from(self.0.respond_to(req)?)
.raw_header("Cache-control", CACHE_CONTROL)
.ok()
}
}
#[get("/<target>/default_avatar")] #[get("/<target>/default_avatar")]
pub async fn req(target: Ref) -> Option<NamedFile> { pub async fn req(target: Ref) -> Option<CachedFile> {
match target.id.chars().nth(25).unwrap() { match target.id.chars().nth(25).unwrap() {
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' => { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' => {
NamedFile::open(Path::new("assets/user_red.png")).await.ok() NamedFile::open(Path::new("assets/user_red.png")).await.ok().map(|n| CachedFile(n))
} }
'8' | '9' | 'A' | 'C' | 'B' | 'D' | 'E' | 'F' => { '8' | '9' | 'A' | 'C' | 'B' | 'D' | 'E' | 'F' => {
NamedFile::open(Path::new("assets/user_green.png")) NamedFile::open(Path::new("assets/user_green.png"))
.await .await
.ok() .ok().map(|n| CachedFile(n))
} }
'G' | 'H' | 'J' | 'K' | 'M' | 'N' | 'P' | 'Q' => { 'G' | 'H' | 'J' | 'K' | 'M' | 'N' | 'P' | 'Q' => {
NamedFile::open(Path::new("assets/user_blue.png")) NamedFile::open(Path::new("assets/user_blue.png"))
.await .await
.ok() .ok().map(|n| CachedFile(n))
} }
'R' | 'S' | 'T' | 'V' | 'W' | 'X' | 'Y' | 'Z' => { 'R' | 'S' | 'T' | 'V' | 'W' | 'X' | 'Y' | 'Z' => {
NamedFile::open(Path::new("assets/user_yellow.png")) NamedFile::open(Path::new("assets/user_yellow.png"))
.await .await
.ok() .ok().map(|n| CachedFile(n))
} }
_ => unreachable!(), _ => unreachable!(),
} }
......
...@@ -45,25 +45,27 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ...@@ -45,25 +45,27 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
) )
) { ) {
Ok(_) => { Ok(_) => {
let target = target.from_override(&user, RelationshipStatus::None).await?; let target = target
let user = user.from_override(&target, RelationshipStatus::None).await?; .from_override(&user, RelationshipStatus::None)
.await?;
let user = user
.from_override(&target, RelationshipStatus::None)
.await?;
let target_id = target.id.clone(); let target_id = target.id.clone();
try_join!( ClientboundNotification::UserRelationship {
ClientboundNotification::UserRelationship { id: user.id.clone(),
id: user.id.clone(), user: target,
user: target, status: RelationshipStatus::None,
status: RelationshipStatus::None }
} .publish(user.id.clone());
.publish(user.id.clone()),
ClientboundNotification::UserRelationship { ClientboundNotification::UserRelationship {
id: target_id.clone(), id: target_id.clone(),
user, user,
status: RelationshipStatus::None status: RelationshipStatus::None,
} }
.publish(target_id) .publish(target_id);
)
.ok();
Ok(json!({ "status": "None" })) Ok(json!({ "status": "None" }))
} }
......
...@@ -12,100 +12,100 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ...@@ -12,100 +12,100 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let target = target.fetch_user().await?; let target = target.fetch_user().await?;
match get_relationship(&user, &target.id) { match get_relationship(&user, &target.id) {
RelationshipStatus::Blocked => { RelationshipStatus::Blocked => match get_relationship(&target, &user.id) {
match get_relationship(&target, &user.id) { RelationshipStatus::Blocked => {
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( col.update_one(
doc! { doc! {
"_id": &user.id, "_id": &user.id
"relations._id": &target.id
}, },
doc! { doc! {
"$set": { "$pull": {
"relations.$.status": "BlockedOther" "relations": {
"_id": &target.id
}
} }
}, },
None, None
) ),
.await col.update_one(
.map_err(|_| Error::DatabaseError { doc! {
operation: "update_one", "_id": &target.id
with: "user", },
})?; doc! {
"$pull": {
let target = target.from_override(&user, RelationshipStatus::BlockedOther).await?; "relations": {
ClientboundNotification::UserRelationship { "_id": &user.id
id: user.id.clone(),
user: target,
status: RelationshipStatus::BlockedOther,
}
.publish(user.id.clone())
.await
.ok();
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 },
) None
) { )
Ok(_) => { ) {
let target = target.from_override(&user, RelationshipStatus::None).await?; Ok(_) => {
let user = user.from_override(&target, RelationshipStatus::None).await?; let target = target
let target_id = target.id.clone(); .from_override(&user, RelationshipStatus::None)
.await?;
let user = user
.from_override(&target, RelationshipStatus::None)
.await?;
let target_id = target.id.clone();
try_join!( ClientboundNotification::UserRelationship {
ClientboundNotification::UserRelationship { id: user.id.clone(),
id: user.id.clone(), user: target,
user: target, status: RelationshipStatus::None,
status: RelationshipStatus::None }
} .publish(user.id.clone());
.publish(user.id.clone()),
ClientboundNotification::UserRelationship {
id: target_id.clone(),
user: user,
status: RelationshipStatus::None
}
.publish(target_id)
)
.ok();
Ok(json!({ "status": "None" })) ClientboundNotification::UserRelationship {
id: target_id.clone(),
user: user,
status: RelationshipStatus::None,
} }
Err(_) => Err(Error::DatabaseError { .publish(target_id);
operation: "update_one",
with: "user", Ok(json!({ "status": "None" }))
}),
} }
Err(_) => Err(Error::DatabaseError {
operation: "update_one",
with: "user",
}),
} }
_ => Err(Error::InternalError),
} }
} _ => Err(Error::InternalError),
},
_ => Err(Error::NoEffect), _ => Err(Error::NoEffect),
} }
} }
...@@ -3,77 +3,63 @@ use rocket::http::{ContentType, Status}; ...@@ -3,77 +3,63 @@ use rocket::http::{ContentType, Status};
use rocket::request::Request; use rocket::request::Request;
use rocket::response::{self, Responder, Response}; use rocket::response::{self, Responder, Response};
use serde::Serialize; use serde::Serialize;
use snafu::Snafu;
use std::io::Cursor; use std::io::Cursor;
use validator::ValidationErrors; use validator::ValidationErrors;
#[derive(Serialize, Debug, Snafu)] #[derive(Serialize, Debug)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum Error { pub enum Error {
#[snafu(display("This error has not been labelled."))]
LabelMe, LabelMe,
// ? Onboarding related errors. // ? Onboarding related errors.
#[snafu(display("Already finished onboarding."))]
AlreadyOnboarded, AlreadyOnboarded,
// ? User related errors. // ? User related errors.
#[snafu(display("Username has already been taken."))]
UsernameTaken, UsernameTaken,
#[snafu(display("This user does not exist!"))]
UnknownUser, UnknownUser,
#[snafu(display("Already friends with this user."))]
AlreadyFriends, AlreadyFriends,
#[snafu(display("Already sent a request to this user."))]
AlreadySentRequest, AlreadySentRequest,
#[snafu(display("You have blocked this user."))]
Blocked, Blocked,
#[snafu(display("You have been blocked by this user."))]
BlockedByOther, BlockedByOther,
#[snafu(display("Not friends with target user."))]
NotFriends, NotFriends,
// ? Channel related errors. // ? Channel related errors.
#[snafu(display("This channel does not exist!"))]
UnknownChannel, UnknownChannel,
#[snafu(display("Attachment does not exist!"))]
UnknownAttachment, UnknownAttachment,
#[snafu(display("Cannot edit someone else's message."))] UnknownMessage,
CannotEditMessage, CannotEditMessage,
#[snafu(display("Cannot send empty message."))] CannotJoinCall,
TooManyAttachments,
TooManyReplies,
EmptyMessage, EmptyMessage,
#[snafu(display("Cannot remove yourself from a group, use delete channel instead."))]
CannotRemoveYourself, CannotRemoveYourself,
#[snafu(display("Group size is too large."))] GroupTooLarge {
GroupTooLarge { max: usize }, max: usize,
#[snafu(display("User already part of group."))] },
AlreadyInGroup, AlreadyInGroup,
#[snafu(display("User is not part of the group."))]
NotInGroup, NotInGroup,
// ? Server related errors.
UnknownServer,
InvalidRole,
Banned,
// ? General errors. // ? General errors.
#[snafu(display("Trying to fetch too much data."))]
TooManyIds, TooManyIds,
#[snafu(display("Failed to validate fields."))] FailedValidation {
FailedValidation { error: ValidationErrors }, error: ValidationErrors,
#[snafu(display("Encountered a database error."))] },
DatabaseError { DatabaseError {
operation: &'static str, operation: &'static str,
with: &'static str, with: &'static str,
}, },
#[snafu(display("Internal server error."))]
InternalError, InternalError,
#[snafu(display("Missing permission."))]
MissingPermission, MissingPermission,
#[snafu(display("Operation cannot be performed on this object."))]
InvalidOperation, InvalidOperation,
#[snafu(display("Email or password is incorrect."))]
InvalidCredentials, InvalidCredentials,
#[snafu(display("Already created an object with this nonce."))]
DuplicateNonce, DuplicateNonce,
#[snafu(display("Voso is not enabled on this instance."))]
VosoUnavailable, VosoUnavailable,
#[snafu(display("This request had no effect."))] NotFound,
NoEffect, NoEffect,
} }
...@@ -96,14 +82,22 @@ impl<'r> Responder<'r, 'static> for Error { ...@@ -96,14 +82,22 @@ impl<'r> Responder<'r, 'static> for Error {
Error::NotFriends => Status::Forbidden, Error::NotFriends => Status::Forbidden,
Error::UnknownChannel => Status::NotFound, Error::UnknownChannel => Status::NotFound,
Error::UnknownMessage => Status::NotFound,
Error::UnknownAttachment => Status::BadRequest, Error::UnknownAttachment => Status::BadRequest,
Error::CannotEditMessage => Status::Forbidden, Error::CannotEditMessage => Status::Forbidden,
Error::CannotJoinCall => Status::BadRequest,
Error::TooManyAttachments => Status::BadRequest,
Error::TooManyReplies => Status::BadRequest,
Error::EmptyMessage => Status::UnprocessableEntity, Error::EmptyMessage => Status::UnprocessableEntity,
Error::CannotRemoveYourself => Status::BadRequest, Error::CannotRemoveYourself => Status::BadRequest,
Error::GroupTooLarge { .. } => Status::Forbidden, Error::GroupTooLarge { .. } => Status::Forbidden,
Error::AlreadyInGroup => Status::Conflict, Error::AlreadyInGroup => Status::Conflict,
Error::NotInGroup => Status::NotFound, Error::NotInGroup => Status::NotFound,
Error::UnknownServer => Status::NotFound,
Error::InvalidRole => Status::NotFound,
Error::Banned => Status::Forbidden,
Error::FailedValidation { .. } => Status::UnprocessableEntity, Error::FailedValidation { .. } => Status::UnprocessableEntity,
Error::DatabaseError { .. } => Status::InternalServerError, Error::DatabaseError { .. } => Status::InternalServerError,
Error::InternalError => Status::InternalServerError, Error::InternalError => Status::InternalServerError,
...@@ -113,6 +107,7 @@ impl<'r> Responder<'r, 'static> for Error { ...@@ -113,6 +107,7 @@ impl<'r> Responder<'r, 'static> for Error {
Error::InvalidCredentials => Status::Forbidden, Error::InvalidCredentials => Status::Forbidden,
Error::DuplicateNonce => Status::Conflict, Error::DuplicateNonce => Status::Conflict,
Error::VosoUnavailable => Status::BadRequest, Error::VosoUnavailable => Status::BadRequest,
Error::NotFound => Status::NotFound,
Error::NoEffect => Status::Ok, Error::NoEffect => Status::Ok,
}; };
......
...@@ -18,6 +18,8 @@ lazy_static! { ...@@ -18,6 +18,8 @@ lazy_static! {
pub static ref AUTUMN_URL: String = pub static ref AUTUMN_URL: String =
env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string()); env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
pub static ref JANUARY_URL: String =
env::var("JANUARY_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
pub static ref VOSO_URL: String = pub static ref VOSO_URL: String =
env::var("VOSO_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string()); env::var("VOSO_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
pub static ref VOSO_WS_HOST: String = pub static ref VOSO_WS_HOST: String =
...@@ -35,7 +37,6 @@ lazy_static! { ...@@ -35,7 +37,6 @@ lazy_static! {
env::var("REVOLT_VAPID_PUBLIC_KEY").expect("Missing REVOLT_VAPID_PUBLIC_KEY environment variable."); env::var("REVOLT_VAPID_PUBLIC_KEY").expect("Missing REVOLT_VAPID_PUBLIC_KEY environment variable.");
// Application Flags // Application Flags
pub static ref DISABLE_REGISTRATION: bool = env::var("REVOLT_DISABLE_REGISTRATION").map_or(false, |v| v == "1");
pub static ref INVITE_ONLY: bool = env::var("REVOLT_INVITE_ONLY").map_or(false, |v| v == "1"); pub static ref INVITE_ONLY: bool = env::var("REVOLT_INVITE_ONLY").map_or(false, |v| v == "1");
pub static ref USE_EMAIL: bool = env::var("REVOLT_USE_EMAIL_VERIFICATION").map_or( pub static ref USE_EMAIL: bool = env::var("REVOLT_USE_EMAIL_VERIFICATION").map_or(
env::var("REVOLT_SMTP_HOST").is_ok() env::var("REVOLT_SMTP_HOST").is_ok()
...@@ -47,6 +48,7 @@ lazy_static! { ...@@ -47,6 +48,7 @@ lazy_static! {
pub static ref USE_HCAPTCHA: bool = env::var("REVOLT_HCAPTCHA_KEY").is_ok(); pub static ref USE_HCAPTCHA: bool = env::var("REVOLT_HCAPTCHA_KEY").is_ok();
pub static ref USE_PROMETHEUS: bool = env::var("REVOLT_ENABLE_PROMETHEUS").map_or(false, |v| v == "1"); pub static ref USE_PROMETHEUS: bool = env::var("REVOLT_ENABLE_PROMETHEUS").map_or(false, |v| v == "1");
pub static ref USE_AUTUMN: bool = env::var("AUTUMN_PUBLIC_URL").is_ok(); pub static ref USE_AUTUMN: bool = env::var("AUTUMN_PUBLIC_URL").is_ok();
pub static ref USE_JANUARY: bool = env::var("JANUARY_PUBLIC_URL").is_ok();
pub static ref USE_VOSO: bool = env::var("VOSO_PUBLIC_URL").is_ok() && env::var("VOSO_MANAGE_TOKEN").is_ok(); pub static ref USE_VOSO: bool = env::var("VOSO_PUBLIC_URL").is_ok() && env::var("VOSO_MANAGE_TOKEN").is_ok();
// SMTP Settings // SMTP Settings
...@@ -61,6 +63,8 @@ lazy_static! { ...@@ -61,6 +63,8 @@ lazy_static! {
// Application Logic Settings // Application Logic Settings
pub static ref MAX_GROUP_SIZE: usize = pub static ref MAX_GROUP_SIZE: usize =
env::var("REVOLT_MAX_GROUP_SIZE").unwrap_or_else(|_| "50".to_string()).parse().unwrap(); env::var("REVOLT_MAX_GROUP_SIZE").unwrap_or_else(|_| "50".to_string()).parse().unwrap();
pub static ref EARLY_ADOPTER_BADGE: i64 =
env::var("REVOLT_EARLY_ADOPTER_BADGE").unwrap_or_else(|_| "0".to_string()).parse().unwrap();
} }
pub fn preflight_checks() { pub fn preflight_checks() {
......
pub const VERSION: &str = "0.5.1-alpha.21";