Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e2250783c | ||
|
|
2050a0609f | ||
|
|
a547177325 | ||
|
|
c3fa7bf74e | ||
|
|
7f5d6f2312 | ||
|
|
9492e145f9 | ||
|
|
78c42fd412 | ||
|
|
3797878d20 | ||
|
|
d2bf477fd8 | ||
|
|
c9bff789de | ||
|
|
56bd763820 | ||
|
|
32cd9d8a13 | ||
|
|
60731e1c70 | ||
|
|
37df18dbf7 | ||
|
|
0a9d5de369 | ||
|
|
d2864ac025 | ||
|
|
4eb76fd154 | ||
|
|
3c7852271a | ||
|
|
dadad271b4 | ||
|
|
db6047f2d3 | ||
|
|
5baf85a8e9 | ||
|
|
10cac358a9 | ||
|
|
64d2707366 | ||
|
|
78cfbf9d21 | ||
|
|
02077a4024 | ||
|
|
52e070c21c | ||
|
|
a2f14d2d37 | ||
|
|
a7ea29d821 | ||
|
|
b6b51bca26 |
@@ -1,2 +1,3 @@
|
|||||||
target
|
target
|
||||||
|
.mongo
|
||||||
.env
|
.env
|
||||||
Generated
+66
-6
@@ -1,5 +1,7 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
@@ -243,6 +245,16 @@ version = "0.2.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
|
checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5032d51da2741729bfdaeb2664d9b8c6d9fd1e2b90715c660b6def36628499c2"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"safemem",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -686,6 +698,15 @@ dependencies = [
|
|||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "erased-serde"
|
||||||
|
version = "0.3.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0465971a8cc1fa2455c8465aaa377131e1f1cf4983280f474a13e68793aa770c"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "err-derive"
|
name = "err-derive"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@@ -1046,9 +1067,9 @@ checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hive_pubsub"
|
name = "hive_pubsub"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83760410241f6db418bb15e54b506a0887e7240286e29e5b0d2d88f5d1659b24"
|
checksum = "2ac4635b1e9faf304ccb33c2f0edbc615030e40d249183281fc788ef20ef5c76"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"many-to-many",
|
"many-to-many",
|
||||||
@@ -2454,10 +2475,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt"
|
name = "revolt"
|
||||||
version = "0.3.3-alpha.3"
|
version = "0.4.0-alpha.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
|
"base64 0.13.0",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
"chrono",
|
"chrono",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
@@ -2489,6 +2511,7 @@ dependencies = [
|
|||||||
"ulid",
|
"ulid",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"validator",
|
"validator",
|
||||||
|
"web-push",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2501,7 +2524,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"spin",
|
"spin",
|
||||||
"untrusted",
|
"untrusted 0.7.1",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
@@ -2673,6 +2696,12 @@ version = "1.0.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "safemem"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schannel"
|
name = "schannel"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
@@ -2705,7 +2734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
|
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"untrusted",
|
"untrusted 0.7.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3477,6 +3506,12 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -3678,6 +3713,31 @@ version = "0.2.70"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
|
checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-push"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8353dd6c7cfb9a02737fd6dc9a66a80dca2a93fb690f6ad264d2a7672e6f1c0"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.7.0",
|
||||||
|
"chrono",
|
||||||
|
"erased-serde",
|
||||||
|
"futures",
|
||||||
|
"http",
|
||||||
|
"hyper",
|
||||||
|
"hyper-tls",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"native-tls",
|
||||||
|
"openssl",
|
||||||
|
"ring",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"time 0.1.44",
|
||||||
|
"untrusted 0.6.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.47"
|
version = "0.3.47"
|
||||||
@@ -3695,7 +3755,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
|
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"untrusted",
|
"untrusted 0.7.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
+33
-36
@@ -1,47 +1,44 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt"
|
name = "revolt"
|
||||||
version = "0.3.3-alpha.3"
|
version = "0.4.0-alpha.6"
|
||||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = "0.3.8"
|
regex = "1"
|
||||||
impl_ops = "0.1.1"
|
md5 = "0.7.0"
|
||||||
many-to-many = "0.1.2"
|
log = "0.4.11"
|
||||||
ctrlc = { version = "3.0", features = ["termination"] }
|
|
||||||
async-std = { version = "1.8.0", features = ["tokio02", "attributes"] }
|
|
||||||
async-tungstenite = { version = "0.10.0", features = ["async-std-runtime"] }
|
|
||||||
rauth = { git = "https://gitlab.insrt.uk/insert/rauth", rev = "c52758a5087cd035b0ed9c6eacc942ba5468d2ce" }
|
|
||||||
|
|
||||||
hive_pubsub = { version = "0.4.3", features = ["mongo"] }
|
|
||||||
rocket_cors = { git = "https://github.com/insertish/rocket_cors", branch = "master" }
|
|
||||||
rocket_prometheus = { git = "https://github.com/insertish/rocket_prometheus", rev = "3d825aedb42793246c306a81fe67c5b187948983" }
|
|
||||||
rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00" }
|
|
||||||
rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00", default-features = false }
|
|
||||||
mongodb = { version = "1.1.1", features = ["tokio-runtime"], default-features = false }
|
|
||||||
|
|
||||||
once_cell = "1.4.1"
|
|
||||||
dotenv = "0.15.0"
|
|
||||||
ulid = "0.4.1"
|
ulid = "0.4.1"
|
||||||
|
rand = "0.7.3"
|
||||||
|
time = "0.2.16"
|
||||||
|
base64 = "0.13.0"
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
futures = "0.3.8"
|
||||||
|
chrono = "0.4.15"
|
||||||
|
num_enum = "0.5.1"
|
||||||
|
impl_ops = "0.1.1"
|
||||||
|
web-push = "0.7.2"
|
||||||
|
once_cell = "1.4.1"
|
||||||
|
bitfield = "0.13.2"
|
||||||
|
env_logger = "0.7.1"
|
||||||
|
serde_json = "1.0.57"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
urlencoding = "1.1.1"
|
||||||
|
many-to-many = "0.1.2"
|
||||||
|
lettre = "0.10.0-alpha.1"
|
||||||
|
snafu = { version = "0.6.9" }
|
||||||
|
reqwest = { version = "0.10.8", features = ["json"] }
|
||||||
serde = { version = "1.0.115", features = ["derive"] }
|
serde = { version = "1.0.115", features = ["derive"] }
|
||||||
validator = { version = "0.11", features = ["derive"] }
|
validator = { version = "0.11", features = ["derive"] }
|
||||||
snafu = { version = "0.6.9" }
|
ctrlc = { version = "3.0", features = ["termination"] }
|
||||||
serde_json = "1.0.57"
|
hive_pubsub = { version = "0.4.4", features = ["mongo"] }
|
||||||
bitfield = "0.13.2"
|
async-std = { version = "1.8.0", features = ["tokio02", "attributes"] }
|
||||||
|
async-tungstenite = { version = "0.10.0", features = ["async-std-runtime"] }
|
||||||
reqwest = { version = "0.10.8", features = ["json"] }
|
rocket_cors = { git = "https://github.com/insertish/rocket_cors", branch = "master" }
|
||||||
urlencoding = "1.1.1"
|
mongodb = { version = "1.1.1", features = ["tokio-runtime"], default-features = false }
|
||||||
lazy_static = "1.4.0"
|
rauth = { git = "https://gitlab.insrt.uk/insert/rauth", rev = "c52758a5087cd035b0ed9c6eacc942ba5468d2ce" }
|
||||||
num_enum = "0.5.1"
|
rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00" }
|
||||||
chrono = "0.4.15"
|
rocket_prometheus = { git = "https://github.com/insertish/rocket_prometheus", rev = "3d825aedb42793246c306a81fe67c5b187948983" }
|
||||||
time = "0.2.16"
|
rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00", default-features = false }
|
||||||
rand = "0.7.3"
|
|
||||||
md5 = "0.7.0"
|
|
||||||
regex = "1"
|
|
||||||
|
|
||||||
lettre = "0.10.0-alpha.1"
|
|
||||||
env_logger = "0.7.1"
|
|
||||||
log = "0.4.11"
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ekidd/rust-musl-builder:nightly-2021-01-01 AS builder
|
FROM ekidd/rust-musl-builder:nightly-2021-01-01 AS builder
|
||||||
|
USER 0:0
|
||||||
WORKDIR /home/rust/src
|
WORKDIR /home/rust/src
|
||||||
|
|
||||||
RUN USER=root cargo new --bin revolt
|
RUN USER=root cargo new --bin revolt
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
enum Metadata {
|
||||||
|
File,
|
||||||
|
Image { width: isize, height: isize },
|
||||||
|
Video { width: isize, height: isize },
|
||||||
|
Audio,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct File {
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: String,
|
||||||
|
filename: String,
|
||||||
|
metadata: Metadata,
|
||||||
|
content_type: String,
|
||||||
|
|
||||||
|
message_id: Option<String>,
|
||||||
|
}
|
||||||
@@ -46,9 +46,9 @@ pub enum Channel {
|
|||||||
impl Channel {
|
impl Channel {
|
||||||
pub fn id(&self) -> &str {
|
pub fn id(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Channel::SavedMessages { id, .. } => id,
|
Channel::SavedMessages { id, .. }
|
||||||
Channel::DirectMessage { id, .. } => id,
|
| Channel::DirectMessage { id, .. }
|
||||||
Channel::Group { id, .. } => id,
|
| Channel::Group { id, .. } => id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,49 @@
|
|||||||
|
use crate::util::variables::VAPID_PRIVATE_KEY;
|
||||||
use crate::{
|
use crate::{
|
||||||
database::*,
|
database::*,
|
||||||
notifications::events::ClientboundNotification,
|
notifications::{events::ClientboundNotification, websocket::is_online},
|
||||||
util::result::{Error, Result},
|
util::result::{Error, Result},
|
||||||
};
|
};
|
||||||
use mongodb::bson::{doc, to_bson, DateTime};
|
|
||||||
|
use futures::StreamExt;
|
||||||
|
use mongodb::{
|
||||||
|
bson::{doc, to_bson, DateTime},
|
||||||
|
options::FindOptions,
|
||||||
|
};
|
||||||
use rocket_contrib::json::JsonValue;
|
use rocket_contrib::json::JsonValue;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
use web_push::{
|
||||||
|
ContentEncoding, SubscriptionInfo, VapidSignatureBuilder, WebPushClient, WebPushMessageBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum MessageEmbed {
|
||||||
|
Dummy,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum SystemMessage {
|
||||||
|
#[serde(rename = "text")]
|
||||||
|
Text { content: String },
|
||||||
|
#[serde(rename = "user_added")]
|
||||||
|
UserAdded { id: String, by: String },
|
||||||
|
#[serde(rename = "user_remove")]
|
||||||
|
UserRemove { id: String, by: String },
|
||||||
|
#[serde(rename = "user_left")]
|
||||||
|
UserLeft { id: String },
|
||||||
|
#[serde(rename = "channel_renamed")]
|
||||||
|
ChannelRenamed { name: String, by: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Content {
|
||||||
|
Text(String),
|
||||||
|
SystemMessage(SystemMessage),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
@@ -17,13 +54,17 @@ pub struct Message {
|
|||||||
pub channel: String,
|
pub channel: String,
|
||||||
pub author: String,
|
pub author: String,
|
||||||
|
|
||||||
pub content: String,
|
pub content: Content,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub attachment: Option<File>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub edited: Option<DateTime>,
|
pub edited: Option<DateTime>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub embeds: Option<MessageEmbed>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
pub fn create(author: String, channel: String, content: String) -> Message {
|
pub fn create(author: String, channel: String, content: Content) -> Message {
|
||||||
Message {
|
Message {
|
||||||
id: Ulid::new().to_string(),
|
id: Ulid::new().to_string(),
|
||||||
nonce: None,
|
nonce: None,
|
||||||
@@ -31,7 +72,9 @@ impl Message {
|
|||||||
author,
|
author,
|
||||||
|
|
||||||
content,
|
content,
|
||||||
|
attachment: None,
|
||||||
edited: None,
|
edited: None,
|
||||||
|
embeds: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,22 +87,28 @@ impl Message {
|
|||||||
with: "message",
|
with: "message",
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let mut set = if let Content::Text(text) = &self.content {
|
||||||
|
doc! {
|
||||||
|
"last_message": {
|
||||||
|
"_id": self.id.clone(),
|
||||||
|
"author": self.author.clone(),
|
||||||
|
"short": text.chars().take(24).collect::<String>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
doc! {}
|
||||||
|
};
|
||||||
|
|
||||||
// ! FIXME: temp code
|
// ! FIXME: temp code
|
||||||
let channels = get_collection("channels");
|
let channels = get_collection("channels");
|
||||||
match channel {
|
match &channel {
|
||||||
Channel::DirectMessage { id, .. } => {
|
Channel::DirectMessage { id, .. } => {
|
||||||
|
set.insert("active", true);
|
||||||
channels
|
channels
|
||||||
.update_one(
|
.update_one(
|
||||||
doc! { "_id": id },
|
doc! { "_id": id },
|
||||||
doc! {
|
doc! {
|
||||||
"$set": {
|
"$set": set
|
||||||
"active": true,
|
|
||||||
"last_message": {
|
|
||||||
"_id": self.id.clone(),
|
|
||||||
"author": self.author.clone(),
|
|
||||||
"short": self.content.chars().take(24).collect::<String>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -70,33 +119,104 @@ impl Message {
|
|||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
Channel::Group { id, .. } => {
|
Channel::Group { id, .. } => {
|
||||||
channels
|
if let Content::Text(_) = &self.content {
|
||||||
.update_one(
|
channels
|
||||||
doc! { "_id": id },
|
.update_one(
|
||||||
doc! {
|
doc! { "_id": id },
|
||||||
"$set": {
|
doc! {
|
||||||
"last_message": {
|
"$set": set
|
||||||
"_id": self.id.clone(),
|
},
|
||||||
"author": self.author.clone(),
|
None,
|
||||||
"short": self.content.chars().take(24).collect::<String>()
|
)
|
||||||
}
|
.await
|
||||||
}
|
.map_err(|_| Error::DatabaseError {
|
||||||
},
|
operation: "update_one",
|
||||||
None,
|
with: "channel",
|
||||||
)
|
})?;
|
||||||
.await
|
}
|
||||||
.map_err(|_| Error::DatabaseError {
|
|
||||||
operation: "update_one",
|
|
||||||
with: "channel",
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let enc = serde_json::to_string(&self).unwrap();
|
||||||
ClientboundNotification::Message(self)
|
ClientboundNotification::Message(self)
|
||||||
.publish(channel.id().to_string())
|
.publish(channel.id().to_string())
|
||||||
.await
|
.await
|
||||||
.ok();
|
.unwrap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Web Push Test Code
|
||||||
|
! FIXME: temp code
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Find all offline users.
|
||||||
|
let mut target_ids = vec![];
|
||||||
|
match &channel {
|
||||||
|
Channel::DirectMessage { recipients, .. } | Channel::Group { recipients, .. } => {
|
||||||
|
for recipient in recipients {
|
||||||
|
if !is_online(recipient) {
|
||||||
|
target_ids.push(recipient.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch their corresponding sessions.
|
||||||
|
if let Ok(mut cursor) = get_collection("accounts")
|
||||||
|
.find(
|
||||||
|
doc! {
|
||||||
|
"_id": {
|
||||||
|
"$in": target_ids
|
||||||
|
},
|
||||||
|
"sessions.subscription": {
|
||||||
|
"$exists": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FindOptions::builder()
|
||||||
|
.projection(doc! { "sessions": 1 })
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
let mut subscriptions = vec![];
|
||||||
|
while let Some(result) = cursor.next().await {
|
||||||
|
if let Ok(doc) = result {
|
||||||
|
if let Ok(sessions) = doc.get_array("sessions") {
|
||||||
|
for session in sessions {
|
||||||
|
if let Some(doc) = session.as_document() {
|
||||||
|
if let Ok(sub) = doc.get_document("subscription") {
|
||||||
|
let endpoint = sub.get_str("endpoint").unwrap().to_string();
|
||||||
|
let p256dh = sub.get_str("p256dh").unwrap().to_string();
|
||||||
|
let auth = sub.get_str("auth").unwrap().to_string();
|
||||||
|
|
||||||
|
subscriptions
|
||||||
|
.push(SubscriptionInfo::new(endpoint, p256dh, auth));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if subscriptions.len() > 0 {
|
||||||
|
let client = WebPushClient::new();
|
||||||
|
let key =
|
||||||
|
base64::decode_config(VAPID_PRIVATE_KEY.clone(), base64::URL_SAFE).unwrap();
|
||||||
|
|
||||||
|
for subscription in subscriptions {
|
||||||
|
let mut builder = WebPushMessageBuilder::new(&subscription).unwrap();
|
||||||
|
let sig_builder =
|
||||||
|
VapidSignatureBuilder::from_pem(std::io::Cursor::new(&key), &subscription)
|
||||||
|
.unwrap();
|
||||||
|
let signature = sig_builder.build().unwrap();
|
||||||
|
builder.set_vapid_signature(signature);
|
||||||
|
builder.set_payload(ContentEncoding::AesGcm, enc.as_bytes());
|
||||||
|
let m = builder.build().unwrap();
|
||||||
|
client.send(m).await.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -115,6 +235,26 @@ impl Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(&self) -> Result<()> {
|
pub async fn delete(&self) -> Result<()> {
|
||||||
|
if let Some(attachment) = &self.attachment {
|
||||||
|
get_collection("attachments")
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": &attachment.id
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"deleted": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "update_one",
|
||||||
|
with: "attachment",
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
get_collection("messages")
|
get_collection("messages")
|
||||||
.delete_one(
|
.delete_one(
|
||||||
doc! {
|
doc! {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
mod autumn;
|
||||||
mod channel;
|
mod channel;
|
||||||
mod guild;
|
mod guild;
|
||||||
mod message;
|
mod message;
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
|
pub use autumn::*;
|
||||||
pub use channel::*;
|
pub use channel::*;
|
||||||
pub use guild::*;
|
pub use guild::*;
|
||||||
pub use message::*;
|
pub use message::*;
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
|
use mongodb::bson::doc;
|
||||||
|
use mongodb::options::{Collation, FindOneOptions};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{database::permissions::user::UserPermissions, notifications::websocket::is_online};
|
use crate::database::permissions::user::UserPermissions;
|
||||||
|
use crate::database::*;
|
||||||
|
use crate::notifications::websocket::is_online;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
pub enum RelationshipStatus {
|
pub enum RelationshipStatus {
|
||||||
@@ -20,6 +26,37 @@ pub struct Relationship {
|
|||||||
pub status: RelationshipStatus,
|
pub status: RelationshipStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub enum Badge {
|
||||||
|
Developer = 1,
|
||||||
|
Translator = 2,
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub enum Presence {
|
||||||
|
Online,
|
||||||
|
Idle,
|
||||||
|
Busy,
|
||||||
|
Invisible
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Validate, Serialize, Deserialize, Debug)]
|
||||||
|
pub struct UserStatus {
|
||||||
|
#[validate(length(min = 1, max = 128))]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
text: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
presence: Option<Presence>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Validate, Serialize, Deserialize, Debug)]
|
||||||
|
pub struct UserProfile {
|
||||||
|
#[validate(length(min = 1, max = 2000))]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
content: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
#[serde(rename = "_id")]
|
#[serde(rename = "_id")]
|
||||||
@@ -28,6 +65,13 @@ pub struct User {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub relations: Option<Vec<Relationship>>,
|
pub relations: Option<Vec<Relationship>>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub badges: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub status: Option<UserStatus>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub profile: Option<UserProfile>,
|
||||||
|
|
||||||
// ? This should never be pushed to the collection.
|
// ? This should never be pushed to the collection.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub relationship: Option<RelationshipStatus>,
|
pub relationship: Option<RelationshipStatus>,
|
||||||
@@ -60,6 +104,35 @@ impl User {
|
|||||||
self.online = Some(is_online(&self.id));
|
self.online = Some(is_online(&self.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.profile = None;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Utility function for checking claimed usernames.
|
||||||
|
pub async fn is_username_taken(username: &str) -> Result<bool> {
|
||||||
|
if username.to_lowercase() == "revolt" && username.to_lowercase() == "admin" {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if get_collection("users")
|
||||||
|
.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",
|
||||||
|
})?
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ pub enum ChannelPermission {
|
|||||||
View = 1,
|
View = 1,
|
||||||
SendMessage = 2,
|
SendMessage = 2,
|
||||||
ManageMessages = 4,
|
ManageMessages = 4,
|
||||||
|
ManageChannel = 8,
|
||||||
|
VoiceCall = 16,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
@@ -20,6 +22,8 @@ bitfield! {
|
|||||||
pub get_view, _: 31;
|
pub get_view, _: 31;
|
||||||
pub get_send_message, _: 30;
|
pub get_send_message, _: 30;
|
||||||
pub get_manage_messages, _: 29;
|
pub get_manage_messages, _: 29;
|
||||||
|
pub get_manage_channel, _: 28;
|
||||||
|
pub get_voice_call, _: 27;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_op_ex!(+ |a: &ChannelPermission, b: &ChannelPermission| -> u32 { *a as u32 | *b as u32 });
|
impl_op_ex!(+ |a: &ChannelPermission, b: &ChannelPermission| -> u32 { *a as u32 | *b as u32 });
|
||||||
@@ -36,9 +40,7 @@ impl<'a> PermissionCalculator<'a> {
|
|||||||
match channel {
|
match channel {
|
||||||
Channel::SavedMessages { user: owner, .. } => {
|
Channel::SavedMessages { user: owner, .. } => {
|
||||||
if &self.perspective.id == owner {
|
if &self.perspective.id == owner {
|
||||||
Ok(ChannelPermission::View
|
Ok(u32::MAX)
|
||||||
+ ChannelPermission::SendMessage
|
|
||||||
+ ChannelPermission::ManageMessages)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
@@ -54,7 +56,9 @@ impl<'a> PermissionCalculator<'a> {
|
|||||||
let perms = self.for_user(recipient).await?;
|
let perms = self.for_user(recipient).await?;
|
||||||
|
|
||||||
if perms.get_send_message() {
|
if perms.get_send_message() {
|
||||||
return Ok(ChannelPermission::View + ChannelPermission::SendMessage);
|
return Ok(ChannelPermission::View
|
||||||
|
+ ChannelPermission::SendMessage
|
||||||
|
+ ChannelPermission::VoiceCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(ChannelPermission::View as u32);
|
return Ok(ChannelPermission::View as u32);
|
||||||
@@ -69,7 +73,10 @@ impl<'a> PermissionCalculator<'a> {
|
|||||||
.find(|x| *x == &self.perspective.id)
|
.find(|x| *x == &self.perspective.id)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
Ok(ChannelPermission::View + ChannelPermission::SendMessage)
|
Ok(ChannelPermission::View
|
||||||
|
+ ChannelPermission::SendMessage
|
||||||
|
+ ChannelPermission::ManageChannel
|
||||||
|
+ ChannelPermission::VoiceCall)
|
||||||
} else {
|
} else {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub struct PermissionCalculator<'a> {
|
|||||||
user: Option<&'a User>,
|
user: Option<&'a User>,
|
||||||
channel: Option<&'a Channel>,
|
channel: Option<&'a Channel>,
|
||||||
|
|
||||||
has_mutual_conncetion: bool,
|
has_mutual_connection: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PermissionCalculator<'a> {
|
impl<'a> PermissionCalculator<'a> {
|
||||||
@@ -22,7 +22,7 @@ impl<'a> PermissionCalculator<'a> {
|
|||||||
user: None,
|
user: None,
|
||||||
channel: None,
|
channel: None,
|
||||||
|
|
||||||
has_mutual_conncetion: false,
|
has_mutual_connection: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ impl<'a> PermissionCalculator<'a> {
|
|||||||
|
|
||||||
pub fn with_mutual_connection(self) -> PermissionCalculator<'a> {
|
pub fn with_mutual_connection(self) -> PermissionCalculator<'a> {
|
||||||
PermissionCalculator {
|
PermissionCalculator {
|
||||||
has_mutual_conncetion: true,
|
has_mutual_connection: true,
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,18 +64,16 @@ impl<'a> PermissionCalculator<'a> {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.has_mutual_conncetion
|
if self.has_mutual_connection
|
||||||
|| get_collection("channels")
|
|| get_collection("channels")
|
||||||
.find_one(
|
.find_one(
|
||||||
doc! {
|
doc! {
|
||||||
"$or": [
|
"channel_type": {
|
||||||
{ "type": "Group" },
|
"$in": ["Group", "DirectMessage"]
|
||||||
{ "type": "DirectMessage" },
|
},
|
||||||
],
|
"recipients": {
|
||||||
"$and": [
|
"$all": [ &self.perspective.id, target ]
|
||||||
{ "recipients": &self.perspective.id },
|
}
|
||||||
{ "recipients": target }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -86,7 +84,8 @@ impl<'a> PermissionCalculator<'a> {
|
|||||||
})?
|
})?
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
return Ok(UserPermission::Access as u32);
|
// ! FIXME: add privacy settings
|
||||||
|
return Ok(UserPermission::Access + UserPermission::ViewProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(permissions)
|
Ok(permissions)
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ pub enum WebSocketError {
|
|||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum ServerboundNotification {
|
pub enum ServerboundNotification {
|
||||||
Authenticate(Session),
|
Authenticate(Session),
|
||||||
|
BeginTyping { channel: String },
|
||||||
|
EndTyping { channel: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@@ -63,7 +65,19 @@ pub enum ClientboundNotification {
|
|||||||
ChannelDelete {
|
ChannelDelete {
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
|
ChannelStartTyping {
|
||||||
|
id: String,
|
||||||
|
user: String,
|
||||||
|
},
|
||||||
|
ChannelStopTyping {
|
||||||
|
id: String,
|
||||||
|
user: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
UserUpdate {
|
||||||
|
id: String,
|
||||||
|
data: JsonValue,
|
||||||
|
},
|
||||||
UserRelationship {
|
UserRelationship {
|
||||||
id: String,
|
id: String,
|
||||||
user: String,
|
user: String,
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
FindOptions::builder()
|
FindOptions::builder()
|
||||||
.projection(doc! { "_id": 1, "username": 1 })
|
.projection(doc! { "_id": 1, "username": 1, "badges": 1, "status": 1 })
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -71,8 +71,6 @@ async fn accept(stream: TcpStream) {
|
|||||||
let fwd = rx.map(Ok).forward(write);
|
let fwd = rx.map(Ok).forward(write);
|
||||||
let incoming = read.try_for_each(async move |msg| {
|
let incoming = read.try_for_each(async move |msg| {
|
||||||
let mutex = mutex_generator();
|
let mutex = mutex_generator();
|
||||||
//dbg!(&mutex.lock().unwrap());
|
|
||||||
|
|
||||||
if let Message::Text(text) = msg {
|
if let Message::Text(text) = msg {
|
||||||
if let Ok(notification) = serde_json::from_str::<ServerboundNotification>(&text) {
|
if let Ok(notification) = serde_json::from_str::<ServerboundNotification>(&text) {
|
||||||
match notification {
|
match notification {
|
||||||
@@ -161,6 +159,54 @@ async fn accept(stream: TcpStream) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ! TEMP: verify user part of channel
|
||||||
|
// ! Could just run permission check here.
|
||||||
|
ServerboundNotification::BeginTyping { channel } => {
|
||||||
|
if mutex.lock().unwrap().is_some() {
|
||||||
|
let user = {
|
||||||
|
let mutex = mutex.lock().unwrap();
|
||||||
|
let session = mutex.as_ref().unwrap();
|
||||||
|
session.user_id.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
ClientboundNotification::ChannelStartTyping {
|
||||||
|
id: channel.clone(),
|
||||||
|
user,
|
||||||
|
}
|
||||||
|
.publish(channel)
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
} else {
|
||||||
|
send(ClientboundNotification::Error(
|
||||||
|
WebSocketError::AlreadyAuthenticated,
|
||||||
|
));
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ServerboundNotification::EndTyping { channel } => {
|
||||||
|
if mutex.lock().unwrap().is_some() {
|
||||||
|
let user = {
|
||||||
|
let mutex = mutex.lock().unwrap();
|
||||||
|
let session = mutex.as_ref().unwrap();
|
||||||
|
session.user_id.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
ClientboundNotification::ChannelStopTyping {
|
||||||
|
id: channel.clone(),
|
||||||
|
user,
|
||||||
|
}
|
||||||
|
.publish(channel)
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
} else {
|
||||||
|
send(ClientboundNotification::Error(
|
||||||
|
WebSocketError::AlreadyAuthenticated,
|
||||||
|
));
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub async fn req(user: User, target: Ref) -> Result<()> {
|
|||||||
.for_channel()
|
.for_channel()
|
||||||
.await?;
|
.await?;
|
||||||
if !perm.get_view() {
|
if !perm.get_view() {
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
match &target {
|
match &target {
|
||||||
@@ -102,7 +102,7 @@ pub async fn req(user: User, target: Ref) -> Result<()> {
|
|||||||
Message::create(
|
Message::create(
|
||||||
"00000000000000000000000000".to_string(),
|
"00000000000000000000000000".to_string(),
|
||||||
id.clone(),
|
id.clone(),
|
||||||
format!("<@{}> left the group.", user.id),
|
Content::SystemMessage(SystemMessage::UserLeft { id: user.id }),
|
||||||
)
|
)
|
||||||
.publish(&target)
|
.publish(&target)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
use crate::database::*;
|
||||||
|
use crate::notifications::events::ClientboundNotification;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
|
||||||
|
use mongodb::bson::{doc, 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))]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
name: Option<String>,
|
||||||
|
#[validate(length(min = 0, max = 1024))]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[patch("/<target>", data = "<info>")]
|
||||||
|
pub async fn req(user: User, target: Ref, info: Json<Data>) -> Result<()> {
|
||||||
|
info.validate()
|
||||||
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
if info.name.is_none() && info.description.is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
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, .. } => {
|
||||||
|
get_collection("channels")
|
||||||
|
.update_one(
|
||||||
|
doc! { "_id": &id },
|
||||||
|
doc! { "$set": to_document(&info.0).map_err(|_| Error::DatabaseError { operation: "to_document", with: "data" })? },
|
||||||
|
None
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError { operation: "update_one", with: "channel" })?;
|
||||||
|
|
||||||
|
ClientboundNotification::ChannelUpdate {
|
||||||
|
id: id.clone(),
|
||||||
|
data: json!(info.0),
|
||||||
|
}
|
||||||
|
.publish(id.clone())
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
if let Some(name) = &info.name {
|
||||||
|
Message::create(
|
||||||
|
"00000000000000000000000000".to_string(),
|
||||||
|
id.clone(),
|
||||||
|
Content::SystemMessage(SystemMessage::ChannelRenamed {
|
||||||
|
name: name.clone(),
|
||||||
|
by: user.id,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.publish(&target)
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(Error::InvalidOperation),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
|
|||||||
.for_channel()
|
.for_channel()
|
||||||
.await?;
|
.await?;
|
||||||
if !perm.get_view() {
|
if !perm.get_view() {
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(json!(target))
|
Ok(json!(target))
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
|
|||||||
.for_channel()
|
.for_channel()
|
||||||
.await?;
|
.await?;
|
||||||
if !perm.get_view() {
|
if !perm.get_view() {
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Channel::Group { id, recipients, .. } = &channel {
|
if let Channel::Group { id, recipients, .. } = &channel {
|
||||||
@@ -59,7 +59,10 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
|
|||||||
Message::create(
|
Message::create(
|
||||||
"00000000000000000000000000".to_string(),
|
"00000000000000000000000000".to_string(),
|
||||||
id.clone(),
|
id.clone(),
|
||||||
format!("<@{}> added <@{}> to the group.", user.id, member.id),
|
Content::SystemMessage(SystemMessage::UserAdded {
|
||||||
|
id: member.id,
|
||||||
|
by: user.id,
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.publish(&channel)
|
.publish(&channel)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
|
|||||||
{
|
{
|
||||||
if &user.id != owner {
|
if &user.id != owner {
|
||||||
// figure out if we want to use perm system here
|
// figure out if we want to use perm system here
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
if recipients.iter().find(|x| *x == &member.id).is_none() {
|
if recipients.iter().find(|x| *x == &member.id).is_none() {
|
||||||
@@ -56,7 +56,10 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
|
|||||||
Message::create(
|
Message::create(
|
||||||
"00000000000000000000000000".to_string(),
|
"00000000000000000000000000".to_string(),
|
||||||
id.clone(),
|
id.clone(),
|
||||||
format!("<@{}> removed <@{}> from the group.", user.id, member.id),
|
Content::SystemMessage(SystemMessage::UserRemove {
|
||||||
|
id: member.id,
|
||||||
|
by: user.id,
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.publish(&channel)
|
.publish(&channel)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
use crate::database::*;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
use crate::util::variables::{USE_VOSO, VOSO_MANAGE_TOKEN, VOSO_URL};
|
||||||
|
|
||||||
|
use rocket_contrib::json::JsonValue;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct CreateUserResponse {
|
||||||
|
token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/<target>/join_call")]
|
||||||
|
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
|
||||||
|
if !*USE_VOSO {
|
||||||
|
return Err(Error::VosoUnavailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
let target = target.fetch_channel().await?;
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> {
|
|||||||
.for_channel()
|
.for_channel()
|
||||||
.await?;
|
.await?;
|
||||||
if !perm.get_view() {
|
if !perm.get_view() {
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = msg.fetch_message(&channel).await?;
|
let message = msg.fetch_message(&channel).await?;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
|
|||||||
.for_channel()
|
.for_channel()
|
||||||
.await?;
|
.await?;
|
||||||
if !perm.get_view() {
|
if !perm.get_view() {
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = msg.fetch_message(&channel).await?;
|
let message = msg.fetch_message(&channel).await?;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub async fn req(user: User, target: Ref, msg: Ref) -> Result<JsonValue> {
|
|||||||
.for_channel()
|
.for_channel()
|
||||||
.await?;
|
.await?;
|
||||||
if !perm.get_view() {
|
if !perm.get_view() {
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = msg.fetch_message(&channel).await?;
|
let message = msg.fetch_message(&channel).await?;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
|
|||||||
.for_channel()
|
.for_channel()
|
||||||
.await?;
|
.await?;
|
||||||
if !perm.get_view() {
|
if !perm.get_view() {
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut query = doc! { "channel": target.id() };
|
let mut query = doc! { "channel": target.id() };
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
use crate::database::*;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
|
||||||
|
use futures::StreamExt;
|
||||||
|
use mongodb::bson::{doc, from_document};
|
||||||
|
use rocket_contrib::json::{Json, JsonValue};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Options {
|
||||||
|
ids: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/<target>/messages/stale", data = "<data>")]
|
||||||
|
pub async fn req(user: User, target: Ref, data: Json<Options>) -> Result<JsonValue> {
|
||||||
|
if data.ids.len() > 150 {
|
||||||
|
return Err(Error::TooManyIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::util::result::{Error, Result};
|
use crate::util::result::{Error, Result};
|
||||||
|
|
||||||
use mongodb::bson::doc;
|
use mongodb::{
|
||||||
|
bson::{doc, from_document},
|
||||||
|
options::FindOneOptions,
|
||||||
|
};
|
||||||
use rocket_contrib::json::{Json, JsonValue};
|
use rocket_contrib::json::{Json, JsonValue};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
@@ -9,11 +12,13 @@ use validator::Validate;
|
|||||||
|
|
||||||
#[derive(Validate, Serialize, Deserialize)]
|
#[derive(Validate, Serialize, Deserialize)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
#[validate(length(min = 1, max = 2000))]
|
#[validate(length(min = 0, max = 2000))]
|
||||||
content: String,
|
content: String,
|
||||||
// Maximum length of 36 allows both ULIDs and UUIDs.
|
// Maximum length of 36 allows both ULIDs and UUIDs.
|
||||||
#[validate(length(min = 1, max = 36))]
|
#[validate(length(min = 1, max = 36))]
|
||||||
nonce: String,
|
nonce: String,
|
||||||
|
#[validate(length(min = 1, max = 128))]
|
||||||
|
attachment: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/<target>/messages", data = "<message>")]
|
#[post("/<target>/messages", data = "<message>")]
|
||||||
@@ -22,14 +27,18 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
|
|||||||
.validate()
|
.validate()
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
let target = target.fetch_channel().await?;
|
if message.content.len() == 0 && message.attachment.is_none() {
|
||||||
|
return Err(Error::EmptyMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
let target = target.fetch_channel().await?;
|
||||||
let perm = permissions::PermissionCalculator::new(&user)
|
let perm = permissions::PermissionCalculator::new(&user)
|
||||||
.with_channel(&target)
|
.with_channel(&target)
|
||||||
.for_channel()
|
.for_channel()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !perm.get_send_message() {
|
if !perm.get_send_message() {
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
if get_collection("messages")
|
if get_collection("messages")
|
||||||
@@ -37,7 +46,9 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
|
|||||||
doc! {
|
doc! {
|
||||||
"nonce": &message.nonce
|
"nonce": &message.nonce
|
||||||
},
|
},
|
||||||
None,
|
FindOneOptions::builder()
|
||||||
|
.projection(doc! { "_id": 1 })
|
||||||
|
.build(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::DatabaseError {
|
.map_err(|_| Error::DatabaseError {
|
||||||
@@ -49,14 +60,66 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
|
|||||||
Err(Error::DuplicateNonce)?
|
Err(Error::DuplicateNonce)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let id = Ulid::new().to_string();
|
||||||
|
let attachments = get_collection("attachments");
|
||||||
|
let attachment = if let Some(attachment_id) = &message.attachment {
|
||||||
|
if let Some(doc) = attachments
|
||||||
|
.find_one(
|
||||||
|
doc! {
|
||||||
|
"_id": attachment_id,
|
||||||
|
"message_id": {
|
||||||
|
"$exists": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "find_one",
|
||||||
|
with: "attachment",
|
||||||
|
})?
|
||||||
|
{
|
||||||
|
let attachment = from_document::<File>(doc).map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "from_document",
|
||||||
|
with: "attachment",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
attachments
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": &attachment.id
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"message_id": &id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "update_one",
|
||||||
|
with: "attachment",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Some(attachment)
|
||||||
|
} else {
|
||||||
|
return Err(Error::UnknownAttachment);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let msg = Message {
|
let msg = Message {
|
||||||
id: Ulid::new().to_string(),
|
id,
|
||||||
channel: target.id().to_string(),
|
channel: target.id().to_string(),
|
||||||
author: user.id,
|
author: user.id,
|
||||||
|
|
||||||
content: message.content.clone(),
|
content: Content::Text(message.content.clone()),
|
||||||
|
attachment,
|
||||||
nonce: Some(message.nonce.clone()),
|
nonce: Some(message.nonce.clone()),
|
||||||
edited: None,
|
edited: None,
|
||||||
|
embeds: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
msg.clone().publish(&target).await?;
|
msg.clone().publish(&target).await?;
|
||||||
|
|||||||
@@ -1,27 +1,33 @@
|
|||||||
use rocket::Route;
|
use rocket::Route;
|
||||||
|
|
||||||
mod delete_channel;
|
mod delete_channel;
|
||||||
|
mod edit_channel;
|
||||||
mod fetch_channel;
|
mod fetch_channel;
|
||||||
mod group_add_member;
|
mod group_add_member;
|
||||||
mod group_create;
|
mod group_create;
|
||||||
mod group_remove_member;
|
mod group_remove_member;
|
||||||
|
mod join_call;
|
||||||
mod message_delete;
|
mod message_delete;
|
||||||
mod message_edit;
|
mod message_edit;
|
||||||
mod message_fetch;
|
mod message_fetch;
|
||||||
mod message_query;
|
mod message_query;
|
||||||
|
mod message_query_stale;
|
||||||
mod message_send;
|
mod message_send;
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![
|
routes![
|
||||||
fetch_channel::req,
|
fetch_channel::req,
|
||||||
delete_channel::req,
|
delete_channel::req,
|
||||||
|
edit_channel::req,
|
||||||
message_send::req,
|
message_send::req,
|
||||||
message_query::req,
|
message_query::req,
|
||||||
|
message_query_stale::req,
|
||||||
message_fetch::req,
|
message_fetch::req,
|
||||||
message_edit::req,
|
message_edit::req,
|
||||||
message_delete::req,
|
message_delete::req,
|
||||||
group_create::req,
|
group_create::req,
|
||||||
group_add_member::req,
|
group_add_member::req,
|
||||||
group_remove_member::req
|
group_remove_member::req,
|
||||||
|
join_call::req,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use rocket::Rocket;
|
|||||||
mod channels;
|
mod channels;
|
||||||
mod guild;
|
mod guild;
|
||||||
mod onboard;
|
mod onboard;
|
||||||
|
mod push;
|
||||||
mod root;
|
mod root;
|
||||||
mod users;
|
mod users;
|
||||||
|
|
||||||
@@ -15,4 +16,5 @@ pub fn mount(rocket: Rocket) -> Rocket {
|
|||||||
.mount("/users", users::routes())
|
.mount("/users", users::routes())
|
||||||
.mount("/channels", channels::routes())
|
.mount("/channels", channels::routes())
|
||||||
.mount("/guild", guild::routes())
|
.mount("/guild", guild::routes())
|
||||||
|
.mount("/push", push::routes())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use crate::database::*;
|
|||||||
use crate::util::result::{Error, Result};
|
use crate::util::result::{Error, Result};
|
||||||
|
|
||||||
use mongodb::bson::doc;
|
use mongodb::bson::doc;
|
||||||
use mongodb::options::{Collation, FindOneOptions};
|
|
||||||
use rauth::auth::Session;
|
use rauth::auth::Session;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
@@ -10,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RE_USERNAME: Regex = Regex::new(r"^[a-zA-Z0-9_]+$").unwrap();
|
static ref RE_USERNAME: Regex = Regex::new(r"^[a-zA-Z0-9_.]+$").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Validate, Serialize, Deserialize)]
|
#[derive(Validate, Serialize, Deserialize)]
|
||||||
@@ -28,42 +27,23 @@ pub async fn req(session: Session, user: Option<User>, data: Json<Data>) -> Resu
|
|||||||
data.validate()
|
data.validate()
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
if data.username == "revolt" {
|
if User::is_username_taken(&data.username).await? {
|
||||||
Err(Error::UsernameTaken)?
|
return Err(Error::UsernameTaken);
|
||||||
}
|
}
|
||||||
|
|
||||||
let col = get_collection("users");
|
get_collection("users")
|
||||||
if col
|
.insert_one(
|
||||||
.find_one(
|
|
||||||
doc! {
|
doc! {
|
||||||
|
"_id": session.user_id,
|
||||||
"username": &data.username
|
"username": &data.username
|
||||||
},
|
},
|
||||||
FindOneOptions::builder()
|
None,
|
||||||
.collation(Collation::builder().locale("en").strength(2).build())
|
|
||||||
.build(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::DatabaseError {
|
.map_err(|_| Error::DatabaseError {
|
||||||
operation: "find_one",
|
operation: "insert_one",
|
||||||
with: "user",
|
with: "user",
|
||||||
})?
|
})?;
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
Err(Error::UsernameTaken)?
|
|
||||||
}
|
|
||||||
|
|
||||||
col.insert_one(
|
|
||||||
doc! {
|
|
||||||
"_id": session.user_id,
|
|
||||||
"username": &data.username
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|_| Error::DatabaseError {
|
|
||||||
operation: "insert_one",
|
|
||||||
with: "user",
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use rocket::Route;
|
||||||
|
|
||||||
|
mod subscribe;
|
||||||
|
mod unsubscribe;
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes![subscribe::req, unsubscribe::req]
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
use crate::database::*;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
|
||||||
|
use mongodb::bson::{doc, to_document};
|
||||||
|
use rauth::auth::Session;
|
||||||
|
use rocket_contrib::json::Json;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Subscription {
|
||||||
|
endpoint: String,
|
||||||
|
p256dh: String,
|
||||||
|
auth: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/subscribe", data = "<data>")]
|
||||||
|
pub async fn req(session: Session, data: Json<Subscription>) -> Result<()> {
|
||||||
|
let data = data.into_inner();
|
||||||
|
get_collection("accounts")
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": session.user_id,
|
||||||
|
"sessions.id": session.id.unwrap()
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"sessions.$.subscription": to_document(&data)
|
||||||
|
.map_err(|_| Error::DatabaseError { operation: "to_document", with: "subscription" })?
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError { operation: "update_one", with: "account" })?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
use crate::database::*;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
|
||||||
|
use mongodb::bson::doc;
|
||||||
|
use rauth::auth::Session;
|
||||||
|
|
||||||
|
#[post("/unsubscribe")]
|
||||||
|
pub async fn req(session: Session) -> Result<()> {
|
||||||
|
get_collection("accounts")
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": session.user_id,
|
||||||
|
"sessions.id": session.id.unwrap()
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$unset": {
|
||||||
|
"sessions.$.subscription": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "to_document",
|
||||||
|
with: "subscription",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
+15
-3
@@ -1,5 +1,6 @@
|
|||||||
use crate::util::variables::{
|
use crate::util::variables::{
|
||||||
DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY, USE_EMAIL, USE_HCAPTCHA,
|
APP_URL, AUTUMN_URL, DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY,
|
||||||
|
USE_AUTUMN, USE_EMAIL, USE_HCAPTCHA, USE_VOSO, VAPID_PUBLIC_KEY, VOSO_URL, VOSO_WS_HOST,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mongodb::bson::doc;
|
use mongodb::bson::doc;
|
||||||
@@ -8,7 +9,7 @@ use rocket_contrib::json::JsonValue;
|
|||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub async fn root() -> JsonValue {
|
pub async fn root() -> JsonValue {
|
||||||
json!({
|
json!({
|
||||||
"revolt": "0.3.3-alpha.2",
|
"revolt": "0.4.0-alpha.6",
|
||||||
"features": {
|
"features": {
|
||||||
"registration": !*DISABLE_REGISTRATION,
|
"registration": !*DISABLE_REGISTRATION,
|
||||||
"captcha": {
|
"captcha": {
|
||||||
@@ -16,8 +17,19 @@ pub async fn root() -> JsonValue {
|
|||||||
"key": HCAPTCHA_SITEKEY.to_string()
|
"key": HCAPTCHA_SITEKEY.to_string()
|
||||||
},
|
},
|
||||||
"email": *USE_EMAIL,
|
"email": *USE_EMAIL,
|
||||||
"invite_only": *INVITE_ONLY
|
"invite_only": *INVITE_ONLY,
|
||||||
|
"autumn": {
|
||||||
|
"enabled": *USE_AUTUMN,
|
||||||
|
"url": *AUTUMN_URL
|
||||||
|
},
|
||||||
|
"voso": {
|
||||||
|
"enabled": *USE_VOSO,
|
||||||
|
"url": *VOSO_URL,
|
||||||
|
"ws": *VOSO_WS_HOST
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ws": *EXTERNAL_WS_URL,
|
"ws": *EXTERNAL_WS_URL,
|
||||||
|
"app": *APP_URL,
|
||||||
|
"vapid": *VAPID_PUBLIC_KEY
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
use crate::database::*;
|
||||||
|
use crate::notifications::events::ClientboundNotification;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
|
||||||
|
use mongodb::bson::doc;
|
||||||
|
use rauth::auth::{Auth, Session};
|
||||||
|
use regex::Regex;
|
||||||
|
use rocket::State;
|
||||||
|
use rocket_contrib::json::Json;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
// ! FIXME: should be global somewhere; maybe use config(?)
|
||||||
|
lazy_static! {
|
||||||
|
static ref RE_USERNAME: Regex = Regex::new(r"^[a-zA-Z0-9_.]+$").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Validate, Serialize, Deserialize)]
|
||||||
|
pub struct Data {
|
||||||
|
#[validate(length(min = 2, max = 32), regex = "RE_USERNAME")]
|
||||||
|
username: Option<String>,
|
||||||
|
#[validate(length(min = 8, max = 72))]
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[patch("/<_ignore_id>/username", data = "<data>")]
|
||||||
|
pub async fn req(
|
||||||
|
auth: State<'_, Auth>,
|
||||||
|
session: Session,
|
||||||
|
user: User,
|
||||||
|
data: Json<Data>,
|
||||||
|
_ignore_id: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
data.validate()
|
||||||
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
auth.verify_password(&session, data.password.clone())
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::InvalidCredentials)?;
|
||||||
|
|
||||||
|
let mut set = doc! {};
|
||||||
|
if let Some(username) = &data.username {
|
||||||
|
if 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!(data.0),
|
||||||
|
}
|
||||||
|
.publish(user.id.clone())
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
use crate::database::*;
|
||||||
|
use crate::notifications::events::ClientboundNotification;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
|
||||||
|
use mongodb::bson::{doc, to_document};
|
||||||
|
use rocket_contrib::json::Json;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
#[derive(Validate, Serialize, Deserialize)]
|
||||||
|
pub struct Data {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
status: Option<UserStatus>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
profile: Option<UserProfile>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[patch("/<_ignore_id>", data = "<data>")]
|
||||||
|
pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()> {
|
||||||
|
data.validate()
|
||||||
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
get_collection("users")
|
||||||
|
.update_one(
|
||||||
|
doc! { "_id": &user.id },
|
||||||
|
doc! { "$set": to_document(&data.0).map_err(|_| Error::DatabaseError { operation: "to_document", with: "data" })? },
|
||||||
|
None
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError { operation: "update_one", with: "user" })?;
|
||||||
|
|
||||||
|
ClientboundNotification::UserUpdate {
|
||||||
|
id: user.id.clone(),
|
||||||
|
data: json!(data.0),
|
||||||
|
}
|
||||||
|
.publish(user.id.clone())
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
use crate::database::*;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
|
||||||
|
use mongodb::bson::doc;
|
||||||
|
use rocket_contrib::json::JsonValue;
|
||||||
|
|
||||||
|
#[get("/<target>/profile")]
|
||||||
|
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
|
||||||
|
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!({}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !perm.get_access() {
|
if !perm.get_access() {
|
||||||
Err(Error::LabelMe)?
|
Err(Error::MissingPermission)?
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(json!(target.from(&user).with(perm)))
|
Ok(json!(target.from(&user).with(perm)))
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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._id": &user.id, "relations.status": "Friend" },
|
||||||
|
{ "relations._id": &target.id, "relations.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 }))
|
||||||
|
}
|
||||||
@@ -2,10 +2,14 @@ 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 find_mutual;
|
||||||
mod get_avatar;
|
mod get_avatar;
|
||||||
mod get_default_avatar;
|
mod get_default_avatar;
|
||||||
mod open_dm;
|
mod open_dm;
|
||||||
@@ -16,12 +20,16 @@ pub fn routes() -> Vec<Route> {
|
|||||||
routes![
|
routes![
|
||||||
// User Information
|
// User Information
|
||||||
fetch_user::req,
|
fetch_user::req,
|
||||||
|
edit_user::req,
|
||||||
|
change_username::req,
|
||||||
get_default_avatar::req,
|
get_default_avatar::req,
|
||||||
get_avatar::req,
|
get_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,
|
||||||
|
|||||||
@@ -36,8 +36,12 @@ pub enum Error {
|
|||||||
// ? Channel related errors.
|
// ? Channel related errors.
|
||||||
#[snafu(display("This channel does not exist!"))]
|
#[snafu(display("This channel does not exist!"))]
|
||||||
UnknownChannel,
|
UnknownChannel,
|
||||||
|
#[snafu(display("Attachment does not exist!"))]
|
||||||
|
UnknownAttachment,
|
||||||
#[snafu(display("Cannot edit someone else's message."))]
|
#[snafu(display("Cannot edit someone else's message."))]
|
||||||
CannotEditMessage,
|
CannotEditMessage,
|
||||||
|
#[snafu(display("Cannot send empty message."))]
|
||||||
|
EmptyMessage,
|
||||||
#[snafu(display("Cannot remove yourself from a group, use delete channel instead."))]
|
#[snafu(display("Cannot remove yourself from a group, use delete channel instead."))]
|
||||||
CannotRemoveYourself,
|
CannotRemoveYourself,
|
||||||
#[snafu(display("Group size is too large."))]
|
#[snafu(display("Group size is too large."))]
|
||||||
@@ -48,6 +52,8 @@ pub enum Error {
|
|||||||
NotInGroup,
|
NotInGroup,
|
||||||
|
|
||||||
// ? General errors.
|
// ? General errors.
|
||||||
|
#[snafu(display("Trying to fetch too much data."))]
|
||||||
|
TooManyIds,
|
||||||
#[snafu(display("Failed to validate fields."))]
|
#[snafu(display("Failed to validate fields."))]
|
||||||
FailedValidation { error: ValidationErrors },
|
FailedValidation { error: ValidationErrors },
|
||||||
#[snafu(display("Encountered a database error."))]
|
#[snafu(display("Encountered a database error."))]
|
||||||
@@ -57,10 +63,16 @@ pub enum Error {
|
|||||||
},
|
},
|
||||||
#[snafu(display("Internal server error."))]
|
#[snafu(display("Internal server error."))]
|
||||||
InternalError,
|
InternalError,
|
||||||
|
#[snafu(display("Missing permission."))]
|
||||||
|
MissingPermission,
|
||||||
#[snafu(display("Operation cannot be performed on this object."))]
|
#[snafu(display("Operation cannot be performed on this object."))]
|
||||||
InvalidOperation,
|
InvalidOperation,
|
||||||
|
#[snafu(display("Email or password is incorrect."))]
|
||||||
|
InvalidCredentials,
|
||||||
#[snafu(display("Already created an object with this nonce."))]
|
#[snafu(display("Already created an object with this nonce."))]
|
||||||
DuplicateNonce,
|
DuplicateNonce,
|
||||||
|
#[snafu(display("Voso is not enabled on this instance."))]
|
||||||
|
VosoUnavailable,
|
||||||
#[snafu(display("This request had no effect."))]
|
#[snafu(display("This request had no effect."))]
|
||||||
NoEffect,
|
NoEffect,
|
||||||
}
|
}
|
||||||
@@ -84,7 +96,9 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
Error::NotFriends => Status::Forbidden,
|
Error::NotFriends => Status::Forbidden,
|
||||||
|
|
||||||
Error::UnknownChannel => Status::NotFound,
|
Error::UnknownChannel => Status::NotFound,
|
||||||
|
Error::UnknownAttachment => Status::BadRequest,
|
||||||
Error::CannotEditMessage => Status::Forbidden,
|
Error::CannotEditMessage => Status::Forbidden,
|
||||||
|
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,
|
||||||
@@ -93,8 +107,12 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
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::NoEffect => Status::Ok,
|
Error::NoEffect => Status::Ok,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+27
-3
@@ -7,18 +7,32 @@ 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 =
|
pub static ref APP_URL: String =
|
||||||
env::var("REVOLT_APP_URL").unwrap_or_else(|_| "https://app.revolt.chat".to_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 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 DISABLE_REGISTRATION: bool = env::var("REVOLT_DISABLE_REGISTRATION").map_or(false, |v| v == "1");
|
||||||
@@ -32,6 +46,8 @@ 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_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 =
|
||||||
@@ -48,6 +64,14 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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") {
|
||||||
|
|||||||
Reference in New Issue
Block a user