mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 05:26:59 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c95dc19f67 | ||
|
|
ab2a510bc6 | ||
|
|
7c374ec756 | ||
|
|
cbac802978 | ||
|
|
ff0e539c7b | ||
|
|
8bb1977025 | ||
|
|
000ffe6aaa | ||
|
|
81111c5937 | ||
|
|
49044d7796 | ||
|
|
f44180a980 | ||
|
|
8ee867eec7 | ||
|
|
dde4224deb | ||
|
|
088490dfc3 | ||
|
|
7e7eb34f65 | ||
|
|
74b4238f04 |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
assets
|
||||||
|
target
|
||||||
|
.env
|
||||||
20
.gitlab-ci.yml
Normal file
20
.gitlab-ci.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
stages:
|
||||||
|
- build
|
||||||
|
|
||||||
|
# Rocket does not currently compile on stable Rust.
|
||||||
|
# Once it does, we can uncomment this, and instead
|
||||||
|
# put `allow-failure: true` on the nightly build.
|
||||||
|
#
|
||||||
|
# rust-latest:
|
||||||
|
# stage: build
|
||||||
|
# image: rust:latest
|
||||||
|
# script:
|
||||||
|
# - cargo build --verbose
|
||||||
|
# - cargo test --verbose
|
||||||
|
|
||||||
|
rust-nightly:
|
||||||
|
stage: build
|
||||||
|
image: rustlang/rust:nightly
|
||||||
|
script:
|
||||||
|
- cargo build --verbose
|
||||||
|
- cargo test --verbose
|
||||||
366
Cargo.lock
generated
366
Cargo.lock
generated
@@ -15,15 +15,6 @@ version = "0.2.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.2.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3"
|
|
||||||
dependencies = [
|
|
||||||
"const-random",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
@@ -39,6 +30,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-attributes"
|
name = "async-attributes"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
@@ -174,15 +171,14 @@ checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bcrypt"
|
name = "bcrypt"
|
||||||
version = "0.7.0"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f02d7d008a57bcb2251ba115b803934e02315edbde9a861c88713493e381b63"
|
checksum = "e2cab630912253fb9dc92c0e2fabd0a7b51f5a5a4007177cfa31e517015b7204"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.12.3",
|
"base64 0.12.3",
|
||||||
"blowfish",
|
"blowfish",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"lazy_static",
|
"getrandom",
|
||||||
"rand 0.7.3",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -206,16 +202,16 @@ dependencies = [
|
|||||||
"block-padding",
|
"block-padding",
|
||||||
"byte-tools",
|
"byte-tools",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"generic-array",
|
"generic-array 0.12.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-cipher-trait"
|
name = "block-cipher"
|
||||||
version = "0.6.2"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
|
checksum = "f337a3e6da609650eb74e02bc9fac7b735049f7623ab12f2e4c719316fcc7e80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -243,20 +239,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blowfish"
|
name = "blowfish"
|
||||||
version = "0.4.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3"
|
checksum = "0f06850ba969bc59388b2cc0a4f186fc6d9d37208863b15b84ae3866ac90ac06"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-cipher-trait",
|
"block-cipher",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"opaque-debug",
|
"opaque-debug 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bson"
|
name = "bson"
|
||||||
version = "1.1.0-beta"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef53f55e2f3e8b57182771db30a3e32d0aeaaa5aebbe18ce81b2d75e69b922ec"
|
checksum = "c11f16001d679cb13d14b2c93c7d0fa13bb484a87c34a6c4c39707ad936499b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.12.3",
|
"base64 0.12.3",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -268,6 +264,12 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bufstream"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.4.0"
|
version = "3.4.0"
|
||||||
@@ -322,9 +324,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.13"
|
version = "0.4.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6"
|
checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
@@ -342,6 +344,15 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cloudabi"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "concurrent-queue"
|
name = "concurrent-queue"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -351,26 +362,6 @@ dependencies = [
|
|||||||
"cache-padded",
|
"cache-padded",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-random"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
|
|
||||||
dependencies = [
|
|
||||||
"const-random-macro",
|
|
||||||
"proc-macro-hack",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-random-macro"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
"proc-macro-hack",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.11.3"
|
version = "0.11.3"
|
||||||
@@ -414,7 +405,7 @@ version = "0.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
|
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.12.3",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -467,7 +458,7 @@ version = "0.8.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.12.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -677,8 +668,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "bc1529d07bd55fa54fc6c2d8460f621cfde0ae3fd2b96b2fa7eb4883478e1703"
|
checksum = "bc1529d07bd55fa54fc6c2d8460f621cfde0ae3fd2b96b2fa7eb4883478e1703"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"lock_api",
|
"lock_api 0.3.4",
|
||||||
"parking_lot",
|
"parking_lot 0.10.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -758,6 +749,16 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check 0.9.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
@@ -802,30 +803,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.6.3"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead"
|
checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
|
||||||
dependencies = [
|
|
||||||
"ahash 0.2.18",
|
|
||||||
"autocfg 0.1.7",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.7.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
|
|
||||||
dependencies = [
|
|
||||||
"ahash 0.3.8",
|
|
||||||
"autocfg 1.0.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.8.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
"autocfg 1.0.0",
|
"autocfg 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -966,6 +948,24 @@ dependencies = [
|
|||||||
"tokio-tls",
|
"tokio-tls",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyperx"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9eae1ec4abdc4530fb001ebf585fd14e52ed17f0aacd3e13de497b71ed451750"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.12.3",
|
||||||
|
"bytes 0.5.6",
|
||||||
|
"http",
|
||||||
|
"httparse",
|
||||||
|
"language-tags",
|
||||||
|
"log 0.4.11",
|
||||||
|
"mime 0.3.16",
|
||||||
|
"percent-encoding 2.1.0",
|
||||||
|
"time 0.1.43",
|
||||||
|
"unicase 2.6.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -995,7 +995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7"
|
checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.0",
|
"autocfg 1.0.0",
|
||||||
"hashbrown 0.8.1",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1018,6 +1018,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iovec"
|
name = "iovec"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -1097,12 +1103,61 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lettre"
|
||||||
|
version = "0.10.0-alpha.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "deaf9b74d40fcb52d0f762eb08e45d5152b4db59d29bb73edd4cac7fe796862c"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.12.3",
|
||||||
|
"bufstream",
|
||||||
|
"hostname",
|
||||||
|
"hyperx",
|
||||||
|
"idna 0.2.0",
|
||||||
|
"line-wrap",
|
||||||
|
"mime 0.3.16",
|
||||||
|
"nom",
|
||||||
|
"once_cell",
|
||||||
|
"quoted_printable",
|
||||||
|
"r2d2",
|
||||||
|
"regex",
|
||||||
|
"rustls",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"textnonce",
|
||||||
|
"uuid",
|
||||||
|
"webpki",
|
||||||
|
"webpki-roots 0.19.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lexical-core"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"ryu",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.74"
|
version = "0.2.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10"
|
checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "line-wrap"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
|
||||||
|
dependencies = [
|
||||||
|
"safemem",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -1118,6 +1173,15 @@ dependencies = [
|
|||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@@ -1138,11 +1202,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru"
|
name = "lru"
|
||||||
version = "0.5.3"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35c456c123957de3a220cd03786e0d86aa542a88b46029973b542f426da6ef34"
|
checksum = "111b945ac72ec09eb7bc62a0fbdc3cc6e80555a7245f52a69d3921a75b53b153"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.6.3",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1174,7 +1238,7 @@ checksum = "a18af3dcaf2b0219366cdb4e2af65a6101457b415c3d1a5c71dd9c2b7c77b9c8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"digest",
|
"digest",
|
||||||
"opaque-debug",
|
"opaque-debug 0.2.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1262,9 +1326,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mongodb"
|
name = "mongodb"
|
||||||
version = "1.1.0-beta"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78f8c23edb98f47884f7f419c7dfce7aa5a5334d27983c86f26847b3b6225552"
|
checksum = "f8cb54f85e6380c8cfe130a55a85cfc2584e8317f4bb8bda464d367fd4f24957"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1302,7 +1366,7 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
"version_check 0.9.2",
|
"version_check 0.9.2",
|
||||||
"webpki",
|
"webpki",
|
||||||
"webpki-roots",
|
"webpki-roots 0.18.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1334,6 +1398,17 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "5.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||||
|
dependencies = [
|
||||||
|
"lexical-core",
|
||||||
|
"memchr",
|
||||||
|
"version_check 0.9.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify"
|
name = "notify"
|
||||||
version = "4.0.15"
|
version = "4.0.15"
|
||||||
@@ -1383,9 +1458,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_enum"
|
name = "num_enum"
|
||||||
version = "0.4.3"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4"
|
checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derivative",
|
"derivative",
|
||||||
"num_enum_derive",
|
"num_enum_derive",
|
||||||
@@ -1393,9 +1468,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_enum_derive"
|
name = "num_enum_derive"
|
||||||
version = "0.4.3"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d"
|
checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2 1.0.19",
|
"proc-macro2 1.0.19",
|
||||||
@@ -1411,9 +1486,9 @@ checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.4.0"
|
version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
|
checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
@@ -1421,6 +1496,12 @@ version = "0.2.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.30"
|
version = "0.10.30"
|
||||||
@@ -1476,8 +1557,19 @@ version = "0.10.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
|
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api 0.3.4",
|
||||||
"parking_lot_core",
|
"parking_lot_core 0.7.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
"lock_api 0.4.1",
|
||||||
|
"parking_lot_core 0.8.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1487,7 +1579,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
|
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cloudabi",
|
"cloudabi 0.0.3",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cloudabi 0.1.0",
|
||||||
|
"instant",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@@ -1669,6 +1776,23 @@ dependencies = [
|
|||||||
"proc-macro2 1.0.19",
|
"proc-macro2 1.0.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quoted_printable"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b080c5db639b292ac79cbd34be0cfc5d36694768d8341109634d90b86930e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r2d2"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
|
||||||
|
dependencies = [
|
||||||
|
"log 0.4.11",
|
||||||
|
"parking_lot 0.11.0",
|
||||||
|
"scheduled-thread-pool",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
@@ -1789,7 +1913,7 @@ version = "0.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cloudabi",
|
"cloudabi 0.0.3",
|
||||||
"fuchsia-cprng",
|
"fuchsia-cprng",
|
||||||
"libc",
|
"libc",
|
||||||
"rand_core 0.4.2",
|
"rand_core 0.4.2",
|
||||||
@@ -1861,9 +1985,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.10.7"
|
version = "0.10.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12427a5577082c24419c9c417db35cfeb65962efc7675bb6b0d5f1f9d315bfe6"
|
checksum = "e9eaa17ac5d7b838b7503d118fa16ad88f440498bf9ffe5424e621f93190d61e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.12.3",
|
"base64 0.12.3",
|
||||||
"bytes 0.5.6",
|
"bytes 0.5.6",
|
||||||
@@ -1907,15 +2031,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt"
|
name = "revolt"
|
||||||
version = "0.2.7"
|
version = "0.2.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"hashbrown 0.7.2",
|
"hashbrown",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"lettre",
|
||||||
"log 0.4.11",
|
"log 0.4.11",
|
||||||
"lru",
|
"lru",
|
||||||
"mongodb",
|
"mongodb",
|
||||||
@@ -2101,6 +2226,15 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scheduled-thread-pool"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot 0.11.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -2163,18 +2297,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.114"
|
version = "1.0.115"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
|
checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.114"
|
version = "1.0.115"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
|
checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.19",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
@@ -2235,7 +2369,7 @@ dependencies = [
|
|||||||
"block-buffer",
|
"block-buffer",
|
||||||
"digest",
|
"digest",
|
||||||
"fake-simd",
|
"fake-simd",
|
||||||
"opaque-debug",
|
"opaque-debug 0.2.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2253,7 +2387,7 @@ dependencies = [
|
|||||||
"block-buffer",
|
"block-buffer",
|
||||||
"digest",
|
"digest",
|
||||||
"fake-simd",
|
"fake-simd",
|
||||||
"opaque-debug",
|
"opaque-debug 0.2.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2322,6 +2456,12 @@ version = "0.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028"
|
checksum = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stdweb"
|
name = "stdweb"
|
||||||
version = "0.4.20"
|
version = "0.4.20"
|
||||||
@@ -2456,6 +2596,19 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textnonce"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acc659075a12c12c07bbb384862c352506707f6597f5b495f65427d08519b617"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.12.3",
|
||||||
|
"byteorder",
|
||||||
|
"chrono",
|
||||||
|
"rand 0.7.3",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
@@ -2714,9 +2867,9 @@ checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ulid"
|
name = "ulid"
|
||||||
version = "0.3.3"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39ea3ad32d4895ee0681033bd93f16939e186dac72259fa5efc158703f8be4bc"
|
checksum = "cacfb60872052779608509dcb3dbc8b9d955f5686d71e7ad3173029dc482d07d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@@ -2987,6 +3140,15 @@ dependencies = [
|
|||||||
"webpki",
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8eff4b7516a57307f9349c64bf34caa34b940b66fed4b2fb3136cb7386e5739"
|
||||||
|
dependencies = [
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wepoll-sys-stjepang"
|
name = "wepoll-sys-stjepang"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|||||||
33
Cargo.toml
33
Cargo.toml
@@ -1,32 +1,33 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt"
|
name = "revolt"
|
||||||
version = "0.2.7"
|
version = "0.2.10"
|
||||||
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]
|
||||||
mongodb = { version = "1.1.0-beta", default-features = false, features = ["sync"] } # FIXME: rewrite database with async API
|
mongodb = { version = "1.1.0", default-features = false, features = ["sync"] } # FIXME: rewrite database with async API
|
||||||
rocket = { version = "0.4.4", default-features = false }
|
rocket = { version = "0.4.5", default-features = false }
|
||||||
once_cell = "1.3.1"
|
once_cell = "1.4.1"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
ulid = "0.3.1"
|
ulid = "0.4.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0.115", features = ["derive"] }
|
||||||
rocket_contrib = "0.4.4"
|
rocket_contrib = "0.4.5"
|
||||||
validator = "0.10.0"
|
validator = "0.10.1"
|
||||||
bcrypt = "0.7.0"
|
bcrypt = "0.8.2"
|
||||||
chrono = "0.4.11"
|
chrono = "0.4.15"
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
time = "0.2.9"
|
time = "0.2.16"
|
||||||
reqwest = { version = "0.10.4", features = ["blocking", "json"] }
|
reqwest = { version = "0.10.8", features = ["blocking", "json"] }
|
||||||
num_enum = "0.4.3"
|
num_enum = "0.5.1"
|
||||||
ws = "0.9.1"
|
ws = "0.9.1"
|
||||||
hashbrown = "0.7.1"
|
hashbrown = "0.8.2"
|
||||||
serde_json = "1.0.51"
|
serde_json = "1.0.57"
|
||||||
rocket_cors = "0.5.2"
|
rocket_cors = "0.5.2"
|
||||||
bitfield = "0.13.2"
|
bitfield = "0.13.2"
|
||||||
lru = "0.5.3"
|
lru = "0.6.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
env_logger = "0.7.1"
|
env_logger = "0.7.1"
|
||||||
|
lettre = "0.10.0-alpha.1"
|
||||||
|
|||||||
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Build Stage
|
||||||
|
FROM ekidd/rust-musl-builder:nightly-2020-08-26 AS builder
|
||||||
|
WORKDIR /home/rust/src
|
||||||
|
|
||||||
|
RUN USER=root cargo new --bin revolt
|
||||||
|
WORKDIR ./revolt
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY src ./src
|
||||||
|
RUN cargo build --release
|
||||||
|
|
||||||
|
# Bundle Stage
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=builder /home/rust/src/revolt/target/x86_64-unknown-linux-musl/release/revolt ./
|
||||||
|
EXPOSE 8000
|
||||||
|
EXPOSE 9000
|
||||||
|
CMD ["./revolt"]
|
||||||
11
README.md
11
README.md
@@ -7,3 +7,14 @@ Features:
|
|||||||
- Distributed notification system, allowing any node to be seamlessly connected.
|
- Distributed notification system, allowing any node to be seamlessly connected.
|
||||||
- Simple deployment, based mostly on pure Rust code and libraries.
|
- Simple deployment, based mostly on pure Rust code and libraries.
|
||||||
- Hooks up to a MongoDB deployment, provide URI and no extra work needed.
|
- Hooks up to a MongoDB deployment, provide URI and no extra work needed.
|
||||||
|
|
||||||
|
## Docker Helper Scripts
|
||||||
|
|
||||||
|
If you have Docker installed, you can use the helper scripts to deploy Revolt in your development environment.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./build.sh # build Docker image
|
||||||
|
./run.sh # run Docker container
|
||||||
|
./monitor.sh # view container logs
|
||||||
|
./remove.sh # kill and remove container
|
||||||
|
```
|
||||||
|
|||||||
2
build.sh
Normal file
2
build.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
docker build -t revolt --progress plain .
|
||||||
2
monitor.sh
Normal file
2
monitor.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
docker logs -f revolt
|
||||||
4
remove.sh
Normal file
4
remove.sh
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Removing Revolt container."
|
||||||
|
docker kill revolt
|
||||||
|
docker rm revolt
|
||||||
22
run.sh
Normal file
22
run.sh
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Split at \n instead of space.
|
||||||
|
# https://unix.stackexchange.com/a/39482
|
||||||
|
set -f
|
||||||
|
IFS='
|
||||||
|
'
|
||||||
|
|
||||||
|
input=($(egrep -v '^#' .env))
|
||||||
|
prepended=(${input[@]/#/-e\"})
|
||||||
|
variables=${prepended[@]/%/\"}
|
||||||
|
|
||||||
|
unset IFS
|
||||||
|
set +f
|
||||||
|
|
||||||
|
echo "Running Revolt in detached mode."
|
||||||
|
docker run \
|
||||||
|
-d \
|
||||||
|
--name revolt \
|
||||||
|
-p 8000:8000 \
|
||||||
|
-p 9000:9000 \
|
||||||
|
$variables \
|
||||||
|
revolt
|
||||||
@@ -4,6 +4,7 @@ use lru::LruCache;
|
|||||||
use mongodb::bson::{doc, from_bson, Bson};
|
use mongodb::bson::{doc, from_bson, Bson};
|
||||||
use rocket::http::RawStr;
|
use rocket::http::RawStr;
|
||||||
use rocket::request::FromParam;
|
use rocket::request::FromParam;
|
||||||
|
use rocket_contrib::json::JsonValue;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@@ -40,6 +41,36 @@ pub struct Channel {
|
|||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Channel {
|
||||||
|
pub fn serialise(self) -> JsonValue {
|
||||||
|
match self.channel_type {
|
||||||
|
0 => json!({
|
||||||
|
"id": self.id,
|
||||||
|
"type": self.channel_type,
|
||||||
|
"last_message": self.last_message,
|
||||||
|
"recipients": self.recipients,
|
||||||
|
}),
|
||||||
|
1 => json!({
|
||||||
|
"id": self.id,
|
||||||
|
"type": self.channel_type,
|
||||||
|
"last_message": self.last_message,
|
||||||
|
"recipients": self.recipients,
|
||||||
|
"name": self.name,
|
||||||
|
"owner": self.owner,
|
||||||
|
"description": self.description,
|
||||||
|
}),
|
||||||
|
2 => json!({
|
||||||
|
"id": self.id,
|
||||||
|
"type": self.channel_type,
|
||||||
|
"guild": self.guild,
|
||||||
|
"name": self.name,
|
||||||
|
"description": self.description,
|
||||||
|
}),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CACHE: Arc<Mutex<LruCache<String, Channel>>> =
|
static ref CACHE: Arc<Mutex<LruCache<String, Channel>>> =
|
||||||
Arc::new(Mutex::new(LruCache::new(4_000_000)));
|
Arc::new(Mutex::new(LruCache::new(4_000_000)));
|
||||||
@@ -77,19 +108,19 @@ pub fn fetch_channel(id: &str) -> Result<Option<Channel>, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_channels(ids: &Vec<String>) -> Result<Option<Vec<Channel>>, String> {
|
pub fn fetch_channels(ids: &Vec<String>) -> Result<Vec<Channel>, String> {
|
||||||
let mut missing = vec![];
|
let mut missing = vec![];
|
||||||
let mut channels = vec![];
|
let mut channels = vec![];
|
||||||
|
|
||||||
{
|
{
|
||||||
if let Ok(mut cache) = CACHE.lock() {
|
if let Ok(mut cache) = CACHE.lock() {
|
||||||
for gid in ids {
|
for id in ids {
|
||||||
let existing = cache.get(gid);
|
let existing = cache.get(id);
|
||||||
|
|
||||||
if let Some(channel) = existing {
|
if let Some(channel) = existing {
|
||||||
channels.push((*channel).clone());
|
channels.push((*channel).clone());
|
||||||
} else {
|
} else {
|
||||||
missing.push(gid);
|
missing.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -98,7 +129,7 @@ pub fn fetch_channels(ids: &Vec<String>) -> Result<Option<Vec<Channel>>, String>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if missing.len() == 0 {
|
if missing.len() == 0 {
|
||||||
return Ok(Some(channels));
|
return Ok(channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
let col = get_collection("channels");
|
let col = get_collection("channels");
|
||||||
@@ -117,7 +148,7 @@ pub fn fetch_channels(ids: &Vec<String>) -> Result<Option<Vec<Channel>>, String>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(channels))
|
Ok(channels)
|
||||||
} else {
|
} else {
|
||||||
Err("Failed to fetch channel from database.".to_string())
|
Err("Failed to fetch channel from database.".to_string())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
use super::channel::fetch_channels;
|
||||||
use super::get_collection;
|
use super::get_collection;
|
||||||
|
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use mongodb::bson::{doc, from_bson, Bson};
|
use mongodb::bson::{doc, from_bson, Bson};
|
||||||
use rocket::http::RawStr;
|
use rocket::http::RawStr;
|
||||||
use rocket::request::FromParam;
|
use rocket::request::FromParam;
|
||||||
|
use rocket_contrib::json::JsonValue;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@@ -42,13 +44,43 @@ pub struct Guild {
|
|||||||
pub description: String,
|
pub description: String,
|
||||||
pub owner: String,
|
pub owner: String,
|
||||||
|
|
||||||
// ? FIXME: ADD: pub channels: Vec<Channel>,
|
pub channels: Vec<String>,
|
||||||
pub invites: Vec<Invite>,
|
pub invites: Vec<Invite>,
|
||||||
pub bans: Vec<Ban>,
|
pub bans: Vec<Ban>,
|
||||||
|
|
||||||
pub default_permissions: u32,
|
pub default_permissions: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Guild {
|
||||||
|
pub fn serialise(self) -> JsonValue {
|
||||||
|
json!({
|
||||||
|
"id": self.id,
|
||||||
|
"name": self.name,
|
||||||
|
"description": self.description,
|
||||||
|
"owner": self.owner
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_channels(&self) -> Result<Vec<super::channel::Channel>, String> {
|
||||||
|
super::channel::fetch_channels(&self.channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seralise_with_channels(self) -> Result<JsonValue, String> {
|
||||||
|
let channels = self
|
||||||
|
.fetch_channels()?
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.serialise())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut value = self.serialise();
|
||||||
|
value
|
||||||
|
.as_object_mut()
|
||||||
|
.unwrap()
|
||||||
|
.insert("channels".to_string(), channels);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Hash, Eq, PartialEq)]
|
#[derive(Hash, Eq, PartialEq)]
|
||||||
pub struct MemberKey(pub String, pub String);
|
pub struct MemberKey(pub String, pub String);
|
||||||
|
|
||||||
@@ -91,6 +123,75 @@ pub fn fetch_guild(id: &str) -> Result<Option<Guild>, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fetch_guilds(ids: &Vec<String>) -> Result<Vec<Guild>, String> {
|
||||||
|
let mut missing = vec![];
|
||||||
|
let mut guilds = vec![];
|
||||||
|
|
||||||
|
{
|
||||||
|
if let Ok(mut cache) = CACHE.lock() {
|
||||||
|
for id in ids {
|
||||||
|
let existing = cache.get(id);
|
||||||
|
|
||||||
|
if let Some(guild) = existing {
|
||||||
|
guilds.push((*guild).clone());
|
||||||
|
} else {
|
||||||
|
missing.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err("Failed to lock cache.".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if missing.len() == 0 {
|
||||||
|
return Ok(guilds);
|
||||||
|
}
|
||||||
|
|
||||||
|
let col = get_collection("guilds");
|
||||||
|
if let Ok(result) = col.find(doc! { "_id": { "$in": missing } }, None) {
|
||||||
|
for item in result {
|
||||||
|
let mut cache = CACHE.lock().unwrap();
|
||||||
|
if let Ok(doc) = item {
|
||||||
|
if let Ok(guild) = from_bson(Bson::Document(doc)) as Result<Guild, _> {
|
||||||
|
cache.put(guild.id.clone(), guild.clone());
|
||||||
|
guilds.push(guild);
|
||||||
|
} else {
|
||||||
|
return Err("Failed to deserialize guild!".to_string());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err("Failed to fetch guild.".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(guilds)
|
||||||
|
} else {
|
||||||
|
Err("Failed to fetch channel from database.".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialise_guilds_with_channels(ids: &Vec<String>) -> Result<Vec<JsonValue>, String> {
|
||||||
|
let guilds = fetch_guilds(&ids)?;
|
||||||
|
let cids: Vec<String> = guilds.iter().flat_map(|x| x.channels.clone()).collect();
|
||||||
|
|
||||||
|
let channels = fetch_channels(&cids)?;
|
||||||
|
Ok(guilds
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| {
|
||||||
|
let id = x.id.clone();
|
||||||
|
let mut obj = x.serialise();
|
||||||
|
obj.as_object_mut().unwrap().insert(
|
||||||
|
"channels".to_string(),
|
||||||
|
channels
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.guild.is_some() && x.guild.as_ref().unwrap() == &id)
|
||||||
|
.map(|x| x.clone().serialise())
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
obj
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fetch_member(key: MemberKey) -> Result<Option<Member>, String> {
|
pub fn fetch_member(key: MemberKey) -> Result<Option<Member>, String> {
|
||||||
{
|
{
|
||||||
if let Ok(mut cache) = MEMBER_CACHE.lock() {
|
if let Ok(mut cache) = MEMBER_CACHE.lock() {
|
||||||
@@ -216,19 +317,19 @@ pub fn process_event(event: &Notification) {
|
|||||||
Notification::guild_user_join(ev) => {
|
Notification::guild_user_join(ev) => {
|
||||||
let mut cache = MEMBER_CACHE.lock().unwrap();
|
let mut cache = MEMBER_CACHE.lock().unwrap();
|
||||||
cache.put(
|
cache.put(
|
||||||
MemberKey ( ev.id.clone(), ev.user.clone() ),
|
MemberKey(ev.id.clone(), ev.user.clone()),
|
||||||
Member {
|
Member {
|
||||||
id: MemberRef {
|
id: MemberRef {
|
||||||
guild: ev.id.clone(),
|
guild: ev.id.clone(),
|
||||||
user: ev.user.clone()
|
user: ev.user.clone(),
|
||||||
},
|
},
|
||||||
nickname: None
|
nickname: None,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Notification::guild_user_leave(ev) => {
|
Notification::guild_user_leave(ev) => {
|
||||||
let mut cache = MEMBER_CACHE.lock().unwrap();
|
let mut cache = MEMBER_CACHE.lock().unwrap();
|
||||||
cache.pop(&MemberKey ( ev.id.clone(), ev.user.clone() ));
|
cache.pop(&MemberKey(ev.id.clone(), ev.user.clone()));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,33 @@
|
|||||||
use super::super::get_db;
|
use super::super::get_db;
|
||||||
use super::scripts::LATEST_REVISION;
|
use super::scripts::LATEST_REVISION;
|
||||||
|
|
||||||
use mongodb::options::CreateCollectionOptions;
|
|
||||||
use mongodb::bson::doc;
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use mongodb::bson::doc;
|
||||||
|
use mongodb::options::CreateCollectionOptions;
|
||||||
|
|
||||||
pub fn create_database() {
|
pub fn create_database() {
|
||||||
info!("Creating database.");
|
info!("Creating database.");
|
||||||
let db = get_db();
|
let db = get_db();
|
||||||
|
|
||||||
db.create_collection("users", None).expect("Failed to create users collection.");
|
db.create_collection("users", None)
|
||||||
db.create_collection("channels", None).expect("Failed to create channels collection.");
|
.expect("Failed to create users collection.");
|
||||||
db.create_collection("guilds", None).expect("Failed to create guilds collection.");
|
db.create_collection("channels", None)
|
||||||
db.create_collection("members", None).expect("Failed to create members collection.");
|
.expect("Failed to create channels collection.");
|
||||||
db.create_collection("messages", None).expect("Failed to create messages collection.");
|
db.create_collection("guilds", None)
|
||||||
db.create_collection("migrations", None).expect("Failed to create migrations collection.");
|
.expect("Failed to create guilds collection.");
|
||||||
|
db.create_collection("members", None)
|
||||||
|
.expect("Failed to create members collection.");
|
||||||
|
db.create_collection("messages", None)
|
||||||
|
.expect("Failed to create messages collection.");
|
||||||
|
db.create_collection("migrations", None)
|
||||||
|
.expect("Failed to create migrations collection.");
|
||||||
|
|
||||||
db.create_collection(
|
db.create_collection(
|
||||||
"pubsub",
|
"pubsub",
|
||||||
CreateCollectionOptions::builder()
|
CreateCollectionOptions::builder()
|
||||||
.capped(true)
|
.capped(true)
|
||||||
.size(1_000_000)
|
.size(1_000_000)
|
||||||
.build()
|
.build(),
|
||||||
)
|
)
|
||||||
.expect("Failed to create pubsub collection.");
|
.expect("Failed to create pubsub collection.");
|
||||||
|
|
||||||
@@ -31,9 +37,9 @@ pub fn create_database() {
|
|||||||
"_id": 0,
|
"_id": 0,
|
||||||
"revision": LATEST_REVISION
|
"revision": LATEST_REVISION
|
||||||
},
|
},
|
||||||
None
|
None,
|
||||||
)
|
)
|
||||||
.expect("Failed to save migration info.");
|
.expect("Failed to save migration info.");
|
||||||
|
|
||||||
info!("Created database.");
|
info!("Created database.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ pub mod scripts;
|
|||||||
pub fn run_migrations() {
|
pub fn run_migrations() {
|
||||||
let client = get_connection();
|
let client = get_connection();
|
||||||
|
|
||||||
let list = client.list_database_names(
|
let list = client
|
||||||
None,
|
.list_database_names(None, None)
|
||||||
None
|
.expect("Failed to fetch database names.");
|
||||||
).expect("Failed to fetch database names.");
|
|
||||||
|
|
||||||
if list.iter().position(|x| x == "revolt").is_none() {
|
if list.iter().position(|x| x == "revolt").is_none() {
|
||||||
init::create_database();
|
init::create_database();
|
||||||
|
|||||||
@@ -1,39 +1,43 @@
|
|||||||
use super::super::get_collection;
|
use super::super::get_collection;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use mongodb::bson::{Bson, from_bson, doc};
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use mongodb::bson::{doc, from_bson, Bson};
|
||||||
|
use mongodb::options::FindOptions;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct MigrationInfo {
|
struct MigrationInfo {
|
||||||
_id: i32,
|
_id: i32,
|
||||||
revision: i32
|
revision: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LATEST_REVISION: i32 = 1;
|
pub const LATEST_REVISION: i32 = 2;
|
||||||
|
|
||||||
pub fn migrate_database() {
|
pub fn migrate_database() {
|
||||||
let migrations = get_collection("migrations");
|
let migrations = get_collection("migrations");
|
||||||
let data = migrations.find_one(None, None)
|
let data = migrations
|
||||||
|
.find_one(None, None)
|
||||||
.expect("Failed to fetch migration data.");
|
.expect("Failed to fetch migration data.");
|
||||||
|
|
||||||
if let Some(doc) = data {
|
if let Some(doc) = data {
|
||||||
let info: MigrationInfo = from_bson(Bson::Document(doc))
|
let info: MigrationInfo =
|
||||||
.expect("Failed to read migration information.");
|
from_bson(Bson::Document(doc)).expect("Failed to read migration information.");
|
||||||
|
|
||||||
let revision = run_migrations(info.revision);
|
let revision = run_migrations(info.revision);
|
||||||
|
|
||||||
migrations.update_one(
|
migrations
|
||||||
doc! {
|
.update_one(
|
||||||
"_id": info._id
|
doc! {
|
||||||
},
|
"_id": info._id
|
||||||
doc! {
|
},
|
||||||
"$set": {
|
doc! {
|
||||||
"revision": revision
|
"$set": {
|
||||||
}
|
"revision": revision
|
||||||
},
|
}
|
||||||
None
|
},
|
||||||
).expect("Failed to commit migration information.");
|
None,
|
||||||
|
)
|
||||||
|
.expect("Failed to commit migration information.");
|
||||||
|
|
||||||
info!("Migration complete. Currently at revision {}.", revision);
|
info!("Migration complete. Currently at revision {}.", revision);
|
||||||
} else {
|
} else {
|
||||||
@@ -48,6 +52,68 @@ pub fn run_migrations(revision: i32) -> i32 {
|
|||||||
info!("Running migration [revision 0]: Test migration system.");
|
info!("Running migration [revision 0]: Test migration system.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if revision <= 1 {
|
||||||
|
info!("Running migration [revision 1]: Add channels to guild object.");
|
||||||
|
|
||||||
|
let col = get_collection("guilds");
|
||||||
|
let guilds = col
|
||||||
|
.find(
|
||||||
|
None,
|
||||||
|
FindOptions::builder().projection(doc! { "_id": 1 }).build(),
|
||||||
|
)
|
||||||
|
.expect("Failed to fetch guilds.");
|
||||||
|
|
||||||
|
let result = get_collection("channels")
|
||||||
|
.find(
|
||||||
|
doc! {
|
||||||
|
"type": 2
|
||||||
|
},
|
||||||
|
FindOptions::builder()
|
||||||
|
.projection(doc! { "_id": 1, "guild": 1 })
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.expect("Failed to fetch channels.");
|
||||||
|
|
||||||
|
let mut channels = vec![];
|
||||||
|
for doc in result {
|
||||||
|
let channel = doc.expect("Failed to fetch channel.");
|
||||||
|
let id = channel
|
||||||
|
.get_str("_id")
|
||||||
|
.expect("Failed to get channel id.")
|
||||||
|
.to_string();
|
||||||
|
let gid = channel
|
||||||
|
.get_str("guild")
|
||||||
|
.expect("Failed to get guild id.")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
channels.push((id, gid));
|
||||||
|
}
|
||||||
|
|
||||||
|
for doc in guilds {
|
||||||
|
let guild = doc.expect("Failed to fetch guild.");
|
||||||
|
let id = guild.get_str("_id").expect("Failed to get guild id.");
|
||||||
|
|
||||||
|
let list: Vec<String> = channels
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.1 == id)
|
||||||
|
.map(|x| x.0.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
col.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": id
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"channels": list
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.expect("Failed to update guild.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reminder to update LATEST_REVISION when adding new migrations.
|
// Reminder to update LATEST_REVISION when adding new migrations.
|
||||||
LATEST_REVISION
|
LATEST_REVISION
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
use mongodb::bson::doc;
|
use crate::util::variables::MONGO_URI;
|
||||||
use mongodb::sync::{Client, Collection, Database};
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
|
use mongodb::sync::{Client, Collection, Database};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
static DBCONN: OnceCell<Client> = OnceCell::new();
|
static DBCONN: OnceCell<Client> = OnceCell::new();
|
||||||
|
|
||||||
pub fn connect() {
|
pub fn connect() {
|
||||||
let client =
|
let client = Client::with_uri_str(&MONGO_URI).expect("Failed to init db connection.");
|
||||||
Client::with_uri_str(&env::var("DB_URI").expect("DB_URI not in environment variables!"))
|
|
||||||
.expect("Failed to init db connection.");
|
|
||||||
|
|
||||||
DBCONN.set(client).unwrap();
|
DBCONN.set(client).unwrap();
|
||||||
migrations::run_migrations();
|
migrations::run_migrations();
|
||||||
|
|||||||
@@ -118,12 +118,10 @@ pub fn has_mutual_connection(user_id: &str, target_id: &str, with_permission: bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
} else if result.count() > 0 {
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
if result.count() > 0 {
|
false
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -173,9 +173,12 @@ impl PermissionCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(other) = other_user {
|
if let Some(other) = other_user {
|
||||||
let relationship =
|
let relationship = get_relationship_internal(
|
||||||
get_relationship_internal(&self.user.id, &other, &self.user.relations);
|
&self.user.id,
|
||||||
|
&other,
|
||||||
|
&self.user.relations,
|
||||||
|
);
|
||||||
|
|
||||||
if relationship == Relationship::Friend {
|
if relationship == Relationship::Friend {
|
||||||
permissions = 1024 + 128 + 32 + 16 + 1;
|
permissions = 1024 + 128 + 32 + 16 + 1;
|
||||||
} else if relationship == Relationship::Blocked
|
} else if relationship == Relationship::Blocked
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
|
use super::channel::fetch_channels;
|
||||||
use super::get_collection;
|
use super::get_collection;
|
||||||
|
use super::guild::serialise_guilds_with_channels;
|
||||||
|
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use mongodb::bson::{doc, from_bson, Bson, DateTime};
|
use mongodb::bson::{doc, from_bson, Bson, DateTime};
|
||||||
|
use mongodb::options::FindOptions;
|
||||||
use rocket::http::{RawStr, Status};
|
use rocket::http::{RawStr, Status};
|
||||||
use rocket::request::{self, FromParam, FromRequest, Request};
|
use rocket::request::{self, FromParam, FromRequest, Request};
|
||||||
use rocket::Outcome;
|
use rocket::Outcome;
|
||||||
use std::sync::{Arc, Mutex};
|
use rocket_contrib::json::JsonValue;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct UserEmailVerification {
|
pub struct UserEmailVerification {
|
||||||
@@ -36,6 +40,97 @@ pub struct User {
|
|||||||
pub relations: Option<Vec<UserRelationship>>,
|
pub relations: Option<Vec<UserRelationship>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl User {
|
||||||
|
pub fn serialise(self, relationship: i32) -> JsonValue {
|
||||||
|
if relationship == super::Relationship::SELF as i32 {
|
||||||
|
json!({
|
||||||
|
"id": self.id,
|
||||||
|
"username": self.username,
|
||||||
|
"display_name": self.display_name,
|
||||||
|
"email": self.email,
|
||||||
|
"verified": self.email_verification.verified,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
json!({
|
||||||
|
"id": self.id,
|
||||||
|
"username": self.username,
|
||||||
|
"display_name": self.display_name,
|
||||||
|
"relationship": relationship
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_guilds(&self) -> Result<Vec<String>, String> {
|
||||||
|
let members = get_collection("members")
|
||||||
|
.find(
|
||||||
|
doc! {
|
||||||
|
"_id.user": &self.id
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.map_err(|_| "Failed to fetch members.")?;
|
||||||
|
|
||||||
|
Ok(members
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|x| match x {
|
||||||
|
Ok(doc) => match doc.get_document("_id") {
|
||||||
|
Ok(id) => match id.get_str("guild") {
|
||||||
|
Ok(value) => Some(value.to_string()),
|
||||||
|
Err(_) => None,
|
||||||
|
},
|
||||||
|
Err(_) => None,
|
||||||
|
},
|
||||||
|
Err(_) => None,
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_dms(&self) -> Result<Vec<String>, String> {
|
||||||
|
let channels = get_collection("channels")
|
||||||
|
.find(
|
||||||
|
doc! {
|
||||||
|
"recipients": &self.id
|
||||||
|
},
|
||||||
|
FindOptions::builder().projection(doc! { "_id": 1 }).build(),
|
||||||
|
)
|
||||||
|
.map_err(|_| "Failed to fetch channel ids.")?;
|
||||||
|
|
||||||
|
Ok(channels
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|x| x.ok())
|
||||||
|
.filter_map(|x| match x.get_str("_id") {
|
||||||
|
Ok(value) => Some(value.to_string()),
|
||||||
|
Err(_) => None,
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_payload(self) -> Result<JsonValue, String> {
|
||||||
|
let v = vec![];
|
||||||
|
let relations = self.relations.as_ref().unwrap_or(&v);
|
||||||
|
|
||||||
|
let users: Vec<JsonValue> = fetch_users(&relations.iter().map(|x| x.id.clone()).collect())?
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| {
|
||||||
|
let id = x.id.clone();
|
||||||
|
x.serialise(relations.iter().find(|y| y.id == id).unwrap().status as i32)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let channels: Vec<JsonValue> = fetch_channels(&self.find_dms()?)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.serialise())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(json!({
|
||||||
|
"users": users,
|
||||||
|
"channels": channels,
|
||||||
|
"guilds": serialise_guilds_with_channels(&self.find_guilds()?)?,
|
||||||
|
"user": self.serialise(super::Relationship::SELF as i32)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CACHE: Arc<Mutex<LruCache<String, User>>> =
|
static ref CACHE: Arc<Mutex<LruCache<String, User>>> =
|
||||||
Arc::new(Mutex::new(LruCache::new(4_000_000)));
|
Arc::new(Mutex::new(LruCache::new(4_000_000)));
|
||||||
@@ -73,6 +168,52 @@ pub fn fetch_user(id: &str) -> Result<Option<User>, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fetch_users(ids: &Vec<String>) -> Result<Vec<User>, String> {
|
||||||
|
let mut missing = vec![];
|
||||||
|
let mut users = vec![];
|
||||||
|
|
||||||
|
{
|
||||||
|
if let Ok(mut cache) = CACHE.lock() {
|
||||||
|
for id in ids {
|
||||||
|
let existing = cache.get(id);
|
||||||
|
|
||||||
|
if let Some(user) = existing {
|
||||||
|
users.push((*user).clone());
|
||||||
|
} else {
|
||||||
|
missing.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err("Failed to lock cache.".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if missing.len() == 0 {
|
||||||
|
return Ok(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
let col = get_collection("users");
|
||||||
|
if let Ok(result) = col.find(doc! { "_id": { "$in": missing } }, None) {
|
||||||
|
for item in result {
|
||||||
|
let mut cache = CACHE.lock().unwrap();
|
||||||
|
if let Ok(doc) = item {
|
||||||
|
if let Ok(user) = from_bson(Bson::Document(doc)) as Result<User, _> {
|
||||||
|
cache.put(user.id.clone(), user.clone());
|
||||||
|
users.push(user);
|
||||||
|
} else {
|
||||||
|
return Err("Failed to deserialize user!".to_string());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err("Failed to fetch user.".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(users)
|
||||||
|
} else {
|
||||||
|
Err("Failed to fetch user from database.".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AuthError {
|
pub enum AuthError {
|
||||||
Failed,
|
Failed,
|
||||||
@@ -84,7 +225,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for User {
|
|||||||
type Error = AuthError;
|
type Error = AuthError;
|
||||||
|
|
||||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
|
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
|
||||||
let u = request.headers().get("x-user").next();
|
let u = request.headers().get("x-user").next();
|
||||||
let t = request.headers().get("x-auth-token").next();
|
let t = request.headers().get("x-auth-token").next();
|
||||||
|
|
||||||
if let Some(uid) = u {
|
if let Some(uid) = u {
|
||||||
@@ -143,17 +284,13 @@ pub fn process_event(event: &Notification) {
|
|||||||
if let Some(pos) = relations.iter().position(|x| x.id == ev.user) {
|
if let Some(pos) = relations.iter().position(|x| x.id == ev.user) {
|
||||||
relations.remove(pos);
|
relations.remove(pos);
|
||||||
}
|
}
|
||||||
|
} else if let Some(entry) = relations.iter_mut().find(|x| x.id == ev.user) {
|
||||||
|
entry.status = ev.status as u8;
|
||||||
} else {
|
} else {
|
||||||
if let Some(entry) = relations.iter_mut().find(|x| x.id == ev.user) {
|
relations.push(UserRelationship {
|
||||||
entry.status = ev.status as u8;
|
id: ev.id.clone(),
|
||||||
} else {
|
status: ev.status as u8,
|
||||||
relations.push(
|
});
|
||||||
UserRelationship {
|
|
||||||
id: ev.id.clone(),
|
|
||||||
status: ev.status as u8
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
61
src/email.rs
61
src/email.rs
@@ -1,61 +0,0 @@
|
|||||||
use reqwest::blocking::Client;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
fn public_uri() -> String {
|
|
||||||
env::var("PUBLIC_URI").expect("PUBLIC_URI not in environment variables!")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn portal() -> String {
|
|
||||||
env::var("PORTAL_URL").expect("PORTAL_URL not in environment variables!")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_email(target: String, subject: String, body: String, html: String) -> Result<(), ()> {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
map.insert("target", target.clone());
|
|
||||||
map.insert("subject", subject);
|
|
||||||
map.insert("body", body);
|
|
||||||
map.insert("html", html);
|
|
||||||
|
|
||||||
let client = Client::new();
|
|
||||||
match client.post(&portal()).json(&map).send() {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(_) => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_verification_email(email: String, code: String) -> bool {
|
|
||||||
let url = format!("{}/api/account/verify/{}", public_uri(), code);
|
|
||||||
send_email(
|
|
||||||
email,
|
|
||||||
"Verify your email!".to_string(),
|
|
||||||
format!("Verify your email here: {}", url),
|
|
||||||
format!("<a href=\"{}\">Click to verify your email!</a>", url),
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_password_reset(email: String, code: String) -> bool {
|
|
||||||
let url = format!("{}/api/account/reset/{}", public_uri(), code);
|
|
||||||
send_email(
|
|
||||||
email,
|
|
||||||
"Reset your password.".to_string(),
|
|
||||||
format!("Reset your password here: {}", url),
|
|
||||||
format!("<a href=\"{}\">Click to reset your password!</a>", url),
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_welcome_email(email: String, username: String) -> bool {
|
|
||||||
send_email(
|
|
||||||
email,
|
|
||||||
"Welcome to REVOLT!".to_string(),
|
|
||||||
format!("Welcome, {}! You can now use REVOLT.", username.clone()),
|
|
||||||
format!(
|
|
||||||
"<b>Welcome, {}!</b><br/>You can now use REVOLT.<br/><a href=\"{}\">Go to REVOLT</a>",
|
|
||||||
username.clone(),
|
|
||||||
public_uri()
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
14
src/main.rs
14
src/main.rs
@@ -9,19 +9,25 @@ extern crate bitfield;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
pub mod notifications;
|
|
||||||
pub mod database;
|
pub mod database;
|
||||||
|
pub mod notifications;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
pub mod email;
|
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
use dotenv;
|
|
||||||
use rocket_cors::AllowedOrigins;
|
use rocket_cors::AllowedOrigins;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
env_logger::init();
|
env_logger::init_from_env(
|
||||||
|
env_logger::Env::default()
|
||||||
|
.filter_or("RUST_LOG", "info")
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("Starting REVOLT server.");
|
||||||
|
|
||||||
|
util::variables::preflight_checks();
|
||||||
database::connect();
|
database::connect();
|
||||||
notifications::start_worker();
|
notifications::start_worker();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::state::{self, StateResult};
|
use super::state::{self, StateResult};
|
||||||
|
use crate::util::variables::WS_HOST;
|
||||||
|
|
||||||
use serde_json::{from_str, json, Value};
|
use serde_json::{from_str, json, Value};
|
||||||
use std::env;
|
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
use ws::{listen, CloseCode, Error, Handler, Handshake, Message, Result, Sender};
|
use ws::{listen, CloseCode, Error, Handler, Handshake, Message, Result, Sender};
|
||||||
|
|
||||||
@@ -36,8 +36,11 @@ impl Handler for Server {
|
|||||||
|
|
||||||
match state.try_authenticate(self.id.clone(), token.to_string()) {
|
match state.try_authenticate(self.id.clone(), token.to_string()) {
|
||||||
StateResult::Success(user_id) => {
|
StateResult::Success(user_id) => {
|
||||||
let user = crate::database::user::fetch_user(&user_id).unwrap().unwrap();
|
let user = crate::database::user::fetch_user(&user_id)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
self.user_id = Some(user_id);
|
||||||
|
|
||||||
self.sender.send(
|
self.sender.send(
|
||||||
json!({
|
json!({
|
||||||
"type": "authenticate",
|
"type": "authenticate",
|
||||||
@@ -45,18 +48,21 @@ impl Handler for Server {
|
|||||||
})
|
})
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.user_id = Some(user_id);
|
if let Ok(payload) = user.create_payload() {
|
||||||
self.sender.send(
|
self.sender.send(
|
||||||
json!({
|
json!({
|
||||||
"type": "ready",
|
"type": "ready",
|
||||||
"data": {
|
"data": payload
|
||||||
// ! FIXME: rewrite
|
})
|
||||||
"user": user,
|
.to_string(),
|
||||||
}
|
)
|
||||||
})
|
} else {
|
||||||
.to_string(),
|
// ! TODO: FIXME: ALL THE NOTIFICATIONS CODE NEEDS TO BE
|
||||||
)
|
// ! RESTRUCTURED, IT IS UTTER GARBAGE. :)))))
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
StateResult::DatabaseError => self.sender.send(
|
StateResult::DatabaseError => self.sender.send(
|
||||||
json!({
|
json!({
|
||||||
@@ -120,13 +126,10 @@ impl Handler for Server {
|
|||||||
pub fn launch_server() {
|
pub fn launch_server() {
|
||||||
state::init();
|
state::init();
|
||||||
|
|
||||||
listen(
|
listen(WS_HOST.to_string(), |sender| Server {
|
||||||
env::var("WS_HOST").unwrap_or("0.0.0.0:9999".to_string()),
|
sender,
|
||||||
|sender| Server {
|
user_id: None,
|
||||||
sender,
|
id: Ulid::new().to_string(),
|
||||||
user_id: None,
|
})
|
||||||
id: Ulid::new().to_string(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
use super::Response;
|
use super::Response;
|
||||||
use crate::database;
|
use crate::database;
|
||||||
use crate::email;
|
use crate::util::{captcha, email, gen_token};
|
||||||
use crate::util::gen_token;
|
use crate::util::variables::{DISABLE_REGISTRATION, USE_EMAIL};
|
||||||
|
|
||||||
use bcrypt::{hash, verify};
|
use bcrypt::{hash, verify};
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use database::user::User;
|
use database::user::User;
|
||||||
|
use log::error;
|
||||||
use mongodb::bson::{doc, from_bson, Bson};
|
use mongodb::bson::{doc, from_bson, Bson};
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -17,6 +18,7 @@ pub struct Create {
|
|||||||
username: String,
|
username: String,
|
||||||
password: String,
|
password: String,
|
||||||
email: String,
|
email: String,
|
||||||
|
captcha: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// create a new Revolt account
|
/// create a new Revolt account
|
||||||
@@ -28,6 +30,14 @@ pub struct Create {
|
|||||||
/// (3) add user and send email verification
|
/// (3) add user and send email verification
|
||||||
#[post("/create", data = "<info>")]
|
#[post("/create", data = "<info>")]
|
||||||
pub fn create(info: Json<Create>) -> Response {
|
pub fn create(info: Json<Create>) -> Response {
|
||||||
|
if let Err(error) = captcha::verify(&info.captcha) {
|
||||||
|
return Response::BadRequest(json!({ "error": error }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if *DISABLE_REGISTRATION {
|
||||||
|
return Response::BadRequest(json!({ "error": "Registration disabled." }));
|
||||||
|
}
|
||||||
|
|
||||||
let col = database::get_collection("users");
|
let col = database::get_collection("users");
|
||||||
|
|
||||||
if info.username.len() < 2 || info.username.len() > 32 {
|
if info.username.len() < 2 || info.username.len() > 32 {
|
||||||
@@ -64,30 +74,45 @@ pub fn create(info: Json<Create>) -> Response {
|
|||||||
let access_token = gen_token(92);
|
let access_token = gen_token(92);
|
||||||
let code = gen_token(48);
|
let code = gen_token(48);
|
||||||
|
|
||||||
|
let email_verification = match *USE_EMAIL {
|
||||||
|
true => doc! {
|
||||||
|
"verified": false,
|
||||||
|
"target": info.email.clone(),
|
||||||
|
"expiry": Bson::DateTime(Utc::now() + chrono::Duration::days(1)),
|
||||||
|
"rate_limit": Bson::DateTime(Utc::now() + chrono::Duration::minutes(1)),
|
||||||
|
"code": code.clone(),
|
||||||
|
},
|
||||||
|
false => doc! {
|
||||||
|
"verified": true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = Ulid::new().to_string();
|
||||||
match col.insert_one(
|
match col.insert_one(
|
||||||
doc! {
|
doc! {
|
||||||
"_id": Ulid::new().to_string(),
|
"_id": &id,
|
||||||
"email": info.email.clone(),
|
"email": info.email.clone(),
|
||||||
"username": info.username.clone(),
|
"username": info.username.clone(),
|
||||||
"display_name": info.username.clone(),
|
"display_name": info.username.clone(),
|
||||||
"password": hashed,
|
"password": hashed,
|
||||||
"access_token": access_token,
|
"access_token": &access_token,
|
||||||
"email_verification": {
|
"email_verification": email_verification
|
||||||
"verified": false,
|
|
||||||
"target": info.email.clone(),
|
|
||||||
"expiry": Bson::DateTime(Utc::now() + chrono::Duration::days(1)),
|
|
||||||
"rate_limit": Bson::DateTime(Utc::now() + chrono::Duration::minutes(1)),
|
|
||||||
"code": code.clone(),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let sent = email::send_verification_email(info.email.clone(), code);
|
if *USE_EMAIL {
|
||||||
|
let sent = email::send_verification_email(info.email.clone(), code);
|
||||||
|
|
||||||
Response::Success(json!({
|
Response::Success(json!({
|
||||||
"email_sent": sent,
|
"email_sent": sent,
|
||||||
}))
|
}))
|
||||||
|
} else {
|
||||||
|
Response::Success(json!({
|
||||||
|
"id": id,
|
||||||
|
"access_token": access_token
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Response::InternalServerError(json!({ "error": "Failed to create account." }))
|
Response::InternalServerError(json!({ "error": "Failed to create account." }))
|
||||||
@@ -138,7 +163,11 @@ pub fn verify_email(code: String) -> Response {
|
|||||||
)
|
)
|
||||||
.expect("Failed to update user!");
|
.expect("Failed to update user!");
|
||||||
|
|
||||||
email::send_welcome_email(target.to_string(), user.username);
|
if *USE_EMAIL {
|
||||||
|
if let Err(err) = email::send_welcome_email(target.to_string(), user.username) {
|
||||||
|
error!("Failed to send welcome email! {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Response::Redirect(super::Redirect::to("https://app.revolt.chat"))
|
Response::Redirect(super::Redirect::to("https://app.revolt.chat"))
|
||||||
}
|
}
|
||||||
@@ -150,6 +179,7 @@ pub fn verify_email(code: String) -> Response {
|
|||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Resend {
|
pub struct Resend {
|
||||||
email: String,
|
email: String,
|
||||||
|
captcha: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// resend a verification email
|
/// resend a verification email
|
||||||
@@ -158,6 +188,10 @@ pub struct Resend {
|
|||||||
/// (3) resend the email
|
/// (3) resend the email
|
||||||
#[post("/resend", data = "<info>")]
|
#[post("/resend", data = "<info>")]
|
||||||
pub fn resend_email(info: Json<Resend>) -> Response {
|
pub fn resend_email(info: Json<Resend>) -> Response {
|
||||||
|
if let Err(error) = captcha::verify(&info.captcha) {
|
||||||
|
return Response::BadRequest(json!({ "error": error }));
|
||||||
|
}
|
||||||
|
|
||||||
let col = database::get_collection("users");
|
let col = database::get_collection("users");
|
||||||
|
|
||||||
if let Some(u) = col
|
if let Some(u) = col
|
||||||
@@ -202,12 +236,11 @@ pub fn resend_email(info: Json<Resend>) -> Response {
|
|||||||
None,
|
None,
|
||||||
).expect("Failed to update user!");
|
).expect("Failed to update user!");
|
||||||
|
|
||||||
match email::send_verification_email(info.email.to_string(), code) {
|
if let Err(err) = email::send_verification_email(info.email.clone(), code) {
|
||||||
true => Response::Result(super::Status::Ok),
|
return Response::InternalServerError(json!({ "error": err }));
|
||||||
false => Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to send email! Likely an issue with the backend API." }),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Response::Result(super::Status::Ok)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Response::NotFound(json!({ "error": "Email not found or pending verification!" }))
|
Response::NotFound(json!({ "error": "Email not found or pending verification!" }))
|
||||||
@@ -218,6 +251,7 @@ pub fn resend_email(info: Json<Resend>) -> Response {
|
|||||||
pub struct Login {
|
pub struct Login {
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
|
captcha: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// login to a Revolt account
|
/// login to a Revolt account
|
||||||
@@ -226,6 +260,10 @@ pub struct Login {
|
|||||||
/// (3) return access token
|
/// (3) return access token
|
||||||
#[post("/login", data = "<info>")]
|
#[post("/login", data = "<info>")]
|
||||||
pub fn login(info: Json<Login>) -> Response {
|
pub fn login(info: Json<Login>) -> Response {
|
||||||
|
if let Err(error) = captcha::verify(&info.captcha) {
|
||||||
|
return Response::BadRequest(json!({ "error": error }));
|
||||||
|
}
|
||||||
|
|
||||||
let col = database::get_collection("users");
|
let col = database::get_collection("users");
|
||||||
|
|
||||||
if let Some(u) = col
|
if let Some(u) = col
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::Response;
|
use super::Response;
|
||||||
use crate::database::{
|
use crate::database::{
|
||||||
self, channel::Channel, get_relationship, get_relationship_internal, message::Message,
|
self, channel::Channel, get_relationship, get_relationship_internal, message::Message,
|
||||||
Permission, PermissionCalculator, Relationship, user::User
|
user::User, Permission, PermissionCalculator, Relationship,
|
||||||
};
|
};
|
||||||
use crate::notifications::{
|
use crate::notifications::{
|
||||||
self,
|
self,
|
||||||
@@ -129,51 +129,7 @@ pub fn create_group(user: User, info: Json<CreateGroup>) -> Response {
|
|||||||
#[get("/<target>")]
|
#[get("/<target>")]
|
||||||
pub fn channel(user: User, target: Channel) -> Option<Response> {
|
pub fn channel(user: User, target: Channel) -> Option<Response> {
|
||||||
with_permissions!(user, target);
|
with_permissions!(user, target);
|
||||||
|
Some(Response::Success(target.serialise()))
|
||||||
match target.channel_type {
|
|
||||||
0 => Some(Response::Success(json!({
|
|
||||||
"id": target.id,
|
|
||||||
"type": target.channel_type,
|
|
||||||
"last_message": target.last_message,
|
|
||||||
"recipients": target.recipients,
|
|
||||||
}))),
|
|
||||||
1 => {
|
|
||||||
/*if let Some(info) = target.fetch_data(doc! {
|
|
||||||
"name": 1,
|
|
||||||
"description": 1,
|
|
||||||
"owner": 1,
|
|
||||||
}) {*/
|
|
||||||
Some(Response::Success(json!({
|
|
||||||
"id": target.id,
|
|
||||||
"type": target.channel_type,
|
|
||||||
"last_message": target.last_message,
|
|
||||||
"recipients": target.recipients,
|
|
||||||
"name": target.name,
|
|
||||||
"owner": target.owner,
|
|
||||||
"description": target.description,
|
|
||||||
})))
|
|
||||||
/*} else {
|
|
||||||
None
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
2 => {
|
|
||||||
/*if let Some(info) = target.fetch_data(doc! {
|
|
||||||
"name": 1,
|
|
||||||
"description": 1,
|
|
||||||
}) {*/
|
|
||||||
Some(Response::Success(json!({
|
|
||||||
"id": target.id,
|
|
||||||
"type": target.channel_type,
|
|
||||||
"guild": target.guild,
|
|
||||||
"name": target.name,
|
|
||||||
"description": target.description,
|
|
||||||
})))
|
|
||||||
/*} else {
|
|
||||||
None
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [groups] add user to channel
|
/// [groups] add user to channel
|
||||||
@@ -447,7 +403,8 @@ pub fn delete(user: User, target: Channel) -> Option<Response> {
|
|||||||
"$pull": {
|
"$pull": {
|
||||||
"invites": {
|
"invites": {
|
||||||
"channel": &target.id
|
"channel": &target.id
|
||||||
}
|
},
|
||||||
|
"channels": &target.id
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
@@ -549,11 +506,7 @@ pub struct SendMessage {
|
|||||||
|
|
||||||
/// send a message to a channel
|
/// send a message to a channel
|
||||||
#[post("/<target>/messages", data = "<message>")]
|
#[post("/<target>/messages", data = "<message>")]
|
||||||
pub fn send_message(
|
pub fn send_message(user: User, target: Channel, message: Json<SendMessage>) -> Option<Response> {
|
||||||
user: User,
|
|
||||||
target: Channel,
|
|
||||||
message: Json<SendMessage>,
|
|
||||||
) -> Option<Response> {
|
|
||||||
let permissions = with_permissions!(user, target);
|
let permissions = with_permissions!(user, target);
|
||||||
|
|
||||||
if !permissions.get_send_messages() {
|
if !permissions.get_send_messages() {
|
||||||
@@ -700,10 +653,8 @@ pub fn edit_message(
|
|||||||
pub fn delete_message(user: User, target: Channel, message: Message) -> Option<Response> {
|
pub fn delete_message(user: User, target: Channel, message: Message) -> Option<Response> {
|
||||||
let permissions = with_permissions!(user, target);
|
let permissions = with_permissions!(user, target);
|
||||||
|
|
||||||
if !permissions.get_manage_messages() {
|
if !permissions.get_manage_messages() && message.author != user.id {
|
||||||
if message.author != user.id {
|
return Some(Response::LackingPermission(Permission::ManageMessages));
|
||||||
return Some(Response::LackingPermission(Permission::ManageMessages));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let col = database::get_collection("messages");
|
let col = database::get_collection("messages");
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use super::channel::ChannelType;
|
|||||||
use super::Response;
|
use super::Response;
|
||||||
use crate::database::guild::{fetch_member as get_member, get_invite, Guild, MemberKey};
|
use crate::database::guild::{fetch_member as get_member, get_invite, Guild, MemberKey};
|
||||||
use crate::database::{
|
use crate::database::{
|
||||||
self, channel::fetch_channel, channel::Channel, Permission, PermissionCalculator, user::User
|
self, channel::fetch_channel, channel::Channel, guild::serialise_guilds_with_channels,
|
||||||
|
user::User, Permission, PermissionCalculator,
|
||||||
};
|
};
|
||||||
use crate::notifications::{
|
use crate::notifications::{
|
||||||
self,
|
self,
|
||||||
@@ -10,13 +11,14 @@ use crate::notifications::{
|
|||||||
};
|
};
|
||||||
use crate::util::gen_token;
|
use crate::util::gen_token;
|
||||||
|
|
||||||
use mongodb::bson::{doc, from_bson, Bson};
|
use mongodb::bson::{doc, Bson};
|
||||||
use mongodb::options::{FindOneOptions, FindOptions};
|
use mongodb::options::{FindOneOptions, FindOptions};
|
||||||
use rocket::request::Form;
|
use rocket::request::Form;
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
|
// ! FIXME: GET RID OF THIS
|
||||||
macro_rules! with_permissions {
|
macro_rules! with_permissions {
|
||||||
($user: expr, $target: expr) => {{
|
($user: expr, $target: expr) => {{
|
||||||
let permissions = PermissionCalculator::new($user.clone())
|
let permissions = PermissionCalculator::new($user.clone())
|
||||||
@@ -35,53 +37,9 @@ macro_rules! with_permissions {
|
|||||||
/// fetch your guilds
|
/// fetch your guilds
|
||||||
#[get("/@me")]
|
#[get("/@me")]
|
||||||
pub fn my_guilds(user: User) -> Response {
|
pub fn my_guilds(user: User) -> Response {
|
||||||
if let Ok(result) = database::get_collection("members").find(
|
if let Ok(gids) = user.find_guilds() {
|
||||||
doc! {
|
if let Ok(data) = serialise_guilds_with_channels(&gids) {
|
||||||
"_id.user": &user.id
|
Response::Success(json!(data))
|
||||||
},
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
let mut guilds = vec![];
|
|
||||||
for item in result {
|
|
||||||
if let Ok(entry) = item {
|
|
||||||
guilds.push(Bson::String(
|
|
||||||
entry
|
|
||||||
.get_document("_id")
|
|
||||||
.unwrap()
|
|
||||||
.get_str("guild")
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(result) = database::get_collection("guilds").find(
|
|
||||||
doc! {
|
|
||||||
"_id": {
|
|
||||||
"$in": guilds
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FindOptions::builder()
|
|
||||||
.projection(doc! {
|
|
||||||
"_id": 1,
|
|
||||||
"name": 1,
|
|
||||||
"description": 1,
|
|
||||||
"owner": 1,
|
|
||||||
})
|
|
||||||
.build(),
|
|
||||||
) {
|
|
||||||
let mut parsed = vec![];
|
|
||||||
for item in result {
|
|
||||||
let doc = item.unwrap();
|
|
||||||
parsed.push(json!({
|
|
||||||
"id": doc.get_str("_id").unwrap(),
|
|
||||||
"name": doc.get_str("name").unwrap(),
|
|
||||||
"description": doc.get_str("description").unwrap(),
|
|
||||||
"owner": doc.get_str("owner").unwrap(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response::Success(json!(parsed))
|
|
||||||
} else {
|
} else {
|
||||||
Response::InternalServerError(json!({ "error": "Failed to fetch guilds." }))
|
Response::InternalServerError(json!({ "error": "Failed to fetch guilds." }))
|
||||||
}
|
}
|
||||||
@@ -95,39 +53,12 @@ pub fn my_guilds(user: User) -> Response {
|
|||||||
pub fn guild(user: User, target: Guild) -> Option<Response> {
|
pub fn guild(user: User, target: Guild) -> Option<Response> {
|
||||||
with_permissions!(user, target);
|
with_permissions!(user, target);
|
||||||
|
|
||||||
let col = database::get_collection("channels");
|
if let Ok(result) = target.seralise_with_channels() {
|
||||||
match col.find(
|
Some(Response::Success(result))
|
||||||
doc! {
|
} else {
|
||||||
"type": 2,
|
Some(Response::InternalServerError(
|
||||||
"guild": &target.id,
|
json!({ "error": "Failed to fetch channels!" }),
|
||||||
},
|
))
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(results) => {
|
|
||||||
let mut channels = vec![];
|
|
||||||
for item in results {
|
|
||||||
if let Ok(entry) = item {
|
|
||||||
if let Ok(channel) = from_bson(Bson::Document(entry)) as Result<Channel, _> {
|
|
||||||
channels.push(json!({
|
|
||||||
"id": channel.id,
|
|
||||||
"name": channel.name,
|
|
||||||
"description": channel.description,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Response::Success(json!({
|
|
||||||
"id": target.id,
|
|
||||||
"name": target.name,
|
|
||||||
"description": target.description,
|
|
||||||
"owner": target.owner,
|
|
||||||
"channels": channels,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
Err(_) => Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to fetch channels." }),
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,33 +156,31 @@ pub fn remove_guild(user: User, target: Guild) -> Option<Response> {
|
|||||||
json!({ "error": "Could not fetch channels." }),
|
json!({ "error": "Could not fetch channels." }),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else {
|
} else if database::get_collection("members")
|
||||||
if database::get_collection("members")
|
.delete_one(
|
||||||
.delete_one(
|
doc! {
|
||||||
doc! {
|
"_id.guild": &target.id,
|
||||||
"_id.guild": &target.id,
|
"_id.user": &user.id,
|
||||||
"_id.user": &user.id,
|
},
|
||||||
},
|
None,
|
||||||
None,
|
)
|
||||||
)
|
.is_ok()
|
||||||
.is_ok()
|
{
|
||||||
{
|
notifications::send_message_threaded(
|
||||||
notifications::send_message_threaded(
|
None,
|
||||||
None,
|
target.id.clone(),
|
||||||
target.id.clone(),
|
Notification::guild_user_leave(UserLeave {
|
||||||
Notification::guild_user_leave(UserLeave {
|
id: target.id.clone(),
|
||||||
id: target.id.clone(),
|
user: user.id.clone(),
|
||||||
user: user.id.clone(),
|
banned: false,
|
||||||
banned: false,
|
}),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
Some(Response::Result(super::Status::Ok))
|
||||||
} else {
|
} else {
|
||||||
Some(Response::InternalServerError(
|
Some(Response::InternalServerError(
|
||||||
json!({ "error": "Failed to remove you from the guild." }),
|
json!({ "error": "Failed to remove you from the guild." }),
|
||||||
))
|
))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,20 +234,39 @@ pub fn create_channel(user: User, target: Guild, info: Json<CreateChannel>) -> O
|
|||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
notifications::send_message_threaded(
|
if database::get_collection("guilds")
|
||||||
None,
|
.update_one(
|
||||||
target.id.clone(),
|
doc! {
|
||||||
Notification::guild_channel_create(ChannelCreate {
|
"_id": &target.id
|
||||||
id: target.id.clone(),
|
},
|
||||||
channel: id.clone(),
|
doc! {
|
||||||
name: name.clone(),
|
"$addToSet": {
|
||||||
description: description.clone(),
|
"channels": &id
|
||||||
}),
|
}
|
||||||
);
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
notifications::send_message_threaded(
|
||||||
|
None,
|
||||||
|
target.id.clone(),
|
||||||
|
Notification::guild_channel_create(ChannelCreate {
|
||||||
|
id: target.id.clone(),
|
||||||
|
channel: id.clone(),
|
||||||
|
name: name.clone(),
|
||||||
|
description: description.clone(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
Some(Response::Success(json!({ "id": &id })))
|
Some(Response::Success(json!({ "id": &id })))
|
||||||
|
} else {
|
||||||
|
Some(Response::InternalServerError(
|
||||||
|
json!({ "error": "Couldn't save channel list." }),
|
||||||
|
))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(Response::BadRequest(
|
Some(Response::InternalServerError(
|
||||||
json!({ "error": "Couldn't create channel." }),
|
json!({ "error": "Couldn't create channel." }),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -379,10 +327,8 @@ pub fn remove_invite(user: User, target: Guild, code: String) -> Option<Response
|
|||||||
let (permissions, _) = with_permissions!(user, target);
|
let (permissions, _) = with_permissions!(user, target);
|
||||||
|
|
||||||
if let Some((guild_id, _, invite)) = get_invite(&code, None) {
|
if let Some((guild_id, _, invite)) = get_invite(&code, None) {
|
||||||
if invite.creator != user.id {
|
if invite.creator != user.id && !permissions.get_manage_server() {
|
||||||
if !permissions.get_manage_server() {
|
return Some(Response::LackingPermission(Permission::ManageServer));
|
||||||
return Some(Response::LackingPermission(Permission::ManageServer));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if database::get_collection("guilds")
|
if database::get_collection("guilds")
|
||||||
@@ -590,6 +536,7 @@ pub fn create_guild(user: User, info: Json<CreateGuild>) -> Response {
|
|||||||
"name": name,
|
"name": name,
|
||||||
"description": description,
|
"description": description,
|
||||||
"owner": &user.id,
|
"owner": &user.id,
|
||||||
|
"channels": [ channel_id.clone() ],
|
||||||
"invites": [],
|
"invites": [],
|
||||||
"bans": [],
|
"bans": [],
|
||||||
"default_permissions": 51,
|
"default_permissions": 51,
|
||||||
@@ -673,14 +620,16 @@ pub fn kick_member(user: User, target: Guild, other: String) -> Option<Response>
|
|||||||
return Some(Response::LackingPermission(Permission::KickMembers));
|
return Some(Response::LackingPermission(Permission::KickMembers));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(result) = get_member(MemberKey( target.id.clone(), other.clone() )) {
|
if let Ok(result) = get_member(MemberKey(target.id.clone(), other.clone())) {
|
||||||
if result.is_none() {
|
if result.is_none() {
|
||||||
return Some(Response::BadRequest(
|
return Some(Response::BadRequest(
|
||||||
json!({ "error": "User not part of guild." }),
|
json!({ "error": "User not part of guild." }),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Some(Response::InternalServerError(json!({ "error": "Failed to fetch member." })))
|
return Some(Response::InternalServerError(
|
||||||
|
json!({ "error": "Failed to fetch member." }),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if database::get_collection("members")
|
if database::get_collection("members")
|
||||||
@@ -743,14 +692,16 @@ pub fn ban_member(
|
|||||||
return Some(Response::LackingPermission(Permission::BanMembers));
|
return Some(Response::LackingPermission(Permission::BanMembers));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(result) = get_member(MemberKey( target.id.clone(), other.clone() )) {
|
if let Ok(result) = get_member(MemberKey(target.id.clone(), other.clone())) {
|
||||||
if result.is_none() {
|
if result.is_none() {
|
||||||
return Some(Response::BadRequest(
|
return Some(Response::BadRequest(
|
||||||
json!({ "error": "User not part of guild." }),
|
json!({ "error": "User not part of guild." }),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Some(Response::InternalServerError(json!({ "error": "Failed to fetch member." })))
|
return Some(Response::InternalServerError(
|
||||||
|
json!({ "error": "Failed to fetch member." }),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if database::get_collection("guilds")
|
if database::get_collection("guilds")
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use super::Response;
|
use super::Response;
|
||||||
|
use crate::util::variables::{USE_EMAIL, DISABLE_REGISTRATION, USE_HCAPTCHA};
|
||||||
|
|
||||||
use mongodb::bson::doc;
|
use mongodb::bson::doc;
|
||||||
|
|
||||||
@@ -6,11 +7,16 @@ use mongodb::bson::doc;
|
|||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub fn root() -> Response {
|
pub fn root() -> Response {
|
||||||
Response::Success(json!({
|
Response::Success(json!({
|
||||||
"revolt": "0.2.7",
|
"revolt": "0.2.10",
|
||||||
"version": {
|
"version": {
|
||||||
"major": 0,
|
"major": 0,
|
||||||
"minor": 2,
|
"minor": 2,
|
||||||
"patch": 7
|
"patch": 10
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"registration": !*DISABLE_REGISTRATION,
|
||||||
|
"captcha": *USE_HCAPTCHA,
|
||||||
|
"email": *USE_EMAIL,
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use super::Response;
|
use super::Response;
|
||||||
use crate::database::{self, get_relationship, get_relationship_internal, mutual, Relationship, user::User};
|
use crate::database::{
|
||||||
|
self, get_relationship, get_relationship_internal, user::User, Relationship,
|
||||||
|
};
|
||||||
use crate::notifications::{
|
use crate::notifications::{
|
||||||
self,
|
self,
|
||||||
events::{users::*, Notification},
|
events::{users::*, Notification},
|
||||||
@@ -15,29 +17,14 @@ use ulid::Ulid;
|
|||||||
/// retrieve your user information
|
/// retrieve your user information
|
||||||
#[get("/@me")]
|
#[get("/@me")]
|
||||||
pub fn me(user: User) -> Response {
|
pub fn me(user: User) -> Response {
|
||||||
Response::Success(json!({
|
Response::Success(user.serialise(Relationship::SELF as i32))
|
||||||
"id": user.id,
|
|
||||||
"username": user.username,
|
|
||||||
"display_name": user.display_name,
|
|
||||||
"email": user.email,
|
|
||||||
"verified": user.email_verification.verified,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// retrieve another user's information
|
/// retrieve another user's information
|
||||||
#[get("/<target>")]
|
#[get("/<target>")]
|
||||||
pub fn user(user: User, target: User) -> Response {
|
pub fn user(user: User, target: User) -> Response {
|
||||||
Response::Success(json!({
|
let relationship = get_relationship(&user, &target) as i32;
|
||||||
"id": target.id,
|
Response::Success(user.serialise(relationship))
|
||||||
"username": target.username,
|
|
||||||
"display_name": target.display_name,
|
|
||||||
"relationship": get_relationship(&user, &target) as i32,
|
|
||||||
"mutual": {
|
|
||||||
"guilds": mutual::find_mutual_guilds(&user.id, &target.id),
|
|
||||||
"friends": mutual::find_mutual_friends(&user.id, &target.id),
|
|
||||||
"groups": mutual::find_mutual_groups(&user.id, &target.id),
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
|||||||
43
src/util/captcha.rs
Normal file
43
src/util/captcha.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use crate::util::variables::{HCAPTCHA_KEY, USE_HCAPTCHA};
|
||||||
|
|
||||||
|
use reqwest::blocking::Client;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct CaptchaResponse {
|
||||||
|
success: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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()
|
||||||
|
{
|
||||||
|
let result: CaptchaResponse = response
|
||||||
|
.json()
|
||||||
|
.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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/util/email.rs
Normal file
96
src/util/email.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@ use hashbrown::HashSet;
|
|||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
|
pub mod captcha;
|
||||||
|
pub mod email;
|
||||||
|
pub mod variables;
|
||||||
|
|
||||||
pub fn vec_to_set<T: Clone + Eq + std::hash::Hash>(data: &[T]) -> HashSet<T> {
|
pub fn vec_to_set<T: Clone + Eq + std::hash::Hash>(data: &[T]) -> HashSet<T> {
|
||||||
HashSet::from_iter(data.iter().cloned())
|
HashSet::from_iter(data.iter().cloned())
|
||||||
}
|
}
|
||||||
|
|||||||
66
src/util/variables.rs
Normal file
66
src/util/variables.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
use log::warn;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
// Application Settings
|
||||||
|
pub static ref MONGO_URI: String =
|
||||||
|
env::var("REVOLT_MONGO_URI").expect("Missing REVOLT_MONGO_URI environment variable.");
|
||||||
|
pub static ref PUBLIC_URL: String =
|
||||||
|
env::var("REVOLT_PUBLIC_URL").expect("Missing REVOLT_PUBLIC_URL environment variable.");
|
||||||
|
pub static ref HCAPTCHA_KEY: String =
|
||||||
|
env::var("REVOLT_HCAPTCHA_KEY").unwrap_or_else(|_| "".to_string());
|
||||||
|
pub static ref WS_HOST: String =
|
||||||
|
env::var("REVOLT_WS_HOST").unwrap_or_else(|_| "0.0.0.0:9999".to_string());
|
||||||
|
|
||||||
|
// Application Flags
|
||||||
|
pub static ref DISABLE_REGISTRATION: bool = env::var("REVOLT_DISABLE_REGISTRATION").map_or(false, |v| v == "*1");
|
||||||
|
pub static ref USE_EMAIL: bool = env::var("REVOLT_USE_EMAIL_VERIFICATION").map_or(
|
||||||
|
env::var("REVOLT_SMTP_HOST").is_ok()
|
||||||
|
&& env::var("REVOLT_SMTP_USERNAME").is_ok()
|
||||||
|
&& env::var("REVOLT_SMTP_PASSWORD").is_ok()
|
||||||
|
&& env::var("REVOLT_SMTP_FROM").is_ok(),
|
||||||
|
|v| v == *"1"
|
||||||
|
);
|
||||||
|
pub static ref USE_HCAPTCHA: bool = env::var("REVOLT_HCAPTCHA_KEY").is_ok();
|
||||||
|
|
||||||
|
// SMTP Settings
|
||||||
|
pub static ref SMTP_HOST: String =
|
||||||
|
env::var("REVOLT_SMTP_HOST").unwrap_or_else(|_| "".to_string());
|
||||||
|
pub static ref SMTP_USERNAME: String =
|
||||||
|
env::var("SMTP_USERNAME").unwrap_or_else(|_| "".to_string());
|
||||||
|
pub static ref SMTP_PASSWORD: String =
|
||||||
|
env::var("SMTP_PASSWORD").unwrap_or_else(|_| "".to_string());
|
||||||
|
pub static ref SMTP_FROM: String = env::var("SMTP_FROM").unwrap_or_else(|_| "".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn preflight_checks() {
|
||||||
|
if *USE_EMAIL == false {
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
if !env::var("REVOLT_UNSAFE_NO_EMAIL")
|
||||||
|
.map_or(false, |v| v == *"1") {
|
||||||
|
panic!(
|
||||||
|
"Not letting you run this in production, set REVOLT_UNSAFE_NO_EMAIL=1 to run."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
warn!("No SMTP settings specified! Remember to configure email.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if *USE_HCAPTCHA == false {
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
if !env::var("REVOLT_UNSAFE_NO_CAPTCHA")
|
||||||
|
.map_or(false, |v| v == *"1") {
|
||||||
|
panic!("Not letting you run this in production, set REVOLT_UNSAFE_NO_CAPTCHA=1 to run.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
warn!("No Captcha key specified! Remember to add hCaptcha key.");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user