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 crate::database::*;
use crate::util::result::{Error, Result}; use crate::util::result::{Error, Result};
use crate::{database::*, notifications::websocket::is_online};
use rocket_contrib::json::JsonValue; use rocket_contrib::json::JsonValue;
#[get("/<target>")] #[get("/<target>")]
pub async fn req(user: User, target: Ref) -> Result<JsonValue> { pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let mut target = target.fetch_user().await?; let target = target.fetch_user().await?;
if user.id != target.id { let perm = permissions::PermissionCalculator::new(&user)
// Check whether we are allowed to fetch this user. .with_user(&target)
let perm = permissions::user::calculate(&user, &target.id).await; .for_user_given()
if !perm.get_access() { .await?;
Err(Error::LabelMe)?
}
// Only return user relationships if the target is the caller. if !perm.get_access() {
target.relations = None; Err(Error::MissingPermission)?
// Add relevant relationship
if let Some(relationships) = &user.relations {
target.relationship = relationships
.iter()
.find(|x| x.id == user.id)
.map(|x| x.status.clone())
.or_else(|| Some(RelationshipStatus::None));
} else {
target.relationship = Some(RelationshipStatus::None);
}
} else {
target.relationship = Some(RelationshipStatus::User);
} }
target.online = Some(is_online(&target.id)); Ok(json!(target.from(&user).with(perm)))
Ok(json!(target))
} }
use crate::database::*;
use crate::util::result::{Error, Result};
use futures::StreamExt;
use mongodb::bson::{doc, Document};
use mongodb::options::FindOptions;
use rocket_contrib::json::JsonValue;
#[get("/<target>/mutual")]
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
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>>();
Ok(json!({ "users": users }))
}
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;
#[get("/<target>/avatar")] pub struct CachedFile(NamedFile);
pub async fn req(target: Ref) -> Option<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<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!(),
} }
......
...@@ -2,11 +2,15 @@ use rocket::Route; ...@@ -2,11 +2,15 @@ use rocket::Route;
mod add_friend; mod add_friend;
mod block_user; mod block_user;
mod change_username;
mod edit_user;
mod fetch_dms; mod fetch_dms;
mod fetch_profile;
mod fetch_relationship; mod fetch_relationship;
mod fetch_relationships; mod fetch_relationships;
mod fetch_user; mod fetch_user;
mod get_avatar; mod find_mutual;
mod get_default_avatar;
mod open_dm; mod open_dm;
mod remove_friend; mod remove_friend;
mod unblock_user; mod unblock_user;
...@@ -15,11 +19,15 @@ pub fn routes() -> Vec<Route> { ...@@ -15,11 +19,15 @@ pub fn routes() -> Vec<Route> {
routes![ routes![
// User Information // User Information
fetch_user::req, fetch_user::req,
get_avatar::req, edit_user::req,
change_username::req,
get_default_avatar::req,
fetch_profile::req,
// Direct Messaging // Direct Messaging
fetch_dms::req, fetch_dms::req,
open_dm::req, open_dm::req,
// Relationships // Relationships
find_mutual::req,
fetch_relationships::req, fetch_relationships::req,
fetch_relationship::req, fetch_relationship::req,
add_friend::req, add_friend::req,
......
...@@ -9,12 +9,12 @@ use ulid::Ulid; ...@@ -9,12 +9,12 @@ use ulid::Ulid;
pub async fn req(user: User, target: Ref) -> Result<JsonValue> { pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let query = if user.id == target.id { let query = if user.id == target.id {
doc! { doc! {
"type": "SavedMessages", "channel_type": "SavedMessages",
"user": &user.id "user": &user.id
} }
} else { } else {
doc! { doc! {
"type": "DirectMessage", "channel_type": "DirectMessage",
"recipients": { "recipients": {
"$all": [ &user.id, &target.id ] "$all": [ &user.id, &target.id ]
} }
...@@ -40,6 +40,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ...@@ -40,6 +40,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
id, id,
active: false, active: false,
recipients: vec![user.id, target.id], recipients: vec![user.id, target.id],
last_message: None,
} }
}; };
......
use crate::database::*; use crate::database::*;
use crate::notifications::{events::ClientboundNotification, hive}; use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result}; use crate::util::result::{Error, Result};
use futures::try_join; use futures::try_join;
use hive_pubsub::PubSub;
use mongodb::bson::doc; use mongodb::bson::doc;
use rocket_contrib::json::JsonValue; use rocket_contrib::json::JsonValue;
...@@ -11,6 +10,8 @@ use rocket_contrib::json::JsonValue; ...@@ -11,6 +10,8 @@ use rocket_contrib::json::JsonValue;
pub async fn req(user: User, target: Ref) -> Result<JsonValue> { pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let col = get_collection("users"); let col = get_collection("users");
let target = target.fetch_user().await?;
match get_relationship(&user, &target.id) { match get_relationship(&user, &target.id) {
RelationshipStatus::Friend RelationshipStatus::Friend
| RelationshipStatus::Outgoing | RelationshipStatus::Outgoing
...@@ -44,25 +45,27 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ...@@ -44,25 +45,27 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
) )
) { ) {
Ok(_) => { Ok(_) => {
try_join!( let target = target
ClientboundNotification::UserRelationship { .from_override(&user, RelationshipStatus::None)
id: user.id.clone(), .await?;
user: target.id.clone(), let user = user
status: RelationshipStatus::None .from_override(&target, RelationshipStatus::None)
} .await?;
.publish(user.id.clone()), let target_id = target.id.clone();
ClientboundNotification::UserRelationship {
id: target.id.clone(), ClientboundNotification::UserRelationship {
user: user.id.clone(), id: user.id.clone(),
status: RelationshipStatus::None user: target,
} status: RelationshipStatus::None,
.publish(target.id.clone()) }
) .publish(user.id.clone());
.ok();
let hive = hive::get_hive(); ClientboundNotification::UserRelationship {
hive.unsubscribe(&user.id, &target.id).ok(); id: target_id.clone(),
hive.unsubscribe(&target.id, &user.id).ok(); user,
status: RelationshipStatus::None,
}
.publish(target_id);
Ok(json!({ "status": "None" })) Ok(json!({ "status": "None" }))
} }
......
use crate::database::*; use crate::database::*;
use crate::notifications::{events::ClientboundNotification, hive}; use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result}; use crate::util::result::{Error, Result};
use futures::try_join; use futures::try_join;
use hive_pubsub::PubSub;
use mongodb::bson::doc; use mongodb::bson::doc;
use rocket_contrib::json::JsonValue; use rocket_contrib::json::JsonValue;
#[delete("/<target>/block")] #[delete("/<target>/block")]
pub async fn req(user: User, target: Ref) -> Result<JsonValue> { pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let col = get_collection("users"); let col = get_collection("users");
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.fetch_user().await?, &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": {
ClientboundNotification::UserRelationship { "relations": {
id: user.id.clone(), "_id": &user.id
user: target.id.clone(),
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
)
) {
Ok(_) => {
try_join!(
ClientboundNotification::UserRelationship {
id: user.id.clone(),
user: target.id.clone(),
status: RelationshipStatus::None
}
.publish(user.id.clone()),
ClientboundNotification::UserRelationship {
id: target.id.clone(),
user: user.id.clone(),
status: RelationshipStatus::None
} }
.publish(target.id.clone()) }
) },
.ok(); 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();
let hive = hive::get_hive(); ClientboundNotification::UserRelationship {
hive.unsubscribe(&user.id, &target.id).ok(); id: user.id.clone(),
hive.unsubscribe(&target.id, &user.id).ok(); user: target,
status: RelationshipStatus::None,
}
.publish(user.id.clone());
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),
} }
} }
use crate::util::variables::{HCAPTCHA_KEY, USE_HCAPTCHA};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize)]
struct CaptchaResponse {
success: bool,
}
pub async fn verify(user_token: &Option<String>) -> Result<(), String> {
if *USE_HCAPTCHA {
if let Some(token) = user_token {
let mut map = HashMap::new();
map.insert("secret", HCAPTCHA_KEY.to_string());
map.insert("response", token.to_string());
let client = Client::new();
if let Ok(response) = client
.post("https://hcaptcha.com/siteverify")
.form(&map)
.send()
.await
{
let result: CaptchaResponse = response
.json()
.await
.map_err(|_| "Failed to deserialise captcha result.".to_string())?;
if result.success {
Ok(())
} else {
Err("Unsuccessful captcha verification".to_string())
}
} else {
Err("Failed to verify with hCaptcha".to_string())
}
} else {
Err("Missing hCaptcha token!".to_string())
}
} else {
Ok(())
}
}
use lettre::message::{header, MultiPart, SinglePart};
use lettre::transport::smtp::authentication::Credentials;
use lettre::{Message, SmtpTransport, Transport};
use super::variables::{PUBLIC_URL, SMTP_FROM, SMTP_HOST, SMTP_PASSWORD, SMTP_USERNAME};
lazy_static! {
static ref MAILER: lettre::transport::smtp::SmtpTransport =
SmtpTransport::relay(SMTP_HOST.as_ref())
.unwrap()
.credentials(Credentials::new(
SMTP_USERNAME.to_string(),
SMTP_PASSWORD.to_string()
))
.build();
}
fn send(message: Message) -> Result<(), String> {
MAILER
.send(&message)
.map_err(|err| format!("Failed to send email! {}", err.to_string()))?;
Ok(())
}
fn generate_multipart(text: &str, html: &str) -> MultiPart {
MultiPart::mixed().multipart(
MultiPart::alternative()
.singlepart(
SinglePart::quoted_printable()
.header(header::ContentType(
"text/plain; charset=utf8".parse().unwrap(),
))
.body(text),
)
.multipart(
MultiPart::related().singlepart(
SinglePart::eight_bit()
.header(header::ContentType(
"text/html; charset=utf8".parse().unwrap(),
))
.body(html),
),
),
)
}
pub fn send_verification_email(email: String, code: String) -> Result<(), String> {
let url = format!("{}/api/account/verify/{}", PUBLIC_URL.to_string(), code);
let email = Message::builder()
.from(SMTP_FROM.to_string().parse().unwrap())
.to(email.parse().unwrap())
.subject("Verify your email!")
.multipart(generate_multipart(
&format!("Verify your email here: {}", url),
&format!("<a href=\"{}\">Click to verify your email!</a>", url),
))
.unwrap();
send(email)
}
pub fn send_password_reset(email: String, code: String) -> Result<(), String> {
let url = format!("{}/api/account/reset/{}", PUBLIC_URL.to_string(), code);
let email = Message::builder()
.from(SMTP_FROM.to_string().parse().unwrap())
.to(email.parse().unwrap())
.subject("Reset your password.")
.multipart(generate_multipart(
&format!("Reset your password here: {}", url),
&format!("<a href=\"{}\">Click to reset your password!</a>", url),
))
.unwrap();
send(email)
}
pub fn send_welcome_email(email: String, username: String) -> Result<(), String> {
let email = Message::builder()
.from(SMTP_FROM.to_string().parse().unwrap())
.to(email.parse().unwrap())
.subject("Welcome to REVOLT!")
.multipart(
generate_multipart(
&format!("Welcome, {}! You can now use REVOLT.", username),
&format!(
"<b>Welcome, {}!</b><br/>You can now use REVOLT.<br/><a href=\"{}\">Go to REVOLT</a>",
username,
PUBLIC_URL.to_string()
)
)
)
.unwrap();
send(email)
}
use rand::{distributions::Alphanumeric, Rng};
use std::collections::HashSet;
use std::iter::FromIterator;
pub mod captcha;
pub mod email;
pub mod result; pub mod result;
pub mod variables; pub mod variables;
pub fn vec_to_set<T: Clone + Eq + std::hash::Hash>(data: &[T]) -> HashSet<T> {
HashSet::from_iter(data.iter().cloned())
}
pub fn gen_token(l: usize) -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(l)
.collect::<String>()
}
...@@ -3,63 +3,63 @@ use rocket::http::{ContentType, Status}; ...@@ -3,63 +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("Cannot edit someone else's message."))] UnknownChannel,
UnknownAttachment,
UnknownMessage,
CannotEditMessage, CannotEditMessage,
#[snafu(display("Cannot remove yourself from a group, use delete channel instead."))] CannotJoinCall,
TooManyAttachments,
TooManyReplies,
EmptyMessage,
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("Failed to validate fields."))] TooManyIds,
FailedValidation { error: ValidationErrors }, FailedValidation {
#[snafu(display("Encountered a database error."))] error: ValidationErrors,
},
DatabaseError { DatabaseError {
operation: &'static str, operation: &'static str,
with: &'static str, with: &'static str,
}, },
#[snafu(display("Internal server error."))]
InternalError, InternalError,
#[snafu(display("Operation cannot be performed on this object."))] MissingPermission,
InvalidOperation, InvalidOperation,
#[snafu(display("Already created an object with this nonce."))] InvalidCredentials,
DuplicateNonce, DuplicateNonce,
#[snafu(display("This request had no effect."))] VosoUnavailable,
NotFound,
NoEffect, NoEffect,
} }
...@@ -81,17 +81,33 @@ impl<'r> Responder<'r, 'static> for Error { ...@@ -81,17 +81,33 @@ impl<'r> Responder<'r, 'static> for Error {
Error::BlockedByOther => Status::Forbidden, Error::BlockedByOther => Status::Forbidden,
Error::NotFriends => Status::Forbidden, Error::NotFriends => Status::Forbidden,
Error::UnknownChannel => Status::NotFound,
Error::UnknownMessage => Status::NotFound,
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::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,
Error::MissingPermission => Status::Forbidden,
Error::InvalidOperation => Status::BadRequest, Error::InvalidOperation => Status::BadRequest,
Error::TooManyIds => Status::BadRequest,
Error::InvalidCredentials => Status::Forbidden,
Error::DuplicateNonce => Status::Conflict, Error::DuplicateNonce => Status::Conflict,
Error::VosoUnavailable => Status::BadRequest,
Error::NotFound => Status::NotFound,
Error::NoEffect => Status::Ok, Error::NoEffect => Status::Ok,
}; };
......
...@@ -7,19 +7,37 @@ lazy_static! { ...@@ -7,19 +7,37 @@ lazy_static! {
// Application Settings // Application Settings
pub static ref MONGO_URI: String = pub static ref MONGO_URI: String =
env::var("REVOLT_MONGO_URI").expect("Missing REVOLT_MONGO_URI environment variable."); env::var("REVOLT_MONGO_URI").expect("Missing REVOLT_MONGO_URI environment variable.");
pub static ref WS_HOST: String =
env::var("REVOLT_WS_HOST").unwrap_or_else(|_| "0.0.0.0:9000".to_string());
pub static ref PUBLIC_URL: String = pub static ref PUBLIC_URL: String =
env::var("REVOLT_PUBLIC_URL").expect("Missing REVOLT_PUBLIC_URL environment variable."); env::var("REVOLT_PUBLIC_URL").expect("Missing REVOLT_PUBLIC_URL environment variable.");
pub static ref APP_URL: String =
env::var("REVOLT_APP_URL").expect("Missing REVOLT_APP_URL environment variable.");
pub static ref EXTERNAL_WS_URL: String = pub static ref EXTERNAL_WS_URL: String =
env::var("REVOLT_EXTERNAL_WS_URL").expect("Missing REVOLT_EXTERNAL_WS_URL environment variable."); env::var("REVOLT_EXTERNAL_WS_URL").expect("Missing REVOLT_EXTERNAL_WS_URL environment variable.");
pub static ref AUTUMN_URL: 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 =
env::var("VOSO_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
pub static ref VOSO_WS_HOST: String =
env::var("VOSO_WS_HOST").unwrap_or_else(|_| "wss://example.com".to_string());
pub static ref VOSO_MANAGE_TOKEN: String =
env::var("VOSO_MANAGE_TOKEN").unwrap_or_else(|_| "0".to_string());
pub static ref HCAPTCHA_KEY: String = pub static ref HCAPTCHA_KEY: String =
env::var("REVOLT_HCAPTCHA_KEY").unwrap_or_else(|_| "0x0000000000000000000000000000000000000000".to_string()); env::var("REVOLT_HCAPTCHA_KEY").unwrap_or_else(|_| "0x0000000000000000000000000000000000000000".to_string());
pub static ref HCAPTCHA_SITEKEY: String = pub static ref HCAPTCHA_SITEKEY: String =
env::var("REVOLT_HCAPTCHA_SITEKEY").unwrap_or_else(|_| "10000000-ffff-ffff-ffff-000000000001".to_string()); env::var("REVOLT_HCAPTCHA_SITEKEY").unwrap_or_else(|_| "10000000-ffff-ffff-ffff-000000000001".to_string());
pub static ref WS_HOST: String = pub static ref VAPID_PRIVATE_KEY: String =
env::var("REVOLT_WS_HOST").unwrap_or_else(|_| "0.0.0.0:9000".to_string()); env::var("REVOLT_VAPID_PRIVATE_KEY").expect("Missing REVOLT_VAPID_PRIVATE_KEY environment variable.");
pub static ref VAPID_PUBLIC_KEY: String =
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 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()
&& env::var("REVOLT_SMTP_USERNAME").is_ok() && env::var("REVOLT_SMTP_USERNAME").is_ok()
...@@ -28,6 +46,10 @@ lazy_static! { ...@@ -28,6 +46,10 @@ lazy_static! {
|v| v == *"1" |v| v == *"1"
); );
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_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();
// SMTP Settings // SMTP Settings
pub static ref SMTP_HOST: String = pub static ref SMTP_HOST: String =
...@@ -41,9 +63,19 @@ lazy_static! { ...@@ -41,9 +63,19 @@ 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() {
format!("{}", *APP_URL);
format!("{}", *MONGO_URI);
format!("{}", *PUBLIC_URL);
format!("{}", *EXTERNAL_WS_URL);
format!("{}", *VAPID_PRIVATE_KEY);
format!("{}", *VAPID_PUBLIC_KEY);
if *USE_EMAIL == false { if *USE_EMAIL == false {
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
if !env::var("REVOLT_UNSAFE_NO_EMAIL").map_or(false, |v| v == *"1") { if !env::var("REVOLT_UNSAFE_NO_EMAIL").map_or(false, |v| v == *"1") {
......
pub const VERSION: &str = "0.5.1-alpha.21";