Compare commits
33
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b18e983c9 | ||
|
|
c0e64c9052 | ||
|
|
0d54ccd342 | ||
|
|
d4d5b23c7d | ||
|
|
a641034895 | ||
|
|
9dd24526ef | ||
|
|
6d4f772d78 | ||
|
|
4c4dada3da | ||
|
|
cf82972a80 | ||
|
|
ea00f0fec1 | ||
|
|
226dbca6e0 | ||
|
|
e4b5a23f71 | ||
|
|
9663caa1ff | ||
|
|
f9f0f409a8 | ||
|
|
8f4d6c864b | ||
|
|
513461c99a | ||
|
|
41ef76ff3e | ||
|
|
c451e431cd | ||
|
|
09a848f5a6 | ||
|
|
308193377d | ||
|
|
3d3326e68a | ||
|
|
88c2232a6c | ||
|
|
25016efbc2 | ||
|
|
db63ac4064 | ||
|
|
1f593a1708 | ||
|
|
219c16a69c | ||
|
|
aed8f69f34 | ||
|
|
196a856c68 | ||
|
|
9bf60e87e9 | ||
|
|
b90e7a4412 | ||
|
|
cb813e91f0 | ||
|
|
a6bc9fcbb3 | ||
|
|
cd0737911b |
@@ -57,7 +57,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
TEST_DB: REFERENCE
|
TEST_DB: REFERENCE
|
||||||
run: |
|
run: |
|
||||||
cargo run --bin revolt-delta &
|
cargo build --bin revolt-delta && (cargo run --bin revolt-delta &)
|
||||||
|
|
||||||
- name: Wait for API to go up
|
- name: Wait for API to go up
|
||||||
if: github.event_name != 'pull_request' && github.ref_name == 'master'
|
if: github.event_name != 'pull_request' && github.ref_name == 'master'
|
||||||
|
|||||||
Generated
+279
-111
@@ -104,6 +104,12 @@ version = "1.0.68"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arc-swap"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "arrayref"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@@ -165,7 +171,7 @@ dependencies = [
|
|||||||
"futures-lite",
|
"futures-lite",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"tokio 0.2.25",
|
"tokio 0.2.25",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -246,7 +252,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
@@ -315,7 +321,7 @@ dependencies = [
|
|||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"tungstenite",
|
"tungstenite",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -588,9 +594,19 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.1.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes-utils"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35"
|
||||||
|
dependencies = [
|
||||||
|
"bytes 1.5.0",
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cache-padded"
|
name = "cache-padded"
|
||||||
@@ -612,7 +628,7 @@ dependencies = [
|
|||||||
"instant",
|
"instant",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -712,11 +728,11 @@ version = "4.6.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948"
|
checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tokio-util 0.7.2",
|
"tokio-util 0.7.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -784,6 +800,12 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie-factory"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@@ -809,6 +831,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc16"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@@ -1002,7 +1030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "16a2561fd313df162315935989dceb8c99db4ee1933358270a57a3cfb8c957f3"
|
checksum = "16a2561fd313df162315935989dceb8c99db4ee1933358270a57a3cfb8c957f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-queue",
|
"crossbeam-queue",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1348,6 +1376,15 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -1371,14 +1408,40 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.0.1"
|
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 = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matches",
|
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fred"
|
||||||
|
version = "8.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3b2a2ac060e3266004c552235c241b481e438e2b1ea75715ea1176914ef2868"
|
||||||
|
dependencies = [
|
||||||
|
"arc-swap",
|
||||||
|
"async-trait",
|
||||||
|
"bytes 1.5.0",
|
||||||
|
"bytes-utils",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"float-cmp",
|
||||||
|
"futures",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"redis-protocol",
|
||||||
|
"semver 1.0.9",
|
||||||
|
"socket2 0.5.5",
|
||||||
|
"tokio 1.35.1",
|
||||||
|
"tokio-stream",
|
||||||
|
"tokio-util 0.7.2",
|
||||||
|
"url",
|
||||||
|
"urlencoding",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fuchsia-cprng"
|
name = "fuchsia-cprng"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -1460,7 +1523,7 @@ dependencies = [
|
|||||||
"futures-io",
|
"futures-io",
|
||||||
"memchr",
|
"memchr",
|
||||||
"parking",
|
"parking",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"waker-fn",
|
"waker-fn",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1472,7 +1535,7 @@ checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1517,7 +1580,7 @@ dependencies = [
|
|||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
@@ -1661,7 +1724,7 @@ version = "0.3.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57"
|
checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"fnv",
|
"fnv",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@@ -1669,7 +1732,7 @@ dependencies = [
|
|||||||
"http",
|
"http",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tokio-util 0.7.2",
|
"tokio-util 0.7.2",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@@ -1802,7 +1865,7 @@ version = "0.2.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb"
|
checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itoa 1.0.2",
|
"itoa 1.0.2",
|
||||||
]
|
]
|
||||||
@@ -1823,9 +1886,9 @@ version = "0.4.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
|
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"http",
|
"http",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1885,7 +1948,7 @@ version = "0.14.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f"
|
checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -1895,9 +1958,9 @@ dependencies = [
|
|||||||
"httparse",
|
"httparse",
|
||||||
"httpdate 1.0.2",
|
"httpdate 1.0.2",
|
||||||
"itoa 1.0.2",
|
"itoa 1.0.2",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"socket2 0.4.4",
|
"socket2 0.4.4",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
"want",
|
"want",
|
||||||
@@ -1922,10 +1985,10 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"hyper 0.14.19",
|
"hyper 0.14.19",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1946,6 +2009,16 @@ dependencies = [
|
|||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-bidi",
|
||||||
|
"unicode-normalization",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "if_chain"
|
name = "if_chain"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -2183,7 +2256,7 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"hostname",
|
"hostname",
|
||||||
"httpdate 1.0.2",
|
"httpdate 1.0.2",
|
||||||
"idna",
|
"idna 0.2.3",
|
||||||
"mime",
|
"mime",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"nom",
|
"nom",
|
||||||
@@ -2194,9 +2267,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.126"
|
version = "0.2.153"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libgit2-sys"
|
name = "libgit2-sys"
|
||||||
@@ -2429,14 +2502,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.3"
|
version = "0.8.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
|
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2466,7 +2538,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"metrics",
|
"metrics",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
@@ -2518,7 +2590,7 @@ dependencies = [
|
|||||||
"strsim",
|
"strsim",
|
||||||
"take_mut",
|
"take_mut",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tokio-util 0.7.2",
|
"tokio-util 0.7.2",
|
||||||
"trust-dns-proto",
|
"trust-dns-proto",
|
||||||
@@ -2534,7 +2606,7 @@ version = "2.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f8f35e687561d5c1667590911e6698a8cb714a134a7505718a182e7bc9d3836"
|
checksum = "5f8f35e687561d5c1667590911e6698a8cb714a134a7505718a182e7bc9d3836"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
@@ -2543,7 +2615,7 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
"mime",
|
"mime",
|
||||||
"spin 0.9.3",
|
"spin 0.9.3",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tokio-util 0.6.10",
|
"tokio-util 0.6.10",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
@@ -2846,7 +2918,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-sys",
|
"windows-sys 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2924,9 +2996,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pest"
|
name = "pest"
|
||||||
@@ -2999,9 +3071,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.9"
|
version = "0.2.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
@@ -3114,9 +3186,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.56"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -3334,15 +3406,15 @@ source = "git+https://github.com/revoltchat/redis-rs?rev=1a41faf356fd21aebba71ce
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"combine",
|
"combine",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"itoa 1.0.2",
|
"itoa 1.0.2",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"ryu",
|
"ryu",
|
||||||
"sha1_smol",
|
"sha1_smol",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tokio-util 0.7.2",
|
"tokio-util 0.7.2",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
@@ -3354,14 +3426,14 @@ source = "git+https://github.com/revoltchat/redis-rs?rev=f8ca28ab85da59d2ccde526
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"combine",
|
"combine",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"itoa 1.0.2",
|
"itoa 1.0.2",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"ryu",
|
"ryu",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tokio-util 0.7.2",
|
"tokio-util 0.7.2",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
@@ -3382,6 +3454,20 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redis-protocol"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c31deddf734dc0a39d3112e73490e88b61a05e83e074d211f348404cee4d2c6"
|
||||||
|
dependencies = [
|
||||||
|
"bytes 1.5.0",
|
||||||
|
"bytes-utils",
|
||||||
|
"cookie-factory",
|
||||||
|
"crc16",
|
||||||
|
"log",
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
@@ -3453,7 +3539,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
|
checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -3469,11 +3555,11 @@ dependencies = [
|
|||||||
"mime",
|
"mime",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -3494,10 +3580,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
|
"bincode",
|
||||||
|
"fred",
|
||||||
"futures",
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -3512,7 +3600,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-config"
|
name = "revolt-config"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"cached",
|
"cached",
|
||||||
@@ -3524,7 +3612,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
@@ -3569,7 +3657,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -3615,7 +3703,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"iso8601-timestamp 0.2.11",
|
"iso8601-timestamp 0.2.11",
|
||||||
@@ -3631,7 +3719,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3646,7 +3734,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"log",
|
"log",
|
||||||
@@ -3657,7 +3745,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
@@ -3710,7 +3798,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"revolt_okapi",
|
"revolt_okapi",
|
||||||
"revolt_rocket_okapi",
|
"revolt_rocket_okapi",
|
||||||
@@ -3830,7 +3918,7 @@ dependencies = [
|
|||||||
"atomic",
|
"atomic",
|
||||||
"atty",
|
"atty",
|
||||||
"binascii",
|
"binascii",
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"either",
|
"either",
|
||||||
"figment",
|
"figment",
|
||||||
"futures",
|
"futures",
|
||||||
@@ -3840,7 +3928,7 @@ dependencies = [
|
|||||||
"multer",
|
"multer",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"ref-cast",
|
"ref-cast",
|
||||||
"rocket_codegen",
|
"rocket_codegen",
|
||||||
@@ -3850,7 +3938,7 @@ dependencies = [
|
|||||||
"state",
|
"state",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"time 0.3.17",
|
"time 0.3.17",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-util 0.7.2",
|
"tokio-util 0.7.2",
|
||||||
"ubyte",
|
"ubyte",
|
||||||
@@ -3933,14 +4021,14 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
"pear",
|
"pear",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"ref-cast",
|
"ref-cast",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"stable-pattern",
|
"stable-pattern",
|
||||||
"state",
|
"state",
|
||||||
"time 0.3.17",
|
"time 0.3.17",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"uncased",
|
"uncased",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4088,7 +4176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
|
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"windows-sys",
|
"windows-sys 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4222,7 +4310,7 @@ dependencies = [
|
|||||||
"sentry-debug-images",
|
"sentry-debug-images",
|
||||||
"sentry-panic",
|
"sentry-panic",
|
||||||
"sentry-tracing",
|
"sentry-tracing",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"ureq",
|
"ureq",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4523,6 +4611,16 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -4783,33 +4881,32 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.18.2"
|
version = "1.35.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395"
|
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"backtrace",
|
||||||
|
"bytes 1.5.0",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"mio 0.8.10",
|
||||||
"mio 0.8.3",
|
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"once_cell",
|
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2 0.4.4",
|
"socket2 0.5.5",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"winapi 0.3.9",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "1.7.0"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
|
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.26",
|
"quote 1.0.26",
|
||||||
"syn 1.0.107",
|
"syn 2.0.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4819,7 +4916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4829,7 +4926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls",
|
"rustls",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"webpki",
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4840,8 +4937,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
|
checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4874,12 +4971,12 @@ version = "0.6.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507"
|
checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"log",
|
"log",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4888,12 +4985,12 @@ version = "0.7.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c"
|
checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4932,7 +5029,7 @@ checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"log",
|
"log",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.13",
|
||||||
"tracing-attributes",
|
"tracing-attributes",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
@@ -5010,7 +5107,7 @@ dependencies = [
|
|||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"idna",
|
"idna 0.2.3",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
@@ -5018,7 +5115,7 @@ dependencies = [
|
|||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5038,7 +5135,7 @@ dependencies = [
|
|||||||
"resolv-conf",
|
"resolv-conf",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio 1.18.2",
|
"tokio 1.35.1",
|
||||||
"trust-dns-proto",
|
"trust-dns-proto",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5056,7 +5153,7 @@ checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes 1.1.0",
|
"bytes 1.5.0",
|
||||||
"http",
|
"http",
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
@@ -5169,9 +5266,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.8"
|
version = "0.3.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
@@ -5181,9 +5278,9 @@ checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
version = "0.1.19"
|
version = "0.1.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
|
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
@@ -5243,13 +5340,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.2.2"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
|
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna 0.5.0",
|
||||||
"matches",
|
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -5263,6 +5359,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlencoding"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf-8"
|
name = "utf-8"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
@@ -5295,7 +5397,7 @@ version = "0.15.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f07b0a1390e01c0fc35ebb26b28ced33c9a3808f7f9fbe94d3cc01e233bfeed5"
|
checksum = "f07b0a1390e01c0fc35ebb26b28ced33c9a3808f7f9fbe94d3cc01e233bfeed5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"idna",
|
"idna 0.2.3",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -5310,7 +5412,7 @@ version = "0.16.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32ad5bf234c7d3ad1042e5252b7eddb2c4669ee23f32c7dd0e9b7705f07ef591"
|
checksum = "32ad5bf234c7d3ad1042e5252b7eddb2c4669ee23f32c7dd0e9b7705f07ef591"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"idna",
|
"idna 0.2.3",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -5626,43 +5728,109 @@ version = "0.36.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc 0.36.1",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu 0.36.1",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc 0.36.1",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu 0.36.1",
|
||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc 0.48.5",
|
||||||
|
"windows_i686_gnu 0.48.5",
|
||||||
|
"windows_i686_msvc 0.48.5",
|
||||||
|
"windows_x86_64_gnu 0.48.5",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ querystring = "1.1.0"
|
|||||||
revolt-quark = { path = "../quark" }
|
revolt-quark = { path = "../quark" }
|
||||||
|
|
||||||
# serde
|
# serde
|
||||||
|
bincode = "1.3.3"
|
||||||
serde_json = "1.0.79"
|
serde_json = "1.0.79"
|
||||||
rmp-serde = "1.0.0"
|
rmp-serde = "1.0.0"
|
||||||
serde = "1.0.136"
|
serde = "1.0.136"
|
||||||
@@ -34,3 +35,6 @@ async-std = { version = "1.8.0", features = [
|
|||||||
# core
|
# core
|
||||||
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
|
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
|
||||||
sentry = "0.31.5"
|
sentry = "0.31.5"
|
||||||
|
|
||||||
|
# redis
|
||||||
|
fred = { version = "8.0.1", features = ["subscriber-client"] }
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ async fn main() {
|
|||||||
|
|
||||||
// Start accepting new connections and spawn a client for each connection.
|
// Start accepting new connections and spawn a client for each connection.
|
||||||
while let Ok((stream, addr)) = listener.accept().await {
|
while let Ok((stream, addr)) = listener.accept().await {
|
||||||
websocket::spawn_client(database::get_db(), stream, addr);
|
async_std::task::spawn(async move {
|
||||||
|
info!("User connected from {addr:?}");
|
||||||
|
websocket::client(database::get_db(), stream, addr).await;
|
||||||
|
info!("User disconnected from {addr:?}");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+139
-129
@@ -1,6 +1,16 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use futures::{channel::oneshot, pin_mut, select, FutureExt, SinkExt, StreamExt, TryStreamExt};
|
use async_tungstenite::WebSocketStream;
|
||||||
|
use fred::{
|
||||||
|
interfaces::{ClientLike, EventInterface, PubsubInterface},
|
||||||
|
types::RedisConfig,
|
||||||
|
};
|
||||||
|
use futures::{
|
||||||
|
channel::oneshot,
|
||||||
|
pin_mut, select,
|
||||||
|
stream::{SplitSink, SplitStream},
|
||||||
|
FutureExt, SinkExt, StreamExt, TryStreamExt,
|
||||||
|
};
|
||||||
use revolt_presence::{create_session, delete_session};
|
use revolt_presence::{create_session, delete_session};
|
||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
events::{
|
events::{
|
||||||
@@ -9,33 +19,37 @@ use revolt_quark::{
|
|||||||
state::{State, SubscriptionStateChange},
|
state::{State, SubscriptionStateChange},
|
||||||
},
|
},
|
||||||
models::{user::UserHint, User},
|
models::{user::UserHint, User},
|
||||||
redis_kiss, Database,
|
redis_kiss::{PayloadType, REDIS_PAYLOAD_TYPE, REDIS_URI},
|
||||||
|
Database,
|
||||||
};
|
};
|
||||||
|
|
||||||
use async_std::{net::TcpStream, sync::Mutex, task};
|
use async_std::{net::TcpStream, sync::Mutex};
|
||||||
|
|
||||||
use crate::config::WebsocketHandshakeCallback;
|
use crate::config::{ProtocolConfiguration, WebsocketHandshakeCallback};
|
||||||
|
|
||||||
/// Spawn a new WebSocket client worker given access to the database,
|
type WsReader = SplitStream<WebSocketStream<TcpStream>>;
|
||||||
|
type WsWriter = SplitSink<WebSocketStream<TcpStream>, async_tungstenite::tungstenite::Message>;
|
||||||
|
|
||||||
|
/// Start a new WebSocket client worker given access to the database,
|
||||||
/// the relevant TCP stream and the remote address of the client.
|
/// the relevant TCP stream and the remote address of the client.
|
||||||
pub fn spawn_client(db: &'static Database, stream: TcpStream, addr: SocketAddr) {
|
pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr) {
|
||||||
// Spawn a new Async task to work on.
|
|
||||||
task::spawn(async move {
|
|
||||||
info!("User connected from {addr:?}");
|
|
||||||
|
|
||||||
// Upgrade the TCP connection to a WebSocket connection.
|
// Upgrade the TCP connection to a WebSocket connection.
|
||||||
// In this process, we also parse any additional parameters given.
|
// In this process, we also parse any additional parameters given.
|
||||||
// e.g. wss://example.com?format=json&version=1
|
// e.g. wss://example.com?format=json&version=1
|
||||||
let (sender, receiver) = oneshot::channel();
|
let (sender, receiver) = oneshot::channel();
|
||||||
if let Ok(ws) = async_tungstenite::accept_hdr_async_with_config(
|
let Ok(ws) = async_tungstenite::accept_hdr_async_with_config(
|
||||||
stream,
|
stream,
|
||||||
WebsocketHandshakeCallback::from(sender),
|
WebsocketHandshakeCallback::from(sender),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
// Verify we've received a valid config, otherwise we should just drop the connection.
|
// Verify we've received a valid config, otherwise we should just drop the connection.
|
||||||
if let Ok(mut config) = receiver.await {
|
let Ok(mut config) = receiver.await else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
info!(
|
info!(
|
||||||
"User {addr:?} provided protocol configuration (version = {}, format = {:?})",
|
"User {addr:?} provided protocol configuration (version = {}, format = {:?})",
|
||||||
config.get_protocol_version(),
|
config.get_protocol_version(),
|
||||||
@@ -43,66 +57,110 @@ pub fn spawn_client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Split the socket for simultaneously read and write.
|
// Split the socket for simultaneously read and write.
|
||||||
let (write, mut read) = ws.split();
|
let (mut write, mut read) = ws.split();
|
||||||
let write = Mutex::new(write);
|
|
||||||
|
|
||||||
// If the user has not provided authentication, request information.
|
// If the user has not provided authentication, request information.
|
||||||
if config.get_session_token().is_none() {
|
if config.get_session_token().is_none() {
|
||||||
'outer: while let Ok(message) = read.try_next().await {
|
while let Ok(message) = read.try_next().await {
|
||||||
if let Ok(ClientMessage::Authenticate { token }) =
|
if let Ok(ClientMessage::Authenticate { token }) =
|
||||||
config.decode(message.as_ref().unwrap())
|
config.decode(message.as_ref().unwrap())
|
||||||
{
|
{
|
||||||
config.set_session_token(token);
|
config.set_session_token(token);
|
||||||
break 'outer;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to authenticate the user.
|
// Try to authenticate the user.
|
||||||
if let Some(token) = config.get_session_token().as_ref() {
|
let Some(token) = config.get_session_token().as_ref() else {
|
||||||
match User::from_token(db, token, UserHint::Any).await {
|
return;
|
||||||
Ok(user) => {
|
};
|
||||||
|
let user = match User::from_token(db, token, UserHint::Any).await {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(err) => {
|
||||||
|
write.send(config.encode(&err)).await.ok();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
info!("User {addr:?} authenticated as @{}", user.username);
|
info!("User {addr:?} authenticated as @{}", user.username);
|
||||||
|
|
||||||
// Create local state.
|
// Create local state.
|
||||||
let mut state = State::from(user);
|
let mut state = State::from(user);
|
||||||
let user_id = state.cache.user_id.clone();
|
let user_id = state.cache.user_id.clone();
|
||||||
|
|
||||||
// Create presence session.
|
|
||||||
let (first_session, session_id) = create_session(&user_id, 0).await;
|
|
||||||
|
|
||||||
// Notify socket we have authenticated.
|
// Notify socket we have authenticated.
|
||||||
write
|
if write
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.send(config.encode(&EventV1::Authenticated))
|
.send(config.encode(&EventV1::Authenticated))
|
||||||
.await
|
.await
|
||||||
.ok();
|
.is_err()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Download required data to local cache and send Ready payload.
|
// Download required data to local cache and send Ready payload.
|
||||||
if let Ok(ready_payload) = state.generate_ready_payload(db).await {
|
let Ok(ready_payload) = state.generate_ready_payload(db).await else {
|
||||||
write
|
return;
|
||||||
.lock()
|
};
|
||||||
.await
|
if write.send(config.encode(&ready_payload)).await.is_err() {
|
||||||
.send(config.encode(&ready_payload))
|
return;
|
||||||
.await
|
}
|
||||||
.ok();
|
|
||||||
|
// Create presence session.
|
||||||
|
let (first_session, session_id) = create_session(&user_id, 0).await;
|
||||||
|
|
||||||
// If this was the first session, notify other users that we just went online.
|
// If this was the first session, notify other users that we just went online.
|
||||||
if first_session {
|
if first_session {
|
||||||
state.broadcast_presence_change(true).await;
|
state.broadcast_presence_change(true).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a PubSub connection to poll on.
|
|
||||||
let listener = async {
|
|
||||||
if let Ok(mut conn) = redis_kiss::open_pubsub_connection().await
|
|
||||||
{
|
{
|
||||||
|
let write = Mutex::new(write);
|
||||||
|
// Create a PubSub connection to poll on.
|
||||||
|
let listener = listener(db, &mut state, addr, &config, &write).fuse();
|
||||||
|
// Read from WebSocket stream.
|
||||||
|
let worker = worker(user_id.clone(), &config, read, &write).fuse();
|
||||||
|
|
||||||
|
// Pin both tasks.
|
||||||
|
pin_mut!(listener, worker);
|
||||||
|
|
||||||
|
// Wait for either disconnect or for listener to die.
|
||||||
|
select!(
|
||||||
|
() = listener => {},
|
||||||
|
() = worker => {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Clean up presence session.
|
||||||
|
let last_session = delete_session(&user_id, session_id).await;
|
||||||
|
|
||||||
|
// If this was the last session, notify other users that we just went offline.
|
||||||
|
if last_session {
|
||||||
|
state.broadcast_presence_change(false).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn listener(
|
||||||
|
db: &'static Database,
|
||||||
|
state: &mut State,
|
||||||
|
addr: SocketAddr,
|
||||||
|
config: &ProtocolConfiguration,
|
||||||
|
write: &Mutex<WsWriter>,
|
||||||
|
) {
|
||||||
|
let redis_config = RedisConfig::from_url(&REDIS_URI).unwrap();
|
||||||
|
let Ok(subscriber) = fred::types::Builder::from_config(redis_config).build_subscriber_client()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if subscriber.init().await.is_err() {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut message_rx = subscriber.message_rx();
|
||||||
loop {
|
loop {
|
||||||
// Check for state changes for subscriptions.
|
// Check for state changes for subscriptions.
|
||||||
match state.apply_state() {
|
match state.apply_state() {
|
||||||
SubscriptionStateChange::Reset => {
|
SubscriptionStateChange::Reset => {
|
||||||
|
subscriber.unsubscribe_all().await.unwrap();
|
||||||
for id in state.iter_subscriptions() {
|
for id in state.iter_subscriptions() {
|
||||||
conn.subscribe(id).await.unwrap();
|
subscriber.subscribe(id).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
@@ -113,72 +171,69 @@ pub fn spawn_client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
info!("{addr:?} unsubscribing from {id}");
|
info!("{addr:?} unsubscribing from {id}");
|
||||||
|
|
||||||
conn.unsubscribe(id).await.unwrap();
|
subscriber.unsubscribe(id).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
for id in add {
|
for id in add {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
info!("{addr:?} subscribing to {id}");
|
info!("{addr:?} subscribing to {id}");
|
||||||
|
|
||||||
conn.subscribe(id).await.unwrap();
|
subscriber.subscribe(id).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SubscriptionStateChange::None => {}
|
SubscriptionStateChange::None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// * Debug logging of current subscriptions.
|
|
||||||
/*#[cfg(debug_assertions)]
|
|
||||||
info!(
|
|
||||||
"User {addr:?} is subscribed to {:?}",
|
|
||||||
state
|
|
||||||
.iter_subscriptions()
|
|
||||||
.collect::<Vec<&String>>()
|
|
||||||
);*/
|
|
||||||
|
|
||||||
// Handle incoming events.
|
// Handle incoming events.
|
||||||
match conn.on_message().next().await.map(|res| {
|
let Ok(message) = message_rx.recv().await.map_err(|e| {
|
||||||
res.map(|item|(
|
info!("Error while consuming pub/sub messages: {e:?}");
|
||||||
item.get_channel_name().to_string(),
|
sentry::capture_error(&e);
|
||||||
redis_kiss::decode_payload::<EventV1>(&item),
|
}) else {
|
||||||
))
|
return;
|
||||||
}) {
|
};
|
||||||
Some(Ok((channel, item))) => {
|
let event = match *REDIS_PAYLOAD_TYPE {
|
||||||
if let Ok(mut event) = item {
|
PayloadType::Json => message
|
||||||
if state
|
.value
|
||||||
.handle_incoming_event_v1(
|
.as_str()
|
||||||
db, &mut event,
|
.and_then(|s| serde_json::from_str::<EventV1>(s.as_ref()).ok()),
|
||||||
)
|
PayloadType::Msgpack => message
|
||||||
|
.value
|
||||||
|
.as_bytes()
|
||||||
|
.and_then(|b| rmp_serde::from_slice::<EventV1>(b).ok()),
|
||||||
|
PayloadType::Bincode => message
|
||||||
|
.value
|
||||||
|
.as_bytes()
|
||||||
|
.and_then(|b| bincode::deserialize::<EventV1>(b).ok()),
|
||||||
|
};
|
||||||
|
let Some(mut event) = event else {
|
||||||
|
warn!("Failed to deserialise an event for {}!", message.channel);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let should_send = state.handle_incoming_event_v1(db, &mut event).await;
|
||||||
|
|
||||||
|
if should_send
|
||||||
|
&& write
|
||||||
|
.lock()
|
||||||
.await
|
.await
|
||||||
&& write.lock().await
|
|
||||||
.send(config.encode(&event))
|
.send(config.encode(&event))
|
||||||
.await
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
break;
|
return;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn!("Failed to deserialise an event for {channel}!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
}
|
||||||
info!("Error while consuming pub/sub messages: {e:?}");
|
|
||||||
sentry::capture_error(&e);
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// No more data, assume we disconnected or otherwise
|
|
||||||
// something bad occurred, so disconnect user.
|
|
||||||
None => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.fuse();
|
|
||||||
|
|
||||||
// Read from WebSocket stream.
|
async fn worker(
|
||||||
let worker =
|
user_id: String,
|
||||||
async {
|
config: &ProtocolConfiguration,
|
||||||
|
mut read: WsReader,
|
||||||
|
write: &Mutex<WsWriter>,
|
||||||
|
) {
|
||||||
while let Ok(Some(msg)) = read.try_next().await {
|
while let Ok(Some(msg)) = read.try_next().await {
|
||||||
if let Ok(payload) = config.decode(&msg) {
|
let Ok(payload) = config.decode(&msg) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
match payload {
|
match payload {
|
||||||
ClientMessage::BeginTyping { channel } => {
|
ClientMessage::BeginTyping { channel } => {
|
||||||
EventV1::ChannelStartTyping {
|
EventV1::ChannelStartTyping {
|
||||||
@@ -201,9 +256,7 @@ pub fn spawn_client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
|
|||||||
write
|
write
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.send(config.encode(
|
.send(config.encode(&EventV1::Pong { data }))
|
||||||
&EventV1::Pong { data },
|
|
||||||
))
|
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
@@ -211,47 +264,4 @@ pub fn spawn_client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
.fuse();
|
|
||||||
|
|
||||||
// Pin both tasks.
|
|
||||||
pin_mut!(listener, worker);
|
|
||||||
|
|
||||||
// Wait for either disconnect or for listener to die.
|
|
||||||
select!(
|
|
||||||
() = listener => {},
|
|
||||||
() = worker => {}
|
|
||||||
);
|
|
||||||
|
|
||||||
// * Combine the streams back once we are ready to disconnect.
|
|
||||||
/* ws = read.reunite(write).unwrap(); */
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up presence session.
|
|
||||||
let last_session = delete_session(&user_id, session_id).await;
|
|
||||||
|
|
||||||
// If this was the last session, notify other users that we just went offline.
|
|
||||||
if last_session {
|
|
||||||
state.broadcast_presence_change(false).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
write.lock().await.send(config.encode(&err)).await.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// * Disconnect the WebSocket if it isn't already.
|
|
||||||
/*ws.close(Some(CloseFrame {
|
|
||||||
code: CloseCode::Normal,
|
|
||||||
reason: std::borrow::Cow::from(""),
|
|
||||||
}))
|
|
||||||
.await
|
|
||||||
.unwrap();*/
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("User disconnected from {addr:?}");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-config"
|
name = "revolt-config"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -23,11 +23,11 @@ default = ["mongodb", "async-std-runtime", "tasks"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-config = { version = "0.6.8", path = "../config" }
|
revolt-config = { version = "0.6.13", path = "../config" }
|
||||||
revolt-result = { version = "0.6.8", path = "../result" }
|
revolt-result = { version = "0.6.13", path = "../result" }
|
||||||
revolt-models = { version = "0.6.8", path = "../models" }
|
revolt-models = { version = "0.6.13", path = "../models" }
|
||||||
revolt-presence = { version = "0.6.8", path = "../presence" }
|
revolt-presence = { version = "0.6.13", path = "../presence" }
|
||||||
revolt-permissions = { version = "0.6.8", path = "../permissions", features = [
|
revolt-permissions = { version = "0.6.13", path = "../permissions", features = [
|
||||||
"serde",
|
"serde",
|
||||||
"bson",
|
"bson",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"_object_type": "User",
|
||||||
|
"_id": "__ID:0__",
|
||||||
|
"username": "Owner",
|
||||||
|
"discriminator": "0001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "User",
|
||||||
|
"_id": "__ID:1__",
|
||||||
|
"username": "Member",
|
||||||
|
"discriminator": "0001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "User",
|
||||||
|
"_id": "__ID:2__",
|
||||||
|
"username": "Member",
|
||||||
|
"discriminator": "0002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "Channel",
|
||||||
|
"_id": "__ID:3__",
|
||||||
|
"channel_type": "Group",
|
||||||
|
"name": "My Group",
|
||||||
|
"owner": "__ID:0__",
|
||||||
|
"recipients": ["__ID:0__", "__ID:1__"]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"_object_type": "User",
|
||||||
|
"_id": "__ID:0__",
|
||||||
|
"username": "Owner",
|
||||||
|
"discriminator": "0001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "User",
|
||||||
|
"_id": "__ID:1__",
|
||||||
|
"username": "Moderator",
|
||||||
|
"discriminator": "0001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "User",
|
||||||
|
"_id": "__ID:2__",
|
||||||
|
"username": "User",
|
||||||
|
"discriminator": "0001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "Channel",
|
||||||
|
"_id": "__ID:3__",
|
||||||
|
"channel_type": "TextChannel",
|
||||||
|
"name": "General",
|
||||||
|
"server": "__ID:4__",
|
||||||
|
"default_permissions": {
|
||||||
|
"a": 0,
|
||||||
|
"d": 1048576
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"__ID:5__": {
|
||||||
|
"a": 1048576,
|
||||||
|
"d": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "Server",
|
||||||
|
"_id": "__ID:4__",
|
||||||
|
"owner": "__ID:0__",
|
||||||
|
"name": "Server",
|
||||||
|
"channels": ["__ID:3__"],
|
||||||
|
"roles": {
|
||||||
|
"__ID:5__": {
|
||||||
|
"name": "Moderator",
|
||||||
|
"permissions": {
|
||||||
|
"a": 545270208,
|
||||||
|
"d": 0
|
||||||
|
},
|
||||||
|
"rank": 3
|
||||||
|
},
|
||||||
|
"__ID:6__": {
|
||||||
|
"name": "Owner",
|
||||||
|
"permissions": {
|
||||||
|
"a": 0,
|
||||||
|
"d": 0
|
||||||
|
},
|
||||||
|
"rank": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default_permissions": 4000322560
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "ServerMember",
|
||||||
|
"_id": {
|
||||||
|
"user": "__ID:0__",
|
||||||
|
"server": "__ID:4__"
|
||||||
|
},
|
||||||
|
"roles": ["__ID:6__"],
|
||||||
|
"joined_at": 1698318340195
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "ServerMember",
|
||||||
|
"_id": {
|
||||||
|
"user": "__ID:1__",
|
||||||
|
"server": "__ID:4__"
|
||||||
|
},
|
||||||
|
"roles": ["__ID:5__"],
|
||||||
|
"joined_at": 1698318340195
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "ServerMember",
|
||||||
|
"_id": {
|
||||||
|
"user": "__ID:2__",
|
||||||
|
"server": "__ID:4__"
|
||||||
|
},
|
||||||
|
"joined_at": 1698318340195
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -110,6 +110,7 @@ pub enum EventV1 {
|
|||||||
id: String,
|
id: String,
|
||||||
server: Server,
|
server: Server,
|
||||||
channels: Vec<Channel>,
|
channels: Vec<Channel>,
|
||||||
|
emojis: Vec<Emoji>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Update existing server
|
/// Update existing server
|
||||||
@@ -243,8 +244,7 @@ impl EventV1 {
|
|||||||
pub async fn p_user(self, id: String, db: &Database) {
|
pub async fn p_user(self, id: String, db: &Database) {
|
||||||
self.clone().p(id.clone()).await;
|
self.clone().p(id.clone()).await;
|
||||||
|
|
||||||
// ! FIXME: this should be captured by member list in the future
|
// TODO: this should be captured by member list in the future and not immediately fanned out to users
|
||||||
// ! and not immediately fanned out to users
|
|
||||||
if let Ok(members) = db.fetch_all_memberships(&id).await {
|
if let Ok(members) = db.fetch_all_memberships(&id).await {
|
||||||
for member in members {
|
for member in members {
|
||||||
self.clone().p(member.id.server).await;
|
self.clone().p(member.id.server).await;
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ use revolt_permissions::OverrideField;
|
|||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{events::client::EventV1, Database, File, IntoDocumentPath, SystemMessage, User};
|
use crate::{
|
||||||
|
events::client::EventV1, Database, File, IntoDocumentPath, PartialServer, Server,
|
||||||
|
SystemMessage, User,
|
||||||
|
};
|
||||||
|
|
||||||
auto_derived!(
|
auto_derived!(
|
||||||
#[serde(tag = "channel_type")]
|
#[serde(tag = "channel_type")]
|
||||||
@@ -189,6 +192,67 @@ impl Channel {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
/// Create a new server channel
|
||||||
|
pub async fn create_server_channel(
|
||||||
|
db: &Database,
|
||||||
|
server: &mut Server,
|
||||||
|
data: v0::DataCreateServerChannel,
|
||||||
|
update_server: bool,
|
||||||
|
) -> Result<Channel> {
|
||||||
|
let config = config().await;
|
||||||
|
if server.channels.len() > config.features.limits.default.server_channels {
|
||||||
|
return Err(create_error!(TooManyChannels {
|
||||||
|
max: config.features.limits.default.server_channels,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = ulid::Ulid::new().to_string();
|
||||||
|
let channel = match data.channel_type {
|
||||||
|
v0::LegacyServerChannelType::Text => Channel::TextChannel {
|
||||||
|
id: id.clone(),
|
||||||
|
server: server.id.to_owned(),
|
||||||
|
name: data.name,
|
||||||
|
description: data.description,
|
||||||
|
icon: None,
|
||||||
|
last_message_id: None,
|
||||||
|
default_permissions: None,
|
||||||
|
role_permissions: HashMap::new(),
|
||||||
|
nsfw: data.nsfw.unwrap_or(false),
|
||||||
|
},
|
||||||
|
v0::LegacyServerChannelType::Voice => Channel::VoiceChannel {
|
||||||
|
id: id.clone(),
|
||||||
|
server: server.id.to_owned(),
|
||||||
|
name: data.name,
|
||||||
|
description: data.description,
|
||||||
|
icon: None,
|
||||||
|
default_permissions: None,
|
||||||
|
role_permissions: HashMap::new(),
|
||||||
|
nsfw: data.nsfw.unwrap_or(false),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
db.insert_channel(&channel).await?;
|
||||||
|
|
||||||
|
if update_server {
|
||||||
|
server
|
||||||
|
.update(
|
||||||
|
db,
|
||||||
|
PartialServer {
|
||||||
|
channels: Some([server.channels.clone(), [id].into()].concat()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
EventV1::ChannelCreate(channel.clone().into())
|
||||||
|
.p(server.id.clone())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(channel)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a group
|
/// Create a group
|
||||||
pub async fn create_group(
|
pub async fn create_group(
|
||||||
db: &Database,
|
db: &Database,
|
||||||
@@ -627,130 +691,44 @@ impl IntoDocumentPath for FieldsChannel {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::HashMap;
|
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
||||||
|
|
||||||
use revolt_permissions::{calculate_channel_permissions, ChannelPermission, OverrideField};
|
use crate::{fixture, util::permissions::DatabasePermissionQuery};
|
||||||
|
|
||||||
use crate::{
|
#[async_std::test]
|
||||||
util::permissions::DatabasePermissionQuery, Channel, Member, MemberCompositeKey, Role,
|
async fn permissions_group_channel() {
|
||||||
Server, User,
|
database_test!(|db| async move {
|
||||||
};
|
fixture!(db, "group_with_members",
|
||||||
|
owner user 0
|
||||||
|
member1 user 1
|
||||||
|
member2 user 2
|
||||||
|
channel channel 3);
|
||||||
|
|
||||||
|
let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
|
||||||
|
assert!(calculate_channel_permissions(&mut query)
|
||||||
|
.await
|
||||||
|
.has_channel_permission(ChannelPermission::SendMessage));
|
||||||
|
|
||||||
|
let mut query = DatabasePermissionQuery::new(&db, &member1).channel(&channel);
|
||||||
|
assert!(calculate_channel_permissions(&mut query)
|
||||||
|
.await
|
||||||
|
.has_channel_permission(ChannelPermission::SendMessage));
|
||||||
|
|
||||||
|
let mut query = DatabasePermissionQuery::new(&db, &member2).channel(&channel);
|
||||||
|
assert!(!calculate_channel_permissions(&mut query)
|
||||||
|
.await
|
||||||
|
.has_channel_permission(ChannelPermission::SendMessage));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn permissions_text_channel() {
|
async fn permissions_text_channel() {
|
||||||
database_test!(|db| async move {
|
database_test!(|db| async move {
|
||||||
let owner = User::create(&db, "Owner".to_string(), None, None)
|
fixture!(db, "server_with_roles",
|
||||||
.await
|
owner user 0
|
||||||
.unwrap();
|
moderator user 1
|
||||||
|
user user 2
|
||||||
let moderator = User::create(&db, "Moderator".to_string(), None, None)
|
channel channel 3);
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let user = User::create(&db, "User".to_string(), None, None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let server_id = ulid::Ulid::new().to_string();
|
|
||||||
|
|
||||||
let channel = Channel::TextChannel {
|
|
||||||
id: ulid::Ulid::new().to_string(),
|
|
||||||
server: server_id.clone(),
|
|
||||||
name: "Channel".to_string(),
|
|
||||||
description: None,
|
|
||||||
icon: None,
|
|
||||||
last_message_id: None,
|
|
||||||
default_permissions: Some(OverrideField {
|
|
||||||
d: 1048576, // TODO: bitfield
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
role_permissions: HashMap::from([(
|
|
||||||
"01F9HFTSBWTNA2F4TMSV7VM3FG".to_string(),
|
|
||||||
OverrideField {
|
|
||||||
a: 1048576, // TODO: bitfield
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)]),
|
|
||||||
nsfw: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let server = Server {
|
|
||||||
id: server_id,
|
|
||||||
owner: owner.id.clone(),
|
|
||||||
name: "My Server".to_string(),
|
|
||||||
description: None,
|
|
||||||
channels: vec![channel.id()],
|
|
||||||
categories: None,
|
|
||||||
system_messages: None,
|
|
||||||
roles: HashMap::from([
|
|
||||||
(
|
|
||||||
"01F9HFTSBWTNA2F4TMSV7VM3FG".to_string(),
|
|
||||||
Role {
|
|
||||||
name: "Moderator".to_string(),
|
|
||||||
permissions: OverrideField {
|
|
||||||
a: 545270208, // TODO: explicit
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
colour: None,
|
|
||||||
hoist: true,
|
|
||||||
rank: 3,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"01FBF9DNHSRPVTWFMNB3JNB8FK".to_string(),
|
|
||||||
Role {
|
|
||||||
name: "Owner".to_string(),
|
|
||||||
permissions: Default::default(),
|
|
||||||
colour: None,
|
|
||||||
hoist: true,
|
|
||||||
rank: 0,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
default_permissions: 4000322560, // TODO: use bitfield
|
|
||||||
icon: None,
|
|
||||||
banner: None,
|
|
||||||
flags: None,
|
|
||||||
nsfw: false,
|
|
||||||
analytics: false,
|
|
||||||
discoverable: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: proper creation
|
|
||||||
db.insert_channel(&channel).await.unwrap();
|
|
||||||
server.create(&db).await.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: owner.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
roles: vec!["01FBF9DNHSRPVTWFMNB3JNB8FK".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: moderator.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
roles: vec!["01F9HFTSBWTNA2F4TMSV7VM3FG".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: user.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
|
let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
|
||||||
assert!(calculate_channel_permissions(&mut query)
|
assert!(calculate_channel_permissions(&mut query)
|
||||||
|
|||||||
@@ -24,13 +24,12 @@ impl AbstractEmojis for MongoDb {
|
|||||||
async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result<Vec<Emoji>> {
|
async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result<Vec<Emoji>> {
|
||||||
query!(
|
query!(
|
||||||
self,
|
self,
|
||||||
find_one,
|
find,
|
||||||
COL,
|
COL,
|
||||||
doc! {
|
doc! {
|
||||||
"parent.id": parent_id
|
"parent.id": parent_id
|
||||||
}
|
}
|
||||||
)?
|
)
|
||||||
.ok_or_else(|| create_error!(NotFound))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch emoji by their parent ids
|
/// Fetch emoji by their parent ids
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ impl Message {
|
|||||||
author: MessageAuthor<'_>,
|
author: MessageAuthor<'_>,
|
||||||
mut idempotency: IdempotencyKey,
|
mut idempotency: IdempotencyKey,
|
||||||
generate_embeds: bool,
|
generate_embeds: bool,
|
||||||
|
allow_mentions: bool,
|
||||||
) -> Result<Message> {
|
) -> Result<Message> {
|
||||||
let config = config().await;
|
let config = config().await;
|
||||||
|
|
||||||
@@ -272,6 +273,7 @@ impl Message {
|
|||||||
|
|
||||||
// Parse mentions in message.
|
// Parse mentions in message.
|
||||||
let mut mentions = HashSet::new();
|
let mut mentions = HashSet::new();
|
||||||
|
if allow_mentions {
|
||||||
if let Some(content) = &data.content {
|
if let Some(content) = &data.content {
|
||||||
for capture in RE_MENTION.captures_iter(content) {
|
for capture in RE_MENTION.captures_iter(content) {
|
||||||
if let Some(mention) = capture.get(1) {
|
if let Some(mention) = capture.get(1) {
|
||||||
@@ -279,6 +281,7 @@ impl Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Verify replies are valid.
|
// Verify replies are valid.
|
||||||
let mut replies = HashSet::new();
|
let mut replies = HashSet::new();
|
||||||
@@ -292,7 +295,7 @@ impl Message {
|
|||||||
for ReplyIntent { id, mention } in entries {
|
for ReplyIntent { id, mention } in entries {
|
||||||
let message = db.fetch_message(&id).await?;
|
let message = db.fetch_message(&id).await?;
|
||||||
|
|
||||||
if mention {
|
if mention && allow_mentions {
|
||||||
mentions.insert(message.author.to_owned());
|
mentions.insert(message.author.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ impl AbstractMessages for ReferenceDb {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// TODO: sorting, etc (will be required for tests)
|
// FIXME: sorting, etc (will be required for tests)
|
||||||
|
|
||||||
Ok(matched_messages)
|
Ok(matched_messages)
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
|||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::client::EventV1, util::permissions::DatabasePermissionQuery, Database, File, Server,
|
events::client::EventV1, util::permissions::DatabasePermissionQuery, Channel, Database, File,
|
||||||
SystemMessage, User,
|
Server, SystemMessage, User,
|
||||||
};
|
};
|
||||||
|
|
||||||
auto_derived_partial!(
|
auto_derived_partial!(
|
||||||
@@ -80,9 +80,8 @@ impl Member {
|
|||||||
db: &Database,
|
db: &Database,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
user: &User,
|
user: &User,
|
||||||
// channels: Option<Vec<Channel>>,
|
channels: Option<Vec<Channel>>,
|
||||||
//) -> Result<Vec<Channel>> {
|
) -> Result<Vec<Channel>> {
|
||||||
) -> Result<()> {
|
|
||||||
if db.fetch_ban(&server.id, &user.id).await.is_ok() {
|
if db.fetch_ban(&server.id, &user.id).await.is_ok() {
|
||||||
return Err(create_error!(Banned));
|
return Err(create_error!(Banned));
|
||||||
}
|
}
|
||||||
@@ -101,9 +100,10 @@ impl Member {
|
|||||||
|
|
||||||
db.insert_member(&member).await?;
|
db.insert_member(&member).await?;
|
||||||
|
|
||||||
let mut channels = vec![];
|
let should_fetch = channels.is_none();
|
||||||
|
let mut channels = channels.unwrap_or_default();
|
||||||
|
|
||||||
if true {
|
if should_fetch {
|
||||||
let query = DatabasePermissionQuery::new(db, user).server(server);
|
let query = DatabasePermissionQuery::new(db, user).server(server);
|
||||||
let existing_channels = db.fetch_channels(&server.channels).await?;
|
let existing_channels = db.fetch_channels(&server.channels).await?;
|
||||||
|
|
||||||
@@ -119,6 +119,8 @@ impl Member {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
|
||||||
|
|
||||||
EventV1::ServerMemberJoin {
|
EventV1::ServerMemberJoin {
|
||||||
id: server.id.clone(),
|
id: server.id.clone(),
|
||||||
user: user.id.clone(),
|
user: user.id.clone(),
|
||||||
@@ -134,6 +136,7 @@ impl Member {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|channel| channel.into())
|
.map(|channel| channel.into())
|
||||||
.collect(),
|
.collect(),
|
||||||
|
emojis: emojis.into_iter().map(|emoji| emoji.into()).collect(),
|
||||||
}
|
}
|
||||||
.private(user.id.clone())
|
.private(user.id.clone())
|
||||||
.await;
|
.await;
|
||||||
@@ -152,8 +155,7 @@ impl Member {
|
|||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok(channels)
|
Ok(channels)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update member data
|
/// Update member data
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use revolt_permissions::OverrideField;
|
use revolt_models::v0::{self, DataCreateServerChannel};
|
||||||
|
use revolt_permissions::{OverrideField, DEFAULT_PERMISSION_SERVER};
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
use crate::{events::client::EventV1, Database, File};
|
use crate::{events::client::EventV1, Channel, Database, File, User};
|
||||||
|
|
||||||
auto_derived_partial!(
|
auto_derived_partial!(
|
||||||
/// Server
|
/// Server
|
||||||
@@ -22,7 +23,7 @@ auto_derived_partial!(
|
|||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
|
|
||||||
/// Channels within this server
|
/// Channels within this server
|
||||||
// ! FIXME: this may be redundant
|
// TODO: investigate if this is redundant and can be removed
|
||||||
pub channels: Vec<String>,
|
pub channels: Vec<String>,
|
||||||
/// Categories for this server
|
/// Categories for this server
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -131,8 +132,52 @@ auto_derived!(
|
|||||||
#[allow(clippy::disallowed_methods)]
|
#[allow(clippy::disallowed_methods)]
|
||||||
impl Server {
|
impl Server {
|
||||||
/// Create a server
|
/// Create a server
|
||||||
pub async fn create(&self, db: &Database) -> Result<()> {
|
pub async fn create(
|
||||||
db.insert_server(self).await
|
db: &Database,
|
||||||
|
data: v0::DataCreateServer,
|
||||||
|
owner: &User,
|
||||||
|
create_default_channels: bool,
|
||||||
|
) -> Result<(Server, Vec<Channel>)> {
|
||||||
|
let mut server = Server {
|
||||||
|
id: ulid::Ulid::new().to_string(),
|
||||||
|
owner: owner.id.to_string(),
|
||||||
|
name: data.name,
|
||||||
|
description: data.description,
|
||||||
|
channels: vec![],
|
||||||
|
nsfw: data.nsfw.unwrap_or(false),
|
||||||
|
default_permissions: *DEFAULT_PERMISSION_SERVER as i64,
|
||||||
|
|
||||||
|
analytics: false,
|
||||||
|
banner: None,
|
||||||
|
categories: None,
|
||||||
|
discoverable: false,
|
||||||
|
flags: None,
|
||||||
|
icon: None,
|
||||||
|
roles: HashMap::new(),
|
||||||
|
system_messages: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let channels: Vec<Channel> = if create_default_channels {
|
||||||
|
vec![
|
||||||
|
Channel::create_server_channel(
|
||||||
|
db,
|
||||||
|
&mut server,
|
||||||
|
DataCreateServerChannel {
|
||||||
|
channel_type: v0::LegacyServerChannelType::Text,
|
||||||
|
name: "General".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
server.channels = channels.iter().map(|c| c.id()).collect();
|
||||||
|
db.insert_server(&server).await?;
|
||||||
|
Ok((server, channels))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update server data
|
/// Update server data
|
||||||
@@ -209,80 +254,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* /// Create a new member in a server
|
/*
|
||||||
pub async fn create_member(
|
|
||||||
&self,
|
|
||||||
db: &Database,
|
|
||||||
user: User,
|
|
||||||
channels: Option<Vec<Channel>>,
|
|
||||||
) -> Result<Vec<Channel>> {
|
|
||||||
if db.fetch_ban(&self.id, &user.id).await.is_ok() {
|
|
||||||
return Err(Error::Banned);
|
|
||||||
}
|
|
||||||
|
|
||||||
let member = Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
server: self.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
},
|
|
||||||
joined_at: Timestamp::now_utc(),
|
|
||||||
nickname: None,
|
|
||||||
avatar: None,
|
|
||||||
roles: vec![],
|
|
||||||
timeout: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
db.insert_member(&member).await?;
|
|
||||||
|
|
||||||
let should_fetch = channels.is_none();
|
|
||||||
let mut channels = channels.unwrap_or_default();
|
|
||||||
|
|
||||||
if should_fetch {
|
|
||||||
let perm = perms(&user).server(self).member(&member);
|
|
||||||
let existing_channels = db.fetch_channels(&self.channels).await?;
|
|
||||||
for channel in existing_channels {
|
|
||||||
if perm
|
|
||||||
.clone()
|
|
||||||
.channel(&channel)
|
|
||||||
.has_permission(db, Permission::ViewChannel)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
channels.push(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* // TODO: EventV1::ServerMemberJoin {
|
|
||||||
id: self.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
}
|
|
||||||
.p(self.id.clone())
|
|
||||||
.await;
|
|
||||||
|
|
||||||
EventV1::ServerCreate {
|
|
||||||
id: self.id.clone(),
|
|
||||||
server: self.clone(),
|
|
||||||
channels: channels.clone(),
|
|
||||||
}
|
|
||||||
.private(user.id.clone())
|
|
||||||
.await; */
|
|
||||||
|
|
||||||
if let Some(id) = self
|
|
||||||
.system_messages
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|x| x.user_joined.as_ref())
|
|
||||||
{
|
|
||||||
SystemMessage::UserJoined {
|
|
||||||
id: user.id.clone(),
|
|
||||||
}
|
|
||||||
.into_message(id.to_string())
|
|
||||||
.create_no_web_push(db, id, false)
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(channels)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a member from a server
|
/// Remove a member from a server
|
||||||
pub async fn remove_member(
|
pub async fn remove_member(
|
||||||
@@ -453,109 +425,18 @@ impl SystemMessageChannels {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::HashMap;
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
|
|
||||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission, OverrideField};
|
use crate::{fixture, util::permissions::DatabasePermissionQuery};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
util::permissions::DatabasePermissionQuery, Member, MemberCompositeKey, Role, Server, User,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn permissions() {
|
async fn permissions() {
|
||||||
database_test!(|db| async move {
|
database_test!(|db| async move {
|
||||||
let owner = User::create(&db, "Owner".to_string(), None, None)
|
fixture!(db, "server_with_roles",
|
||||||
.await
|
owner user 0
|
||||||
.unwrap();
|
moderator user 1
|
||||||
|
user user 2
|
||||||
let moderator = User::create(&db, "Moderator".to_string(), None, None)
|
server server 4);
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let user = User::create(&db, "User".to_string(), None, None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let server_id = ulid::Ulid::new().to_string();
|
|
||||||
|
|
||||||
// TODO: seeder functions
|
|
||||||
// e.g. seed!("channel", "file.json")
|
|
||||||
let server = Server {
|
|
||||||
id: server_id,
|
|
||||||
owner: owner.id.clone(),
|
|
||||||
name: "My Server".to_string(),
|
|
||||||
description: None,
|
|
||||||
channels: vec![],
|
|
||||||
categories: None,
|
|
||||||
system_messages: None,
|
|
||||||
roles: HashMap::from([
|
|
||||||
(
|
|
||||||
"01F9HFTSBWTNA2F4TMSV7VM3FG".to_string(),
|
|
||||||
Role {
|
|
||||||
name: "Moderator".to_string(),
|
|
||||||
permissions: OverrideField {
|
|
||||||
a: 545270208, // TODO: explicit
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
colour: None,
|
|
||||||
hoist: true,
|
|
||||||
rank: 3,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"01FBF9DNHSRPVTWFMNB3JNB8FK".to_string(),
|
|
||||||
Role {
|
|
||||||
name: "Owner".to_string(),
|
|
||||||
permissions: Default::default(),
|
|
||||||
colour: None,
|
|
||||||
hoist: true,
|
|
||||||
rank: 0,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
default_permissions: 4000322560, // TODO: use bitfield
|
|
||||||
icon: None,
|
|
||||||
banner: None,
|
|
||||||
flags: None,
|
|
||||||
nsfw: false,
|
|
||||||
analytics: false,
|
|
||||||
discoverable: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: proper creation
|
|
||||||
server.create(&db).await.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: owner.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
roles: vec!["01FBF9DNHSRPVTWFMNB3JNB8FK".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: moderator.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
roles: vec!["01F9HFTSBWTNA2F4TMSV7VM3FG".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: user.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut query = DatabasePermissionQuery::new(&db, &owner).server(&server);
|
let mut query = DatabasePermissionQuery::new(&db, &owner).server(&server);
|
||||||
assert!(calculate_server_permissions(&mut query)
|
assert!(calculate_server_permissions(&mut query)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use crate::{events::client::EventV1, Database, File, RatelimitEvent};
|
|||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
use revolt_config::config;
|
||||||
use revolt_result::{create_error, Error, ErrorType, Result};
|
use revolt_result::{create_error, Error, ErrorType, Result};
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
@@ -218,6 +219,18 @@ impl User {
|
|||||||
.is_empty())
|
.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if this user can acquire another server
|
||||||
|
pub async fn can_acquire_server(&self, db: &Database) -> Result<()> {
|
||||||
|
let config = config().await;
|
||||||
|
if db.fetch_server_count(&self.id).await? <= config.features.limits.default.servers {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(create_error!(TooManyServers {
|
||||||
|
max: config.features.limits.default.servers
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sanitise and validate a username can be used
|
/// Sanitise and validate a username can be used
|
||||||
pub fn validate_username(username: String) -> Result<String> {
|
pub fn validate_username(username: String) -> Result<String> {
|
||||||
// Copy the username for validation
|
// Copy the username for validation
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ pub async fn generate(
|
|||||||
return Err(create_error!(LabelMe));
|
return Err(create_error!(LabelMe));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! FIXME: batch request to january
|
// TODO: batch request to january
|
||||||
let mut tasks = Vec::new();
|
let mut tasks = Vec::new();
|
||||||
|
|
||||||
for link in links {
|
for link in links {
|
||||||
|
|||||||
@@ -341,11 +341,7 @@ impl From<crate::Message> for Message {
|
|||||||
embeds: value.embeds,
|
embeds: value.embeds,
|
||||||
mentions: value.mentions,
|
mentions: value.mentions,
|
||||||
replies: value.replies,
|
replies: value.replies,
|
||||||
reactions: value
|
reactions: value.reactions,
|
||||||
.reactions
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k, v.into_iter().collect()))
|
|
||||||
.collect(),
|
|
||||||
interactions: value.interactions.into(),
|
interactions: value.interactions.into(),
|
||||||
masquerade: value.masquerade.map(|masq| masq.into()),
|
masquerade: value.masquerade.map(|masq| masq.into()),
|
||||||
}
|
}
|
||||||
@@ -369,12 +365,7 @@ impl From<crate::PartialMessage> for PartialMessage {
|
|||||||
embeds: value.embeds,
|
embeds: value.embeds,
|
||||||
mentions: value.mentions,
|
mentions: value.mentions,
|
||||||
replies: value.replies,
|
replies: value.replies,
|
||||||
reactions: value.reactions.map(|reactions| {
|
reactions: value.reactions,
|
||||||
reactions
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k, v.into_iter().collect()))
|
|
||||||
.collect()
|
|
||||||
}),
|
|
||||||
interactions: value.interactions.map(|interactions| interactions.into()),
|
interactions: value.interactions.map(|interactions| interactions.into()),
|
||||||
masquerade: value.masquerade.map(|masq| masq.into()),
|
masquerade: value.masquerade.map(|masq| masq.into()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,3 +2,6 @@ pub mod bridge;
|
|||||||
pub mod idempotency;
|
pub mod idempotency;
|
||||||
pub mod permissions;
|
pub mod permissions;
|
||||||
pub mod reference;
|
pub mod reference;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod test_fixtures;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::borrow::Cow;
|
|||||||
|
|
||||||
use revolt_permissions::{
|
use revolt_permissions::{
|
||||||
calculate_user_permissions, ChannelType, Override, PermissionQuery, PermissionValue,
|
calculate_user_permissions, ChannelType, Override, PermissionQuery, PermissionValue,
|
||||||
RelationshipStatus,
|
RelationshipStatus, DEFAULT_PERMISSION_DIRECT_MESSAGE,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Channel, Database, Member, Server, User};
|
use crate::{Channel, Database, Member, Server, User};
|
||||||
@@ -209,7 +209,7 @@ impl PermissionQuery for DatabasePermissionQuery<'_> {
|
|||||||
match channel {
|
match channel {
|
||||||
Cow::Borrowed(Channel::Group { permissions, .. })
|
Cow::Borrowed(Channel::Group { permissions, .. })
|
||||||
| Cow::Owned(Channel::Group { permissions, .. }) => Override {
|
| Cow::Owned(Channel::Group { permissions, .. }) => Override {
|
||||||
allow: permissions.unwrap_or_default() as u64,
|
allow: permissions.unwrap_or(*DEFAULT_PERMISSION_DIRECT_MESSAGE as i64) as u64,
|
||||||
deny: 0,
|
deny: 0,
|
||||||
},
|
},
|
||||||
Cow::Borrowed(Channel::TextChannel {
|
Cow::Borrowed(Channel::TextChannel {
|
||||||
@@ -344,7 +344,23 @@ impl PermissionQuery for DatabasePermissionQuery<'_> {
|
|||||||
| Cow::Owned(Channel::TextChannel { server, .. })
|
| Cow::Owned(Channel::TextChannel { server, .. })
|
||||||
| Cow::Borrowed(Channel::VoiceChannel { server, .. })
|
| Cow::Borrowed(Channel::VoiceChannel { server, .. })
|
||||||
| Cow::Owned(Channel::VoiceChannel { server, .. }) => {
|
| Cow::Owned(Channel::VoiceChannel { server, .. }) => {
|
||||||
// FIXME: may double fetch
|
if let Some(known_server) =
|
||||||
|
// I'm not sure why I can't just pattern match both at once here?
|
||||||
|
// It throws some weird error and the provided fix doesn't work :/
|
||||||
|
if let Some(Cow::Borrowed(known_server)) = self.server {
|
||||||
|
Some(known_server)
|
||||||
|
} else if let Some(Cow::Owned(ref known_server)) = self.server {
|
||||||
|
Some(known_server)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if server == &known_server.id {
|
||||||
|
// Already cached, return early.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(server) = self.database.fetch_server(server).await {
|
if let Ok(server) = self.database.fetch_server(server).await {
|
||||||
self.server.replace(Cow::Owned(server));
|
self.server.replace(Cow::Owned(server));
|
||||||
}
|
}
|
||||||
@@ -428,6 +444,26 @@ impl<'a> DatabasePermissionQuery<'a> {
|
|||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access the underlying user
|
||||||
|
pub fn user_ref(&self) -> &Option<Cow<User>> {
|
||||||
|
&self.user
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying server
|
||||||
|
pub fn channel_ref(&self) -> &Option<Cow<Channel>> {
|
||||||
|
&self.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying server
|
||||||
|
pub fn server_ref(&self) -> &Option<Cow<Server>> {
|
||||||
|
&self.server
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying member
|
||||||
|
pub fn member_ref(&self) -> &Option<Cow<Member>> {
|
||||||
|
&self.member
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Short-hand for creating a permission calculator
|
/// Short-hand for creating a permission calculator
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use schemars::{
|
|||||||
JsonSchema,
|
JsonSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Bot, Channel, Database, Emoji, Message, User, Webhook};
|
use crate::{Bot, Channel, Database, Emoji, Message, Server, User, Webhook};
|
||||||
|
|
||||||
/// Reference to some object in the database
|
/// Reference to some object in the database
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@@ -42,6 +42,11 @@ impl Reference {
|
|||||||
db.fetch_message(&self.id).await
|
db.fetch_message(&self.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch server from Ref
|
||||||
|
pub async fn as_server(&self, db: &Database) -> Result<Server> {
|
||||||
|
db.fetch_server(&self.id).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch user from Ref
|
/// Fetch user from Ref
|
||||||
pub async fn as_user(&self, db: &Database) -> Result<User> {
|
pub async fn as_user(&self, db: &Database) -> Result<User> {
|
||||||
db.fetch_user(&self.id).await
|
db.fetch_user(&self.id).await
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::{Captures, Regex};
|
||||||
|
use serde_json::from_str;
|
||||||
|
|
||||||
|
use crate::{Channel, Database, Member, Server, User};
|
||||||
|
|
||||||
|
static RE_ID: Lazy<Regex> = Lazy::new(|| Regex::new("__ID:(\\d+)__").unwrap());
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(tag = "_object_type")]
|
||||||
|
enum LoadedFixture {
|
||||||
|
User(User),
|
||||||
|
Channel(Channel),
|
||||||
|
Server(Server),
|
||||||
|
ServerMember(Member),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load_fixture(db: &Database, input: &str) -> HashMap<String, String> {
|
||||||
|
let mut ids = HashMap::<String, String>::new();
|
||||||
|
let input = RE_ID.replace_all(input, |cap: &Captures| {
|
||||||
|
let d = cap.get(1).unwrap().as_str();
|
||||||
|
|
||||||
|
if !ids.contains_key(d) {
|
||||||
|
ids.insert(d.to_string(), ulid::Ulid::new().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
ids.get(d).unwrap().clone()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deserialise the fixtures
|
||||||
|
let items: Vec<LoadedFixture> = from_str(&input).expect("Failed to deserialise fixture");
|
||||||
|
|
||||||
|
// Load all of the items within
|
||||||
|
for item in items {
|
||||||
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
match item {
|
||||||
|
LoadedFixture::User(user) => db.insert_user(&user).await.unwrap(),
|
||||||
|
LoadedFixture::Channel(channel) => db.insert_channel(&channel).await.unwrap(),
|
||||||
|
LoadedFixture::Server(server) => db.insert_server(&server).await.unwrap(),
|
||||||
|
LoadedFixture::ServerMember(member) => db.insert_member(&member).await.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return IDs for ease of use
|
||||||
|
ids
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait FetchFixture {
|
||||||
|
async fn user(&self, db: &Database, d: usize) -> User;
|
||||||
|
async fn channel(&self, db: &Database, d: usize) -> Channel;
|
||||||
|
async fn server(&self, db: &Database, d: usize) -> Server;
|
||||||
|
async fn member(&self, db: &Database, d_server: usize, d_user: usize) -> Member;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl FetchFixture for HashMap<String, String> {
|
||||||
|
async fn user(&self, db: &Database, d: usize) -> User {
|
||||||
|
db.fetch_user(self.get(&d.to_string()).unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn channel(&self, db: &Database, d: usize) -> Channel {
|
||||||
|
db.fetch_channel(self.get(&d.to_string()).unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn server(&self, db: &Database, d: usize) -> Server {
|
||||||
|
db.fetch_server(self.get(&d.to_string()).unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn member(&self, db: &Database, d_server: usize, d_user: usize) -> Member {
|
||||||
|
db.fetch_member(
|
||||||
|
self.get(&d_server.to_string()).unwrap(),
|
||||||
|
self.get(&d_user.to_string()).unwrap(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! fixture {
|
||||||
|
( $database:expr, $name:expr, $( $variable:ident $type:ident $id: expr )+ ) => {
|
||||||
|
use $crate::util::test_fixtures::FetchFixture;
|
||||||
|
|
||||||
|
let fixtures = $crate::util::test_fixtures::load_fixture(
|
||||||
|
&$database,
|
||||||
|
include_str!(concat!("../../../fixtures/", $name, ".json")),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
$(
|
||||||
|
let $variable = fixtures.$type(&$database, $id).await;
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -18,8 +18,8 @@ default = ["serde", "partials"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-config = { version = "0.6.8", path = "../config" }
|
revolt-config = { version = "0.6.13", path = "../config" }
|
||||||
revolt-permissions = { version = "0.6.8", path = "../permissions" }
|
revolt-permissions = { version = "0.6.13", path = "../permissions" }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
regex = "1"
|
regex = "1"
|
||||||
@@ -32,7 +32,7 @@ serde = { version = "1", features = ["derive"], optional = true }
|
|||||||
iso8601-timestamp = { version = "0.2.11", features = ["schema", "bson"] }
|
iso8601-timestamp = { version = "0.2.11", features = ["schema", "bson"] }
|
||||||
|
|
||||||
# Spec Generation
|
# Spec Generation
|
||||||
schemars = { version = "0.8.8", optional = true }
|
schemars = { version = "0.8.8", optional = true, features = ["indexmap1"] }
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validator = { version = "0.16.0", optional = true, features = ["derive"] }
|
validator = { version = "0.16.0", optional = true, features = ["derive"] }
|
||||||
|
|||||||
@@ -212,24 +212,52 @@ auto_derived!(
|
|||||||
#[cfg_attr(feature = "validator", derive(validator::Validate))]
|
#[cfg_attr(feature = "validator", derive(validator::Validate))]
|
||||||
pub struct DataCreateGroup {
|
pub struct DataCreateGroup {
|
||||||
/// Group name
|
/// Group name
|
||||||
#[validate(length(min = 1, max = 32))]
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Group description
|
/// Group description
|
||||||
#[validate(length(min = 0, max = 1024))]
|
#[cfg_attr(feature = "validator", validate(length(min = 0, max = 1024)))]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// Group icon
|
/// Group icon
|
||||||
#[validate(length(min = 1, max = 128))]
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
/// Array of user IDs to add to the group
|
/// Array of user IDs to add to the group
|
||||||
///
|
///
|
||||||
/// Must be friends with these users.
|
/// Must be friends with these users.
|
||||||
#[validate(length(min = 0, max = 49))]
|
#[cfg_attr(feature = "validator", validate(length(min = 0, max = 49)))]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub users: HashSet<String>,
|
pub users: HashSet<String>,
|
||||||
/// Whether this group is age-restricted
|
/// Whether this group is age-restricted
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub nsfw: Option<bool>,
|
pub nsfw: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Server Channel Type
|
||||||
|
#[derive(Default)]
|
||||||
|
pub enum LegacyServerChannelType {
|
||||||
|
/// Text Channel
|
||||||
|
#[default]
|
||||||
|
Text,
|
||||||
|
/// Voice Channel
|
||||||
|
Voice,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create new server channel
|
||||||
|
#[derive(Default)]
|
||||||
|
#[cfg_attr(feature = "validator", derive(validator::Validate))]
|
||||||
|
pub struct DataCreateServerChannel {
|
||||||
|
/// Channel type
|
||||||
|
#[serde(rename = "type", default = "LegacyServerChannelType::default")]
|
||||||
|
pub channel_type: LegacyServerChannelType,
|
||||||
|
/// Channel name
|
||||||
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
||||||
|
pub name: String,
|
||||||
|
/// Channel description
|
||||||
|
#[cfg_attr(feature = "validator", validate(length(min = 0, max = 1024)))]
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// Whether this channel is age restricted
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub nsfw: Option<bool>,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
impl Channel {
|
impl Channel {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use std::{
|
use std::time::SystemTime;
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
time::SystemTime,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use indexmap::{IndexMap, IndexSet};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use revolt_config::config;
|
use revolt_config::config;
|
||||||
@@ -55,8 +53,8 @@ auto_derived_partial!(
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub replies: Option<Vec<String>>,
|
pub replies: Option<Vec<String>>,
|
||||||
/// Hashmap of emoji IDs to array of user IDs
|
/// Hashmap of emoji IDs to array of user IDs
|
||||||
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
|
#[serde(skip_serializing_if = "IndexMap::is_empty", default)]
|
||||||
pub reactions: HashMap<String, HashSet<String>>,
|
pub reactions: IndexMap<String, IndexSet<String>>,
|
||||||
/// Information about how this message should be interacted with
|
/// Information about how this message should be interacted with
|
||||||
#[serde(skip_serializing_if = "Interactions::is_default", default)]
|
#[serde(skip_serializing_if = "Interactions::is_default", default)]
|
||||||
pub interactions: Interactions,
|
pub interactions: Interactions,
|
||||||
@@ -98,17 +96,19 @@ auto_derived!(
|
|||||||
/// Name and / or avatar override information
|
/// Name and / or avatar override information
|
||||||
#[cfg_attr(feature = "validator", derive(Validate))]
|
#[cfg_attr(feature = "validator", derive(Validate))]
|
||||||
pub struct Masquerade {
|
pub struct Masquerade {
|
||||||
// FIXME: missing validation
|
|
||||||
/// Replace the display name shown on this message
|
/// Replace the display name shown on this message
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[validate(length(min = 1, max = 32))]
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
/// Replace the avatar shown on this message (URL to image file)
|
/// Replace the avatar shown on this message (URL to image file)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[validate(length(min = 1, max = 256))]
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
/// Replace the display role colour shown on this message
|
/// Replace the display role colour shown on this message
|
||||||
///
|
///
|
||||||
/// Must have `ManageRole` permission to use
|
/// Must have `ManageRole` permission to use
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[validate(length(min = 1, max = 128), regex = "RE_COLOUR")]
|
||||||
pub colour: Option<String>,
|
pub colour: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ auto_derived!(
|
|||||||
pub struct Interactions {
|
pub struct Interactions {
|
||||||
/// Reactions which should always appear and be distinct
|
/// Reactions which should always appear and be distinct
|
||||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
pub reactions: Option<HashSet<String>>,
|
pub reactions: Option<IndexSet<String>>,
|
||||||
/// Whether reactions should be restricted to the given list
|
/// Whether reactions should be restricted to the given list
|
||||||
///
|
///
|
||||||
/// Can only be set to true if reactions list is of at least length 1
|
/// Can only be set to true if reactions list is of at least length 1
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use super::File;
|
use super::{Channel, File};
|
||||||
|
|
||||||
use revolt_permissions::OverrideField;
|
use revolt_permissions::OverrideField;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
auto_derived_partial!(
|
auto_derived_partial!(
|
||||||
/// Server
|
/// Server
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
@@ -19,7 +22,7 @@ auto_derived_partial!(
|
|||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
|
|
||||||
/// Channels within this server
|
/// Channels within this server
|
||||||
// ! FIXME: this may be redundant
|
// TODO: investigate if this is redundant and can be removed
|
||||||
pub channels: Vec<String>,
|
pub channels: Vec<String>,
|
||||||
/// Categories for this server
|
/// Categories for this server
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
@@ -141,4 +144,27 @@ auto_derived!(
|
|||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub user_banned: Option<String>,
|
pub user_banned: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about new server to create
|
||||||
|
#[derive(Default)]
|
||||||
|
#[cfg_attr(feature = "validator", derive(Validate))]
|
||||||
|
pub struct DataCreateServer {
|
||||||
|
/// Server name
|
||||||
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
||||||
|
pub name: String,
|
||||||
|
/// Server description
|
||||||
|
#[cfg_attr(feature = "validator", validate(length(min = 0, max = 1024)))]
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// Whether this server is age-restricted
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub nsfw: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information returned when creating server
|
||||||
|
pub struct CreateServerLegacyResponse {
|
||||||
|
/// Server object
|
||||||
|
pub server: Server,
|
||||||
|
/// Default channels
|
||||||
|
pub channels: Vec<Channel>,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -21,7 +21,7 @@ async-std = { version = "1.8.0", features = ["attributes"] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-result = { version = "0.6.5", path = "../result" }
|
revolt-result = { version = "0.6.13", path = "../result" }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
auto_ops = "0.3.0"
|
auto_ops = "0.3.0"
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ pub async fn calculate_user_permissions<P: PermissionQuery>(query: &mut P) -> Pe
|
|||||||
} else {
|
} else {
|
||||||
permissions.into()
|
permissions.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add boolean switch for permission for users to globally message a user
|
||||||
|
// maybe an enum?
|
||||||
|
// PrivacyLevel { Private, Friends, Mutual, Public, Global }
|
||||||
|
|
||||||
|
// TODO: add boolean switch for permission for users to mutually DM a user
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate permissions against a server
|
/// Calculate permissions against a server
|
||||||
@@ -105,7 +111,7 @@ pub async fn calculate_channel_permissions<P: PermissionQuery>(query: &mut P) ->
|
|||||||
query.set_server_from_channel().await;
|
query.set_server_from_channel().await;
|
||||||
|
|
||||||
if query.are_we_server_owner().await {
|
if query.are_we_server_owner().await {
|
||||||
return ChannelPermission::GrantAllSafe.into();
|
ChannelPermission::GrantAllSafe.into()
|
||||||
} else if query.are_we_a_member().await {
|
} else if query.are_we_a_member().await {
|
||||||
let mut permissions = calculate_server_permissions(query).await;
|
let mut permissions = calculate_server_permissions(query).await;
|
||||||
permissions.apply(query.get_default_channel_permissions().await);
|
permissions.apply(query.get_default_channel_permissions().await);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ pub async fn invite_bot(
|
|||||||
.await
|
.await
|
||||||
.throw_if_lacking_channel_permission(ChannelPermission::ManageServer)?;
|
.throw_if_lacking_channel_permission(ChannelPermission::ManageServer)?;
|
||||||
|
|
||||||
Member::create(db, &server, &bot_user)
|
Member::create(db, &server, &bot_user, None)
|
||||||
.await
|
.await
|
||||||
.map(|_| EmptyResponse)
|
.map(|_| EmptyResponse)
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ pub async fn invite_bot(
|
|||||||
mod test {
|
mod test {
|
||||||
use crate::{rocket, util::test::TestHarness};
|
use crate::{rocket, util::test::TestHarness};
|
||||||
use revolt_database::{events::client::EventV1, Bot, Channel, Server};
|
use revolt_database::{events::client::EventV1, Bot, Channel, Server};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0::{self, DataCreateServer};
|
||||||
use rocket::http::{ContentType, Header, Status};
|
use rocket::http::{ContentType, Header, Status};
|
||||||
|
|
||||||
#[rocket::async_test]
|
#[rocket::async_test]
|
||||||
@@ -125,26 +125,17 @@ mod test {
|
|||||||
.await
|
.await
|
||||||
.expect("`Bot`");
|
.expect("`Bot`");
|
||||||
|
|
||||||
// FIXME: Server::create_server
|
let (server, _) = Server::create(
|
||||||
let server = Server {
|
&harness.db,
|
||||||
id: ulid::Ulid::new().to_string(),
|
DataCreateServer {
|
||||||
name: TestHarness::rand_string(),
|
name: TestHarness::rand_string(),
|
||||||
owner: user.id.to_string(),
|
..Default::default()
|
||||||
analytics: false,
|
},
|
||||||
discoverable: false,
|
&user,
|
||||||
nsfw: false,
|
false,
|
||||||
banner: None,
|
)
|
||||||
icon: None,
|
.await
|
||||||
categories: None,
|
.unwrap();
|
||||||
channels: vec![],
|
|
||||||
default_permissions: 0,
|
|
||||||
description: None,
|
|
||||||
flags: None,
|
|
||||||
roles: Default::default(),
|
|
||||||
system_messages: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
server.create(&harness.db).await.unwrap();
|
|
||||||
|
|
||||||
let response = harness
|
let response = harness
|
||||||
.client
|
.client
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ pub async fn req(
|
|||||||
|
|
||||||
match &channel {
|
match &channel {
|
||||||
Channel::Group { .. } => {
|
Channel::Group { .. } => {
|
||||||
// FIXME: use permissions here?
|
// TODO: use permissions here? interesting if users could block new group invites
|
||||||
// interesting if users could block new group invites
|
|
||||||
let member = member_id.as_user(db).await?;
|
let member = member_id.as_user(db).await?;
|
||||||
if !user.is_friends_with(&member.id) {
|
if !user.is_friends_with(&member.id) {
|
||||||
return Err(create_error!(NotFriends));
|
return Err(create_error!(NotFriends));
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ mod test {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
dbg!(response.into_string().await);
|
dbg!(response.into_string().await);
|
||||||
// TODO: finish impl
|
// FIXME: finish impl
|
||||||
// assert_eq!(response.status(), Status::NotFound);
|
// assert_eq!(response.status(), Status::NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +185,6 @@ mod test {
|
|||||||
.dispatch()
|
.dispatch()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// TODO: impl assert_eq!(response.status(), Status::Forbidden);
|
// FIXME: impl assert_eq!(response.status(), Status::Forbidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ pub async fn req(
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
BulkMessageResponse::transform(db, Some(&channel), messages, include_users)
|
BulkMessageResponse::transform(db, Some(&channel), messages, &user, include_users)
|
||||||
.await
|
.await
|
||||||
.map(Json)
|
.map(Json)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ pub async fn req(
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
BulkMessageResponse::transform(db, Some(&channel), messages, include_users)
|
BulkMessageResponse::transform(db, Some(&channel), messages, &user, include_users)
|
||||||
.await
|
.await
|
||||||
.map(Json)
|
.map(Json)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use chrono::{Duration, Utc};
|
||||||
use revolt_database::util::permissions::DatabasePermissionQuery;
|
use revolt_database::util::permissions::DatabasePermissionQuery;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::idempotency::IdempotencyKey, util::reference::Reference, Database, User,
|
util::idempotency::IdempotencyKey, util::reference::Reference, Database, User,
|
||||||
@@ -61,6 +62,18 @@ pub async fn message_send(
|
|||||||
interactions.validate(db, &permissions).await?;
|
interactions.validate(db, &permissions).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disallow mentions for new users (TRUST-0: <12 hours age) in public servers
|
||||||
|
let allow_mentions = if let Some(server) = query.server_ref() {
|
||||||
|
if server.discoverable {
|
||||||
|
(Utc::now() - ulid::Ulid::from_string(&user.id).unwrap().datetime())
|
||||||
|
>= Duration::hours(12)
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
// Create the message
|
// Create the message
|
||||||
let author: v0::User = user.clone().into(db, Some(&user)).await;
|
let author: v0::User = user.clone().into(db, Some(&user)).await;
|
||||||
Ok(Json(
|
Ok(Json(
|
||||||
@@ -71,6 +84,7 @@ pub async fn message_send(
|
|||||||
v0::MessageAuthor::User(&author),
|
v0::MessageAuthor::User(&author),
|
||||||
idempotency,
|
idempotency,
|
||||||
permissions.has_channel_permission(ChannelPermission::SendEmbeds),
|
permissions.has_channel_permission(ChannelPermission::SendEmbeds),
|
||||||
|
allow_mentions,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.into(),
|
.into(),
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ pub async fn create_emoji(
|
|||||||
|
|
||||||
// Check that we haven't hit the emoji limit
|
// Check that we haven't hit the emoji limit
|
||||||
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
|
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
|
||||||
if emojis.len() > config.features.limits.default.server_emoji {
|
if emojis.len() >= config.features.limits.default.server_emoji {
|
||||||
return Err(create_error!(TooManyEmoji {
|
return Err(create_error!(TooManyEmoji {
|
||||||
max: config.features.limits.default.server_emoji,
|
max: config.features.limits.default.server_emoji,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,119 +1,39 @@
|
|||||||
use std::collections::HashMap;
|
use revolt_database::util::permissions::DatabasePermissionQuery;
|
||||||
|
use revolt_database::{util::reference::Reference, Channel, Database, User};
|
||||||
use revolt_quark::{
|
use revolt_models::v0;
|
||||||
models::{server::PartialServer, Channel, User},
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
perms, Db, Error, Permission, Ref, Result, variables::delta::MAX_CHANNEL_COUNT,
|
use revolt_result::{create_error, Result};
|
||||||
};
|
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use serde::{Deserialize, Serialize};
|
use rocket::State;
|
||||||
use ulid::Ulid;
|
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
/// # Channel Type
|
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Default)]
|
|
||||||
enum ChannelType {
|
|
||||||
/// Text Channel
|
|
||||||
#[default]
|
|
||||||
Text,
|
|
||||||
/// Voice Channel
|
|
||||||
Voice,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Channel Data
|
|
||||||
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
|
|
||||||
pub struct DataCreateChannel {
|
|
||||||
/// Channel type
|
|
||||||
#[serde(rename = "type", default = "ChannelType::default")]
|
|
||||||
channel_type: ChannelType,
|
|
||||||
/// Channel name
|
|
||||||
#[validate(length(min = 1, max = 32))]
|
|
||||||
name: String,
|
|
||||||
/// Channel description
|
|
||||||
#[validate(length(min = 0, max = 1024))]
|
|
||||||
description: Option<String>,
|
|
||||||
/// Whether this channel is age restricted
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
nsfw: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Create Channel
|
/// # Create Channel
|
||||||
///
|
///
|
||||||
/// Create a new Text or Voice channel.
|
/// Create a new Text or Voice channel.
|
||||||
#[openapi(tag = "Server Information")]
|
#[openapi(tag = "Server Information")]
|
||||||
#[post("/<target>/channels", data = "<info>")]
|
#[post("/<server>/channels", data = "<data>")]
|
||||||
pub async fn req(
|
pub async fn create_server_channel(
|
||||||
db: &Db,
|
db: &State<Database>,
|
||||||
user: User,
|
user: User,
|
||||||
target: Ref,
|
server: Reference,
|
||||||
info: Json<DataCreateChannel>,
|
data: Json<v0::DataCreateServerChannel>,
|
||||||
) -> Result<Json<Channel>> {
|
) -> Result<Json<v0::Channel>> {
|
||||||
let info = info.into_inner();
|
let data = data.into_inner();
|
||||||
info.validate()
|
data.validate().map_err(|error| {
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
create_error!(FailedValidation {
|
||||||
|
error: error.to_string()
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut server = target.as_server(db).await?;
|
let mut server = server.as_server(db).await?;
|
||||||
perms(&user)
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||||
.server(&server)
|
calculate_server_permissions(&mut query)
|
||||||
.throw_permission(db, Permission::ManageChannel)
|
.await
|
||||||
.await?;
|
.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?;
|
||||||
|
|
||||||
if server.channels.len() > *MAX_CHANNEL_COUNT {
|
Channel::create_server_channel(db, &mut server, data, true)
|
||||||
return Err(Error::TooManyChannels { max: *MAX_CHANNEL_COUNT })
|
.await
|
||||||
};
|
.map(|channel| channel.into())
|
||||||
|
.map(Json)
|
||||||
let id = Ulid::new().to_string();
|
|
||||||
let mut channels = server.channels.clone();
|
|
||||||
channels.push(id.clone());
|
|
||||||
|
|
||||||
let DataCreateChannel {
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
nsfw,
|
|
||||||
channel_type,
|
|
||||||
} = info;
|
|
||||||
let channel = match channel_type {
|
|
||||||
ChannelType::Text => Channel::TextChannel {
|
|
||||||
id,
|
|
||||||
server: server.id.clone(),
|
|
||||||
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
|
|
||||||
icon: None,
|
|
||||||
last_message_id: None,
|
|
||||||
|
|
||||||
default_permissions: None,
|
|
||||||
role_permissions: HashMap::new(),
|
|
||||||
|
|
||||||
nsfw: nsfw.unwrap_or(false),
|
|
||||||
},
|
|
||||||
ChannelType::Voice => Channel::VoiceChannel {
|
|
||||||
id,
|
|
||||||
server: server.id.clone(),
|
|
||||||
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
icon: None,
|
|
||||||
|
|
||||||
default_permissions: None,
|
|
||||||
role_permissions: HashMap::new(),
|
|
||||||
|
|
||||||
nsfw: nsfw.unwrap_or(false),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
channel.create(db).await?;
|
|
||||||
server
|
|
||||||
.update(
|
|
||||||
db,
|
|
||||||
PartialServer {
|
|
||||||
channels: Some(channels),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Json(channel))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::models::server_member::MemberResponse;
|
||||||
models::{Member, User},
|
use revolt_quark::{models::User, perms, Db, Ref, Result};
|
||||||
perms, Db, Ref, Result,
|
|
||||||
};
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
/// # Fetch Member
|
/// # Fetch Member
|
||||||
///
|
///
|
||||||
/// Retrieve a member.
|
/// Retrieve a member.
|
||||||
#[openapi(tag = "Server Members")]
|
#[openapi(tag = "Server Members")]
|
||||||
#[get("/<target>/members/<member>")]
|
#[get("/<target>/members/<member>?<roles>")]
|
||||||
pub async fn req(db: &Db, user: User, target: Ref, member: Ref) -> Result<Json<Member>> {
|
pub async fn req(
|
||||||
|
db: &Db,
|
||||||
|
user: User,
|
||||||
|
target: Ref,
|
||||||
|
member: Ref,
|
||||||
|
roles: Option<bool>,
|
||||||
|
) -> Result<Json<MemberResponse>> {
|
||||||
let server = target.as_server(db).await?;
|
let server = target.as_server(db).await?;
|
||||||
perms(&user).server(&server).calc(db).await?;
|
perms(&user).server(&server).calc(db).await?;
|
||||||
|
|
||||||
member.as_member(db, &server.id).await.map(Json)
|
let member_response: MemberResponse = match roles {
|
||||||
|
Some(true) => member.as_member_with_roles(db, &server.id).await?.into(),
|
||||||
|
_ => member.as_member(db, &server.id).await?.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Json(member_response))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ mod permissions_set_default;
|
|||||||
mod roles_create;
|
mod roles_create;
|
||||||
mod roles_delete;
|
mod roles_delete;
|
||||||
mod roles_edit;
|
mod roles_edit;
|
||||||
|
mod roles_fetch;
|
||||||
mod server_ack;
|
mod server_ack;
|
||||||
mod server_create;
|
mod server_create;
|
||||||
mod server_delete;
|
mod server_delete;
|
||||||
@@ -25,12 +26,12 @@ mod server_fetch;
|
|||||||
|
|
||||||
pub fn routes() -> (Vec<Route>, OpenApi) {
|
pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||||
openapi_get_routes_spec![
|
openapi_get_routes_spec![
|
||||||
server_create::req,
|
server_create::create_server,
|
||||||
server_delete::req,
|
server_delete::req,
|
||||||
server_fetch::req,
|
server_fetch::req,
|
||||||
server_edit::req,
|
server_edit::req,
|
||||||
server_ack::req,
|
server_ack::req,
|
||||||
channel_create::req,
|
channel_create::create_server_channel,
|
||||||
member_fetch_all::req,
|
member_fetch_all::req,
|
||||||
member_remove::req,
|
member_remove::req,
|
||||||
member_fetch::req,
|
member_fetch::req,
|
||||||
@@ -42,6 +43,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
|||||||
invites_fetch::req,
|
invites_fetch::req,
|
||||||
roles_create::req,
|
roles_create::req,
|
||||||
roles_edit::req,
|
roles_edit::req,
|
||||||
|
roles_fetch::req,
|
||||||
roles_delete::req,
|
roles_delete::req,
|
||||||
permissions_set::req,
|
permissions_set::req,
|
||||||
permissions_set_default::req,
|
permissions_set_default::req,
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
use revolt_quark::{models::server::Role, Db, Error, Ref, Result};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
/// # Fetch Role
|
||||||
|
///
|
||||||
|
/// Fetch a role by its id.
|
||||||
|
#[openapi(tag = "Server Permissions")]
|
||||||
|
#[get("/<target>/roles/<role_id>")]
|
||||||
|
pub async fn req(db: &Db, target: Ref, role_id: String) -> Result<Json<Role>> {
|
||||||
|
let server = target.as_server(db).await?;
|
||||||
|
|
||||||
|
let role = server.roles.get(&role_id);
|
||||||
|
|
||||||
|
if let Some(role) = role {
|
||||||
|
Ok(Json(role.clone()))
|
||||||
|
} else {
|
||||||
|
Err(Error::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,102 +1,39 @@
|
|||||||
use std::collections::HashMap;
|
use revolt_database::{Database, Member, Server, User};
|
||||||
|
use revolt_models::v0;
|
||||||
use revolt_quark::{
|
use revolt_result::{create_error, Result};
|
||||||
models::{Channel, Server, User},
|
|
||||||
variables::delta::MAX_SERVER_COUNT,
|
|
||||||
Db, Error, Result, DEFAULT_PERMISSION_SERVER,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use serde::{Deserialize, Serialize};
|
use rocket::State;
|
||||||
use ulid::Ulid;
|
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
/// # Server Data
|
|
||||||
#[derive(Validate, Deserialize, JsonSchema)]
|
|
||||||
pub struct DataCreateServer {
|
|
||||||
/// Server name
|
|
||||||
#[validate(length(min = 1, max = 32))]
|
|
||||||
name: String,
|
|
||||||
/// Server description
|
|
||||||
#[validate(length(min = 0, max = 1024))]
|
|
||||||
description: Option<String>,
|
|
||||||
/// Whether this server is age-restricted
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
nsfw: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Create Server Response
|
|
||||||
#[derive(Validate, Serialize, JsonSchema)]
|
|
||||||
pub struct CreateServerResponse {
|
|
||||||
/// Server object
|
|
||||||
server: Server,
|
|
||||||
/// Default channels
|
|
||||||
channels: Vec<Channel>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Create Server
|
/// # Create Server
|
||||||
///
|
///
|
||||||
/// Create a new server.
|
/// Create a new server.
|
||||||
#[openapi(tag = "Server Information")]
|
#[openapi(tag = "Server Information")]
|
||||||
#[post("/create", data = "<info>")]
|
#[post("/create", data = "<data>")]
|
||||||
pub async fn req(
|
pub async fn create_server(
|
||||||
db: &Db,
|
db: &State<Database>,
|
||||||
user: User,
|
user: User,
|
||||||
info: Json<DataCreateServer>,
|
data: Json<v0::DataCreateServer>,
|
||||||
) -> Result<Json<CreateServerResponse>> {
|
) -> Result<Json<v0::CreateServerLegacyResponse>> {
|
||||||
if user.bot.is_some() {
|
if user.bot.is_some() {
|
||||||
return Err(Error::IsBot);
|
return Err(create_error!(IsBot));
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = info.into_inner();
|
let data = data.into_inner();
|
||||||
info.validate()
|
data.validate().map_err(|error| {
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
create_error!(FailedValidation {
|
||||||
|
error: error.to_string()
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
if !user.can_acquire_server(db).await? {
|
user.can_acquire_server(db).await?;
|
||||||
return Err(Error::TooManyServers {
|
|
||||||
max: *MAX_SERVER_COUNT,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let DataCreateServer {
|
let (server, channels) = Server::create(db, data, &user, true).await?;
|
||||||
name,
|
let channels = Member::create(db, &server, &user, Some(channels)).await?;
|
||||||
description,
|
|
||||||
nsfw,
|
|
||||||
} = info;
|
|
||||||
|
|
||||||
let channel_id = Ulid::new().to_string();
|
Ok(Json(v0::CreateServerLegacyResponse {
|
||||||
let server_id = Ulid::new().to_string();
|
server: server.into(),
|
||||||
|
channels: channels.into_iter().map(|channel| channel.into()).collect(),
|
||||||
let channel = Channel::TextChannel {
|
}))
|
||||||
id: channel_id.clone(),
|
|
||||||
server: server_id.clone(),
|
|
||||||
|
|
||||||
name: "General".into(),
|
|
||||||
description: None,
|
|
||||||
|
|
||||||
icon: None,
|
|
||||||
last_message_id: None,
|
|
||||||
|
|
||||||
default_permissions: None,
|
|
||||||
role_permissions: HashMap::new(),
|
|
||||||
|
|
||||||
nsfw: nsfw.unwrap_or(false),
|
|
||||||
};
|
|
||||||
|
|
||||||
db.insert_channel(&channel).await?;
|
|
||||||
|
|
||||||
let server = Server {
|
|
||||||
id: server_id.clone(),
|
|
||||||
owner: user.id.clone(),
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
channels: vec![channel_id],
|
|
||||||
nsfw: nsfw.unwrap_or(false),
|
|
||||||
default_permissions: *DEFAULT_PERMISSION_SERVER as i64,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
server.create(db).await?;
|
|
||||||
let channels = server.create_member(db, user, Some(vec![channel])).await?;
|
|
||||||
Ok(Json(CreateServerResponse { server, channels }))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ pub async fn webhook_execute(
|
|||||||
v0::MessageAuthor::Webhook(&webhook.into()),
|
v0::MessageAuthor::Webhook(&webhook.into()),
|
||||||
idempotency,
|
idempotency,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.into(),
|
.into(),
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ impl Ratelimiter {
|
|||||||
pub fn from(
|
pub fn from(
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
(bucket, resource): (&str, Option<&str>),
|
(bucket, resource): (&str, Option<&str>),
|
||||||
) -> Result<Ratelimiter, u128> {
|
) -> Result<Ratelimiter, Ratelimiter> {
|
||||||
let mut key = DefaultHasher::new();
|
let mut key = DefaultHasher::new();
|
||||||
key.write(identifier.as_bytes());
|
key.write(identifier.as_bytes());
|
||||||
key.write(bucket.as_bytes());
|
key.write(bucket.as_bytes());
|
||||||
@@ -194,27 +194,29 @@ impl Ratelimiter {
|
|||||||
let mut entry = Entry::from(key);
|
let mut entry = Entry::from(key);
|
||||||
|
|
||||||
let remaining = entry.get_remaining(limit);
|
let remaining = entry.get_remaining(limit);
|
||||||
if remaining > 0 {
|
|
||||||
entry.deduct();
|
|
||||||
|
|
||||||
let reset = entry.left_until_reset();
|
let reset = entry.left_until_reset();
|
||||||
entry.save(key);
|
let mut ratelimiter = Ratelimiter {
|
||||||
|
|
||||||
Ok(Ratelimiter {
|
|
||||||
key,
|
key,
|
||||||
limit,
|
limit,
|
||||||
remaining: remaining - 1,
|
remaining,
|
||||||
reset,
|
reset,
|
||||||
})
|
};
|
||||||
} else {
|
if remaining == 0 {
|
||||||
Err(entry.left_until_reset())
|
return Err(ratelimiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry.deduct();
|
||||||
|
entry.save(key);
|
||||||
|
ratelimiter.remaining -= 1;
|
||||||
|
ratelimiter.reset = entry.left_until_reset();
|
||||||
|
|
||||||
|
Ok(ratelimiter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<'r> FromRequest<'r> for Ratelimiter {
|
impl<'r> FromRequest<'r> for Ratelimiter {
|
||||||
type Error = u128;
|
type Error = Ratelimiter;
|
||||||
|
|
||||||
async fn from_request(request: &'r rocket::Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r rocket::Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
let ratelimiter = request
|
let ratelimiter = request
|
||||||
@@ -233,7 +235,7 @@ impl<'r> FromRequest<'r> for Ratelimiter {
|
|||||||
|
|
||||||
match ratelimiter {
|
match ratelimiter {
|
||||||
Ok(ratelimiter) => Outcome::Success(*ratelimiter),
|
Ok(ratelimiter) => Outcome::Success(*ratelimiter),
|
||||||
Err(retry_after) => Outcome::Failure((Status::TooManyRequests, *retry_after)),
|
Err(ratelimiter) => Outcome::Failure((Status::TooManyRequests, *ratelimiter)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,9 +277,10 @@ impl Fairing for RatelimitFairing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
|
async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
|
||||||
use rocket::outcome::Outcome;
|
let guard = request.guard::<Ratelimiter>().await;
|
||||||
match request.guard::<Ratelimiter>().await {
|
let (Outcome::Success(ratelimiter) | Outcome::Failure((_, ratelimiter))) = guard else {
|
||||||
Outcome::Success(ratelimiter) => {
|
unreachable!()
|
||||||
|
};
|
||||||
let Ratelimiter {
|
let Ratelimiter {
|
||||||
key,
|
key,
|
||||||
limit,
|
limit,
|
||||||
@@ -289,9 +292,9 @@ impl Fairing for RatelimitFairing {
|
|||||||
response.set_raw_header("X-RateLimit-Bucket", key.to_string());
|
response.set_raw_header("X-RateLimit-Bucket", key.to_string());
|
||||||
response.set_raw_header("X-RateLimit-Remaining", remaining.to_string());
|
response.set_raw_header("X-RateLimit-Remaining", remaining.to_string());
|
||||||
response.set_raw_header("X-RateLimit-Reset-After", reset.to_string());
|
response.set_raw_header("X-RateLimit-Reset-After", reset.to_string());
|
||||||
}
|
|
||||||
Outcome::Failure(_) => response.set_status(Status::TooManyRequests),
|
if guard.is_failure() {
|
||||||
Outcome::Forward(_) => unreachable!(),
|
response.set_status(Status::TooManyRequests);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,11 +311,14 @@ impl<'r> FromRequest<'r> for RatelimitInformation {
|
|||||||
type Error = u128;
|
type Error = u128;
|
||||||
|
|
||||||
async fn from_request(request: &'r rocket::Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r rocket::Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
Outcome::Success(match request.guard::<Ratelimiter>().await {
|
let info = match request.guard::<Ratelimiter>().await {
|
||||||
Outcome::Success(ratelimiter) => RatelimitInformation::Success(ratelimiter),
|
Outcome::Success(ratelimiter) => RatelimitInformation::Success(ratelimiter),
|
||||||
Outcome::Failure((_, retry_after)) => RatelimitInformation::Failure { retry_after },
|
Outcome::Failure((_, ratelimiter)) => RatelimitInformation::Failure {
|
||||||
|
retry_after: ratelimiter.reset,
|
||||||
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
})
|
};
|
||||||
|
Outcome::Success(info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.6.8"
|
version = "0.6.13"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ pub enum EventV1 {
|
|||||||
id: String,
|
id: String,
|
||||||
server: Server,
|
server: Server,
|
||||||
channels: Vec<Channel>,
|
channels: Vec<Channel>,
|
||||||
|
emojis: Vec<Emoji>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Update existing server
|
/// Update existing server
|
||||||
|
|||||||
@@ -401,6 +401,7 @@ impl State {
|
|||||||
id,
|
id,
|
||||||
server,
|
server,
|
||||||
channels,
|
channels,
|
||||||
|
emojis: _,
|
||||||
} => {
|
} => {
|
||||||
self.insert_subscription(id.clone());
|
self.insert_subscription(id.clone());
|
||||||
self.cache.servers.insert(id.clone(), server.clone());
|
self.cache.servers.insert(id.clone(), server.clone());
|
||||||
@@ -568,8 +569,7 @@ impl EventV1 {
|
|||||||
pub async fn p_user(self, id: String, db: &Database) {
|
pub async fn p_user(self, id: String, db: &Database) {
|
||||||
self.clone().p(id.clone()).await;
|
self.clone().p(id.clone()).await;
|
||||||
|
|
||||||
// ! FIXME: this should be captured by member list in the future
|
// TODO: this should be captured by member list in the future and not immediately fanned out to users
|
||||||
// ! and not immediately fanned out to users
|
|
||||||
if let Ok(members) = db.fetch_all_memberships(&id).await {
|
if let Ok(members) = db.fetch_all_memberships(&id).await {
|
||||||
for member in members {
|
for member in members {
|
||||||
self.clone().p(member.id.server).await;
|
self.clone().p(member.id.server).await;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::models::server_member::{FieldsMember, Member, MemberCompositeKey, PartialMember};
|
use crate::models::server_member::{
|
||||||
|
FieldsMember, Member, MemberCompositeKey, MemberWithRoles, PartialMember,
|
||||||
|
};
|
||||||
use crate::{AbstractServerMember, Result};
|
use crate::{AbstractServerMember, Result};
|
||||||
|
|
||||||
use super::super::DummyDb;
|
use super::super::DummyDb;
|
||||||
@@ -8,6 +10,9 @@ impl AbstractServerMember for DummyDb {
|
|||||||
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member> {
|
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member> {
|
||||||
Ok(Member::new(server.into(), user.into()))
|
Ok(Member::new(server.into(), user.into()))
|
||||||
}
|
}
|
||||||
|
async fn fetch_member_with_roles(&self, server: &str, user: &str) -> Result<MemberWithRoles> {
|
||||||
|
Ok(MemberWithRoles::new(server.into(), user.into()))
|
||||||
|
}
|
||||||
|
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()> {
|
async fn insert_member(&self, member: &Member) -> Result<()> {
|
||||||
info!("Create {member:?}");
|
info!("Create {member:?}");
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ impl Message {
|
|||||||
/// Add a reaction to a message
|
/// Add a reaction to a message
|
||||||
pub async fn add_reaction(&self, db: &Database, user: &User, emoji: &str) -> Result<()> {
|
pub async fn add_reaction(&self, db: &Database, user: &User, emoji: &str) -> Result<()> {
|
||||||
// Check how many reactions are already on the message
|
// Check how many reactions are already on the message
|
||||||
if self.reactions.len() >= 20 {
|
if self.reactions.len() >= 20 && !self.reactions.contains_key(emoji) {
|
||||||
return Err(Error::InvalidOperation);
|
return Err(Error::InvalidOperation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,11 +382,16 @@ impl BulkMessageResponse {
|
|||||||
db: &Database,
|
db: &Database,
|
||||||
channel: Option<&Channel>,
|
channel: Option<&Channel>,
|
||||||
messages: Vec<Message>,
|
messages: Vec<Message>,
|
||||||
|
user: &User,
|
||||||
include_users: Option<bool>,
|
include_users: Option<bool>,
|
||||||
) -> Result<BulkMessageResponse> {
|
) -> Result<BulkMessageResponse> {
|
||||||
if let Some(true) = include_users {
|
if let Some(true) = include_users {
|
||||||
let user_ids = messages.get_user_ids();
|
let user_ids = messages.get_user_ids();
|
||||||
let users = User::fetch_foreign_users(db, &user_ids).await?;
|
let users = User::fetch_foreign_users(db, &user_ids)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.with_relationship(user))
|
||||||
|
.collect();
|
||||||
|
|
||||||
Ok(match channel {
|
Ok(match channel {
|
||||||
Some(Channel::TextChannel { server, .. })
|
Some(Channel::TextChannel { server, .. })
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let emojis = db.fetch_emoji_by_parent_id(&self.id).await?;
|
||||||
|
|
||||||
EventV1::ServerMemberJoin {
|
EventV1::ServerMemberJoin {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
user: user.id.clone(),
|
user: user.id.clone(),
|
||||||
@@ -217,6 +219,7 @@ impl Server {
|
|||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
server: self.clone(),
|
server: self.clone(),
|
||||||
channels: channels.clone(),
|
channels: channels.clone(),
|
||||||
|
emojis,
|
||||||
}
|
}
|
||||||
.private(user.id.clone())
|
.private(user.id.clone())
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use iso8601_timestamp::Timestamp;
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::client::EventV1,
|
events::client::EventV1,
|
||||||
models::{
|
models::{
|
||||||
server_member::{FieldsMember, MemberCompositeKey, PartialMember},
|
server_member::{FieldsMember, MemberCompositeKey, MemberWithRoles, PartialMember},
|
||||||
Member, Server,
|
Member, Server,
|
||||||
},
|
},
|
||||||
Database, Result,
|
Database, Result,
|
||||||
@@ -82,3 +84,12 @@ impl Member {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MemberWithRoles {
|
||||||
|
pub fn new(server_id: String, user_id: String) -> Self {
|
||||||
|
Self {
|
||||||
|
member: Member::new(server_id, user_id),
|
||||||
|
roles: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -170,7 +170,8 @@ impl User {
|
|||||||
|
|
||||||
/// Check if this user can acquire another server
|
/// Check if this user can acquire another server
|
||||||
pub async fn can_acquire_server(&self, db: &Database) -> Result<bool> {
|
pub async fn can_acquire_server(&self, db: &Database) -> Result<bool> {
|
||||||
// ! FIXME: hardcoded max server count
|
// hardcoded max server count
|
||||||
|
// NB. fixed in new crate
|
||||||
Ok(db.fetch_server_count(&self.id).await? <= 100)
|
Ok(db.fetch_server_count(&self.id).await? <= 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use bson::Document;
|
use bson::Document;
|
||||||
|
|
||||||
|
use super::super::MongoDb;
|
||||||
|
use crate::models::server_member::MemberWithRoles;
|
||||||
use crate::models::server_member::{FieldsMember, Member, MemberCompositeKey, PartialMember};
|
use crate::models::server_member::{FieldsMember, Member, MemberCompositeKey, PartialMember};
|
||||||
use crate::r#impl::mongo::IntoDocumentPath;
|
use crate::r#impl::mongo::IntoDocumentPath;
|
||||||
use crate::{AbstractServerMember, Error, Result};
|
use crate::{AbstractServer, AbstractServerMember, Error, Result};
|
||||||
|
|
||||||
use super::super::MongoDb;
|
|
||||||
|
|
||||||
static COL: &str = "server_members";
|
static COL: &str = "server_members";
|
||||||
|
|
||||||
@@ -21,6 +21,17 @@ impl AbstractServerMember for MongoDb {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn fetch_member_with_roles(&self, server: &str, user: &str) -> Result<MemberWithRoles> {
|
||||||
|
let member = self.fetch_member(server, user).await?;
|
||||||
|
let server_roles = self.fetch_server(server).await?.roles;
|
||||||
|
let roles = member
|
||||||
|
.roles
|
||||||
|
.iter()
|
||||||
|
.filter_map(|id| server_roles.get(id).map(|r| (id.clone(), r.clone())))
|
||||||
|
.collect();
|
||||||
|
Ok(MemberWithRoles { member, roles })
|
||||||
|
}
|
||||||
|
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()> {
|
async fn insert_member(&self, member: &Member) -> Result<()> {
|
||||||
self.insert_one(COL, member).await.map(|_| ())
|
self.insert_one(COL, member).await.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ pub struct Server {
|
|||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
|
|
||||||
/// Channels within this server
|
/// Channels within this server
|
||||||
// ! FIXME: this may be redundant
|
|
||||||
pub channels: Vec<String>,
|
pub channels: Vec<String>,
|
||||||
/// Categories for this server
|
/// Categories for this server
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use iso8601_timestamp::Timestamp;
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::models::attachment::File;
|
use crate::models::attachment::File;
|
||||||
|
|
||||||
|
use super::server::Role;
|
||||||
|
|
||||||
/// Composite primary key consisting of server and user id
|
/// Composite primary key consisting of server and user id
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, Default)]
|
||||||
pub struct MemberCompositeKey {
|
pub struct MemberCompositeKey {
|
||||||
@@ -41,6 +46,30 @@ pub struct Member {
|
|||||||
pub timeout: Option<Timestamp>,
|
pub timeout: Option<Timestamp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Representation of a member of a server on Revolt With Role Data
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
||||||
|
pub struct MemberWithRoles {
|
||||||
|
pub member: Member,
|
||||||
|
pub roles: HashMap<String, Role>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum MemberResponse {
|
||||||
|
Member(Member),
|
||||||
|
MemberWithRoles(MemberWithRoles),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Member> for MemberResponse {
|
||||||
|
fn from(value: Member) -> Self {
|
||||||
|
Self::Member(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<MemberWithRoles> for MemberResponse {
|
||||||
|
fn from(value: MemberWithRoles) -> Self {
|
||||||
|
Self::MemberWithRoles(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Optional fields on server member object
|
/// Optional fields on server member object
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum FieldsMember {
|
pub enum FieldsMember {
|
||||||
|
|||||||
@@ -78,12 +78,6 @@ async fn calculate_permission(data: &mut PermissionCalculator<'_>, db: &crate::D
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! FIXME: add boolean switch for permission for users to globally message a user
|
|
||||||
// maybe an enum?
|
|
||||||
// PrivacyLevel { Private, Friends, Mutual, Public, Global }
|
|
||||||
|
|
||||||
// ! FIXME: add boolean switch for permission for users to mutually DM a user
|
|
||||||
|
|
||||||
if data.flag_has_mutual_connection
|
if data.flag_has_mutual_connection
|
||||||
|| data
|
|| data
|
||||||
.perspective
|
.perspective
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ pub trait AbstractChannel: Sync + Send {
|
|||||||
async fn remove_user_from_group(&self, channel: &str, user: &str) -> Result<()>;
|
async fn remove_user_from_group(&self, channel: &str, user: &str) -> Result<()>;
|
||||||
|
|
||||||
/// Set role permission for a channel
|
/// Set role permission for a channel
|
||||||
/// ! FIXME: may want to refactor to just use normal updates
|
/// TODO: may want to refactor to just use normal updates
|
||||||
async fn set_channel_role_permission(
|
async fn set_channel_role_permission(
|
||||||
&self,
|
&self,
|
||||||
channel: &str,
|
channel: &str,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::models::server_member::{FieldsMember, Member, MemberCompositeKey, PartialMember};
|
use crate::models::server_member::{
|
||||||
|
FieldsMember, Member, MemberCompositeKey, MemberWithRoles, PartialMember,
|
||||||
|
};
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -6,6 +8,8 @@ pub trait AbstractServerMember: Sync + Send {
|
|||||||
/// Fetch a server member by their id
|
/// Fetch a server member by their id
|
||||||
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member>;
|
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member>;
|
||||||
|
|
||||||
|
async fn fetch_member_with_roles(&self, server: &str, user: &str) -> Result<MemberWithRoles>;
|
||||||
|
|
||||||
/// Insert a new server member into the database
|
/// Insert a new server member into the database
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()>;
|
async fn insert_member(&self, member: &Member) -> Result<()>;
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ use async_lock::Semaphore;
|
|||||||
use async_std::task::spawn;
|
use async_std::task::spawn;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use linkify::{LinkFinder, LinkKind};
|
use linkify::{LinkFinder, LinkKind};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashSet, sync::Arc};
|
use std::{collections::HashSet, sync::Arc};
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
|
|
||||||
use crate::{models::attachment::File, Error, Result};
|
use crate::{models::attachment::File, Error, Result};
|
||||||
|
|
||||||
@@ -187,8 +187,6 @@ impl Embed {
|
|||||||
max_embeds: usize,
|
max_embeds: usize,
|
||||||
semaphore: Arc<Semaphore>,
|
semaphore: Arc<Semaphore>,
|
||||||
) -> Result<Vec<Embed>> {
|
) -> Result<Vec<Embed>> {
|
||||||
|
|
||||||
|
|
||||||
// Ignore code blocks.
|
// Ignore code blocks.
|
||||||
let content = RE_CODE.replace_all(&content, "");
|
let content = RE_CODE.replace_all(&content, "");
|
||||||
|
|
||||||
@@ -233,7 +231,6 @@ impl Embed {
|
|||||||
return Err(Error::LabelMe);
|
return Err(Error::LabelMe);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! FIXME: batch request to january?
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
let mut tasks = Vec::new();
|
let mut tasks = Vec::new();
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::models::{
|
use crate::models::server_member::MemberWithRoles;
|
||||||
Channel, Emoji, Invite, Member, Message, Report, Server, ServerBan, User,
|
use crate::models::{Channel, Emoji, Invite, Member, Message, Report, Server, ServerBan, User};
|
||||||
};
|
|
||||||
use crate::{Database, Error, Result};
|
use crate::{Database, Error, Result};
|
||||||
|
|
||||||
/// Reference to some object in the database
|
/// Reference to some object in the database
|
||||||
@@ -66,6 +65,14 @@ impl Ref {
|
|||||||
db.fetch_member(server, &self.id).await
|
db.fetch_member(server, &self.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn as_member_with_roles(
|
||||||
|
&self,
|
||||||
|
db: &Database,
|
||||||
|
server: &str,
|
||||||
|
) -> Result<MemberWithRoles> {
|
||||||
|
db.fetch_member_with_roles(server, &self.id).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch ban from Ref
|
/// Fetch ban from Ref
|
||||||
pub async fn as_ban(&self, db: &Database, server: &str) -> Result<ServerBan> {
|
pub async fn as_ban(&self, db: &Database, server: &str) -> Result<ServerBan> {
|
||||||
db.fetch_ban(server, &self.id).await
|
db.fetch_ban(server, &self.id).await
|
||||||
|
|||||||
@@ -5,3 +5,15 @@ publish:
|
|||||||
cargo publish --package revolt-models
|
cargo publish --package revolt-models
|
||||||
cargo publish --package revolt-presence
|
cargo publish --package revolt-presence
|
||||||
cargo publish --package revolt-database
|
cargo publish --package revolt-database
|
||||||
|
|
||||||
|
patch:
|
||||||
|
cargo release version patch --execute
|
||||||
|
|
||||||
|
minor:
|
||||||
|
cargo release version minor --execute
|
||||||
|
|
||||||
|
major:
|
||||||
|
cargo release version major --execute
|
||||||
|
|
||||||
|
release:
|
||||||
|
scripts/try-tag-and-release.sh
|
||||||
|
|||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
date=$(date +'%Y%m%d')
|
||||||
|
incr=1
|
||||||
|
|
||||||
|
while [ $(git tag -l "$date-$incr") ]; do
|
||||||
|
incr=$((incr+1))
|
||||||
|
done
|
||||||
|
|
||||||
|
tag=$date-$incr
|
||||||
|
echo About to tag and push $tag in 3 seconds...
|
||||||
|
sleep 3s
|
||||||
|
|
||||||
|
git tag $tag
|
||||||
|
git push --atomic origin $(git rev-parse --abbrev-ref HEAD) $tag
|
||||||
Reference in New Issue
Block a user