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
use rocket::Route;
mod get_settings;
mod get_unreads;
mod set_settings;
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(())
}
......@@ -57,10 +57,12 @@ pub async fn req(
ClientboundNotification::UserUpdate {
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());
Ok(())
}
......@@ -99,7 +99,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
}
let avatar = std::mem::replace(&mut data.avatar, None);
let attachment = if let Some(attachment_id) = avatar {
if let Some(attachment_id) = avatar {
let attachment = File::find_and_use(&attachment_id, "avatars", "user", &user.id).await?;
set.insert(
"avatar",
......@@ -110,14 +110,11 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
);
remove_avatar = true;
Some(attachment)
} else {
None
};
}
let mut operations = doc! {};
if set.len() > 0 {
operations.insert("$set", set);
operations.insert("$set", &set);
}
if unset.len() > 0 {
......@@ -134,32 +131,12 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
})?;
}
if let Some(status) = &data.status {
ClientboundNotification::UserUpdate {
id: user.id.clone(),
data: json!({ "status": status }),
clear: None,
}
.publish(user.id.clone());
}
if let Some(avatar) = attachment {
ClientboundNotification::UserUpdate {
id: user.id.clone(),
data: json!({ "avatar": avatar }),
clear: None,
}
.publish(user.id.clone());
}
if let Some(clear) = data.remove {
ClientboundNotification::UserUpdate {
id: user.id.clone(),
data: json!({}),
clear: Some(clear),
}
.publish(user.id.clone());
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 {
......
use rocket::response::NamedFile;
use rocket::{Request, Response};
use rocket::response::{self, NamedFile, Responder};
use std::path::Path;
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")]
pub async fn req(target: Ref) -> Option<NamedFile> {
pub async fn req(target: Ref) -> Option<CachedFile> {
match target.id.chars().nth(25).unwrap() {
'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' => {
NamedFile::open(Path::new("assets/user_green.png"))
.await
.ok()
.ok().map(|n| CachedFile(n))
}
'G' | 'H' | 'J' | 'K' | 'M' | 'N' | 'P' | 'Q' => {
NamedFile::open(Path::new("assets/user_blue.png"))
.await
.ok()
.ok().map(|n| CachedFile(n))
}
'R' | 'S' | 'T' | 'V' | 'W' | 'X' | 'Y' | 'Z' => {
NamedFile::open(Path::new("assets/user_yellow.png"))
.await
.ok()
.ok().map(|n| CachedFile(n))
}
_ => unreachable!(),
}
......
......@@ -26,16 +26,29 @@ pub enum Error {
// ? Channel related errors.
UnknownChannel,
UnknownAttachment,
UnknownMessage,
CannotEditMessage,
CannotJoinCall,
TooManyAttachments,
TooManyReplies,
EmptyMessage,
CannotRemoveYourself,
GroupTooLarge { max: usize },
GroupTooLarge {
max: usize,
},
AlreadyInGroup,
NotInGroup,
// ? Server related errors.
UnknownServer,
InvalidRole,
Banned,
// ? General errors.
TooManyIds,
FailedValidation { error: ValidationErrors },
FailedValidation {
error: ValidationErrors,
},
DatabaseError {
operation: &'static str,
with: &'static str,
......@@ -46,6 +59,7 @@ pub enum Error {
InvalidCredentials,
DuplicateNonce,
VosoUnavailable,
NotFound,
NoEffect,
}
......@@ -68,14 +82,22 @@ impl<'r> Responder<'r, 'static> for Error {
Error::NotFriends => Status::Forbidden,
Error::UnknownChannel => Status::NotFound,
Error::UnknownMessage => Status::NotFound,
Error::UnknownAttachment => Status::BadRequest,
Error::CannotEditMessage => Status::Forbidden,
Error::CannotJoinCall => Status::BadRequest,
Error::TooManyAttachments => Status::BadRequest,
Error::TooManyReplies => Status::BadRequest,
Error::EmptyMessage => Status::UnprocessableEntity,
Error::CannotRemoveYourself => Status::BadRequest,
Error::GroupTooLarge { .. } => Status::Forbidden,
Error::AlreadyInGroup => Status::Conflict,
Error::NotInGroup => Status::NotFound,
Error::UnknownServer => Status::NotFound,
Error::InvalidRole => Status::NotFound,
Error::Banned => Status::Forbidden,
Error::FailedValidation { .. } => Status::UnprocessableEntity,
Error::DatabaseError { .. } => Status::InternalServerError,
Error::InternalError => Status::InternalServerError,
......@@ -85,6 +107,7 @@ impl<'r> Responder<'r, 'static> for Error {
Error::InvalidCredentials => Status::Forbidden,
Error::DuplicateNonce => Status::Conflict,
Error::VosoUnavailable => Status::BadRequest,
Error::NotFound => Status::NotFound,
Error::NoEffect => Status::Ok,
};
......
......@@ -37,7 +37,6 @@ lazy_static! {
env::var("REVOLT_VAPID_PUBLIC_KEY").expect("Missing REVOLT_VAPID_PUBLIC_KEY environment variable.");
// 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 USE_EMAIL: bool = env::var("REVOLT_USE_EMAIL_VERIFICATION").map_or(
env::var("REVOLT_SMTP_HOST").is_ok()
......
pub const VERSION: &str = "0.4.2-alpha.0";
pub const VERSION: &str = "0.5.1-alpha.21";