From ad0dcad49748bf8fd0892b4e8edd23175584d64d Mon Sep 17 00:00:00 2001 From: Zomatree Date: Tue, 9 Apr 2024 02:18:34 +0100 Subject: [PATCH 01/62] initial livekit support fix up code undo changes to compose file add back .env.example --- .gitignore | 2 + Cargo.lock | 3223 +++++++++++------ Cargo.toml | 3 +- crates/bonfire/src/events/impl.rs | 26 +- crates/core/config/Revolt.toml | 1 + crates/core/config/src/lib.rs | 12 +- crates/core/database/src/drivers/mod.rs | 4 +- crates/core/database/src/drivers/mongodb.rs | 1 + crates/core/database/src/drivers/reference.rs | 2 +- crates/core/database/src/events/client.rs | 33 +- .../database/src/models/channels/model.rs | 7 +- .../core/database/src/models/users/model.rs | 10 + crates/core/database/src/util/bridge/v0.rs | 4 + crates/core/models/src/v0/channels.rs | 19 +- crates/core/result/src/lib.rs | 5 +- crates/core/result/src/rocket.rs | 4 +- crates/delta/Cargo.toml | 3 + crates/delta/src/main.rs | 8 + .../delta/src/routes/channels/voice_join.rs | 138 +- crates/delta/src/routes/root.rs | 13 +- crates/delta/src/util/test.rs | 2 +- crates/voice-ingress/.gitignore | 1 + crates/voice-ingress/Cargo.toml | 44 + crates/voice-ingress/Dockerfile | 11 + crates/voice-ingress/src/main.rs | 75 + rust-toolchain.toml | 2 + 26 files changed, 2358 insertions(+), 1295 deletions(-) create mode 100644 crates/voice-ingress/.gitignore create mode 100644 crates/voice-ingress/Cargo.toml create mode 100644 crates/voice-ingress/Dockerfile create mode 100644 crates/voice-ingress/src/main.rs create mode 100644 rust-toolchain.toml diff --git a/.gitignore b/.gitignore index 943bcfead..6293bc546 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ target .vercel .DS_Store + +livekit.yml \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 98fb375cf..10f7a1c2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.17.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -19,30 +19,30 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" -version = "0.4.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "generic-array 0.14.5", + "crypto-common", + "generic-array 0.14.7", ] [[package]] name = "aes" -version = "0.7.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", - "opaque-debug 0.3.0", ] [[package]] name = "aes-gcm" -version = "0.9.4" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -65,20 +65,22 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -90,37 +92,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] -name = "ansi_term" -version = "0.12.1" +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "winapi", + "libc", ] [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-attributes" @@ -128,69 +136,102 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", ] [[package]] name = "async-channel" -version = "1.6.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 5.3.0", + "event-listener-strategy 0.5.1", "futures-core", + "pin-project-lite 0.2.14", ] [[package]] name = "async-executor" -version = "1.4.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +checksum = "5f98c37cf288e302c16ef6c8472aad1e034c6c84ce5ea7b8101c98eb4a802fee" dependencies = [ + "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand", - "futures-lite", - "once_cell", + "fastrand 2.0.2", + "futures-lite 2.3.0", "slab", ] [[package]] name = "async-global-executor" -version = "2.1.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd8b508d585e01084059b60f06ade4cb7415cd2e4084b71dd1cb44e7d3fb9880" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel", + "async-channel 2.2.0", "async-executor", - "async-io", - "async-lock", + "async-io 2.3.2", + "async-lock 3.3.0", "blocking", - "futures-lite", + "futures-lite 2.3.0", "once_cell", "tokio 0.2.25", - "tokio 1.35.1", + "tokio 1.37.0", ] [[package]] name = "async-io" -version = "1.7.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ + "async-lock 2.8.0", + "autocfg 1.2.0", + "cfg-if", "concurrent-queue", - "futures-lite", - "libc", + "futures-lite 1.13.0", "log", - "once_cell", "parking", - "polling", + "polling 2.8.0", + "rustix 0.37.27", "slab", - "socket2 0.4.4", + "socket2 0.4.10", "waker-fn", - "winapi", +] + +[[package]] +name = "async-io" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.6.0", + "rustix 0.38.32", + "slab", + "tracing", + "windows-sys 0.52.0", ] [[package]] @@ -199,35 +240,64 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", + "pin-project-lite 0.2.14", ] [[package]] name = "async-process" -version = "1.4.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ - "async-io", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", "blocking", "cfg-if", - "event-listener", - "futures-lite", - "libc", - "once_cell", - "signal-hook", - "winapi", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.32", + "windows-sys 0.48.0", ] [[package]] name = "async-recursion" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" +checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 2.0.15", + "quote 1.0.35", + "syn 2.0.58", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.3.2", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.32", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", ] [[package]] @@ -237,22 +307,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-attributes", - "async-channel", + "async-channel 1.9.0", "async-global-executor", - "async-io", - "async-lock", + "async-io 1.13.0", + "async-lock 2.8.0", "async-process", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite", + "futures-lite 1.13.0", "gloo-timers", "kv-log-macro", "log", "memchr", "once_cell", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", "pin-utils", "slab", "wasm-bindgen-futures", @@ -269,46 +339,47 @@ dependencies = [ "futures-io", "futures-util", "pin-utils", - "socket2 0.4.4", + "socket2 0.4.10", "trust-dns-resolver", ] [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite 0.2.14", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] name = "async-task" -version = "4.2.0" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.56" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] @@ -321,24 +392,30 @@ dependencies = [ "futures-io", "futures-util", "log", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", "tungstenite", ] [[package]] name = "atomic" -version = "0.5.1" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "atomic" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" dependencies = [ - "autocfg 1.1.0", + "bytemuck", ] [[package]] name = "atomic-waker" -version = "1.0.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atty" @@ -346,7 +423,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -364,7 +441,7 @@ dependencies = [ "chrono", "futures", "handlebars", - "iso8601-timestamp 0.1.10", + "iso8601-timestamp 0.1.11", "lazy_static", "lettre", "log", @@ -397,20 +474,20 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.2.0", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" -version = "0.3.65" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -423,9 +500,9 @@ dependencies = [ [[package]] name = "base16ct" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base32" @@ -435,15 +512,27 @@ checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + +[[package]] +name = "base64" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.21.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" [[package]] name = "base64ct" @@ -485,96 +574,97 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "blake2b_simd" -version = "1.0.0" +name = "bitflags" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] -name = "block-buffer" -version = "0.7.3" +name = "bitvec" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", + "funty", + "radium", + "tap", + "wyz", ] [[package]] -name = "block-buffer" -version = "0.10.2" +name = "blake2b_simd" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ - "generic-array 0.14.5", + "arrayref", + "arrayvec", + "constant_time_eq", ] [[package]] -name = "block-padding" -version = "0.1.5" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "byte-tools", + "generic-array 0.14.7", ] [[package]] name = "blocking" -version = "1.2.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel", + "async-channel 2.2.0", + "async-lock 3.3.0", "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", + "fastrand 2.0.2", + "futures-io", + "futures-lite 2.3.0", + "piper", + "tracing", ] [[package]] name = "bson" -version = "2.2.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60a2c7c80a7850b56df4b8e98e8e4932c34877b8add4f13e8350499cc1e4572" +checksum = "4d43b38e074cc0de2957f10947e376a1d88b9c4dbab340b590800cc1b2e066b2" dependencies = [ - "ahash 0.7.6", - "base64 0.13.0", - "chrono", + "ahash 0.8.11", + "base64 0.13.1", + "bitvec", "hex", - "indexmap", - "lazy_static", + "indexmap 2.2.6", + "js-sys", + "once_cell", "rand 0.8.5", "serde", "serde_bytes", "serde_json", - "uuid 0.8.2", + "time", + "uuid", ] [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] -name = "byte-tools" -version = "0.3.1" +name = "bytemuck" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -584,9 +674,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bytes-utils" @@ -594,16 +684,10 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "either", ] -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - [[package]] name = "cached" version = "0.44.0" @@ -618,7 +702,7 @@ dependencies = [ "instant", "once_cell", "thiserror", - "tokio 1.35.1", + "tokio 1.37.0", ] [[package]] @@ -630,15 +714,15 @@ dependencies = [ "cached_proc_macro_types", "darling 0.14.4", "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] name = "cached_proc_macro_types" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "castaway" @@ -648,11 +732,12 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -663,26 +748,26 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ + "android-tzdata", + "iana-time-zone", "js-sys", - "libc", - "num-integer", "num-traits", - "time 0.1.44", "wasm-bindgen", - "winapi", + "windows-targets 0.52.4", ] [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array 0.14.5", + "crypto-common", + "inout", ] [[package]] @@ -691,49 +776,48 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "coarsetime" -version = "0.1.23" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90d114103adbc625300f346d4d09dfb4ab1c4a8df6868435dd903392ecf4354" +checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d" dependencies = [ "libc", - "once_cell", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasix", "wasm-bindgen", ] [[package]] name = "combine" -version = "4.6.4" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "futures-core", "memchr", - "pin-project-lite 0.2.13", - "tokio 1.35.1", - "tokio-util 0.7.2", + "pin-project-lite 0.2.14", + "tokio 1.37.0", + "tokio-util", ] [[package]] name = "concurrent-queue" -version = "1.2.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ - "cache-padded", + "crossbeam-utils", ] [[package]] name = "config" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" +checksum = "23738e11972c7643e4ec947840fc463b6a571afcd3e735bdfce7d03c7a784aca" dependencies = [ "async-trait", "json5", @@ -744,7 +828,7 @@ dependencies = [ "rust-ini", "serde", "serde_json", - "toml", + "toml 0.5.11", "yaml-rust", ] @@ -756,45 +840,65 @@ checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "convert_case" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "cookie" -version = "0.16.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ "aes-gcm", - "base64 0.13.0", + "base64 0.20.0", "hkdf", "hmac", "percent-encoding", "rand 0.8.5", "sha2", "subtle", - "time 0.3.17", + "time", + "version_check", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", "version_check", ] [[package]] name = "cookie-factory" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -802,15 +906,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -823,43 +927,39 @@ checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" [[package]] name = "crossbeam-queue" -version = "0.3.5" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" -dependencies = [ - "cfg-if", - "lazy_static", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-bigint" -version = "0.4.9" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "generic-array 0.14.5", - "rand_core 0.6.3", + "generic-array 0.14.7", + "rand_core 0.6.4", "subtle", "zeroize", ] [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.7", + "rand_core 0.6.4", "typenum", ] @@ -869,45 +969,35 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" -[[package]] -name = "ctor" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" -dependencies = [ - "quote 1.0.26", - "syn 1.0.107", -] - [[package]] name = "ctr" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ "cipher", ] [[package]] name = "curl" -version = "0.4.44" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", - "socket2 0.4.4", - "winapi", + "socket2 0.5.6", + "windows-sys 0.52.0", ] [[package]] name = "curl-sys" -version = "0.4.65+curl-8.2.1" +version = "0.4.72+curl-8.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "961ba061c9ef2fe34bbd12b807152d96f0badd2bebe7b90ce6c8c8b7572a0986" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" dependencies = [ "cc", "libc", @@ -916,7 +1006,7 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -948,9 +1038,9 @@ dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote 1.0.26", + "quote 1.0.35", "strsim", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -962,9 +1052,9 @@ dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote 1.0.26", + "quote 1.0.35", "strsim", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -974,8 +1064,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] @@ -985,27 +1075,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] name = "dashmap" -version = "5.3.4" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.12.1", + "hashbrown 0.14.3", "lock_api", + "once_cell", "parking_lot_core", ] [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "deadqueue" @@ -1014,7 +1105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16a2561fd313df162315935989dceb8c99db4ee1933358270a57a3cfb8c957f3" dependencies = [ "crossbeam-queue", - "tokio 1.35.1", + "tokio 1.37.0", ] [[package]] @@ -1024,14 +1115,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ "serde", - "uuid 1.4.1", + "uuid", ] [[package]] name = "decancer" -version = "1.6.2" +version = "1.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808127a7de612079ec37bfc1abc48ed77a6015a971a8bd7d4178d79147cbc839" +checksum = "080b09f6adad25c23d8c47c54e52e59b0dc09d079c4b23e0f147dac440359d0d" [[package]] name = "der" @@ -1049,8 +1140,19 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ - "const-oid 0.9.5", - "pem-rfc7468", + "const-oid 0.9.6", + "pem-rfc7468 0.6.0", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -1061,11 +1163,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aed3b3c608dc56cf36c45fe979d04eda51242e6703d8d0bb03426ef7c41db6a" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", "synstructure", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1073,8 +1184,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote 1.0.35", + "rustc_version 0.4.0", + "syn 1.0.109", ] [[package]] @@ -1094,7 +1218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "123c73e7a6e51b05c75fe1a1b2f4e241399ea5740ed810b0e3e6cacd9db5e7b2" dependencies = [ "devise_core", - "quote 1.0.26", + "quote 1.0.35", ] [[package]] @@ -1103,20 +1227,11 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841ef46f4787d9097405cac4e70fb8644fc037b526e8c14054247c0263c400d0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "proc-macro2", - "proc-macro2-diagnostics", - "quote 1.0.26", - "syn 1.0.107", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", + "proc-macro2-diagnostics 0.9.1", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] @@ -1125,8 +1240,8 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.2", - "const-oid 0.9.5", + "block-buffer", + "const-oid 0.9.6", "crypto-common", "subtle", ] @@ -1145,29 +1260,31 @@ checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" [[package]] name = "dyn-clone" -version = "1.0.5" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" -version = "0.14.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.6.1", + "der 0.7.9", + "digest", "elliptic-curve", "rfc6979", - "signature", + "signature 2.2.0", + "spki 0.7.3", ] [[package]] name = "ece" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5463ffecc0677adcd786c4481f73b215714d4757edf2eb37a573c03d00459" +checksum = "c2ea1d2f2cc974957a4e2575d8e5bb494549bab66338d6320c2789abcfff5746" dependencies = [ - "base64 0.13.0", + "base64 0.21.7", "byteorder", "hex", "hkdf", @@ -1181,9 +1298,9 @@ dependencies = [ [[package]] name = "ed25519-compact" -version = "2.0.4" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3d382e8464107391c8706b4c14b087808ecb909f6c15c34114bc42e53a9e4c" +checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190" dependencies = [ "ct-codecs", "getrandom", @@ -1191,27 +1308,26 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "elliptic-curve" -version = "0.12.3" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "der 0.6.1", - "digest 0.10.7", + "digest", "ff", - "generic-array 0.14.5", + "generic-array 0.14.7", "group", "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.3", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -1219,18 +1335,25 @@ dependencies = [ [[package]] name = "email-encoding" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6690291166824e467790ac08ba42f241791567e8337bbf00c5a6e87889629f98" +checksum = "a87260449b06739ee78d6281c68d2a0ff3e3af64a78df63d3a1aeb3c06997c8a" dependencies = [ - "base64 0.13.0", + "base64 0.22.0", + "memchr", ] +[[package]] +name = "email_address" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112" + [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -1241,30 +1364,30 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] name = "enum-iterator" -version = "1.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91a4ec26efacf4aeff80887a175a419493cb6f8b5480d26387eb0bd038976187" +checksum = "9fd242f399be1da0a5354aa462d57b4ab2b4ee0683cc552f7c007d2d12d36e94" dependencies = [ "enum-iterator-derive", ] [[package]] name = "enum-iterator-derive" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "828de45d0ca18782232dfb8f3ea9cc428e8ced380eb26a520baaacfc70de39ce" +checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] @@ -1281,35 +1404,104 @@ dependencies = [ ] [[package]] -name = "erased-serde" -version = "0.3.20" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad132dd8d0d0b546348d7d86cb3191aad14b34e5f979781fc005c80d4ac67ffd" -dependencies = [ +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ "serde", ] +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "event-listener" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] -name = "fake-simd" -version = "0.1.2" +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.14", +] + +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.14", +] + +[[package]] +name = "event-listener" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.14", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite 0.2.14", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +dependencies = [ + "event-listener 5.3.0", + "pin-project-lite 0.2.14", +] [[package]] name = "fastrand" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + [[package]] name = "fcm" version = "0.9.2" @@ -1326,24 +1518,24 @@ dependencies = [ [[package]] name = "ff" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] [[package]] name = "figment" -version = "0.10.6" +version = "0.10.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df" +checksum = "7270677e7067213e04f323b55084586195f18308cd7546cfac9f873344ccceb6" dependencies = [ - "atomic", + "atomic 0.6.0", "pear", "serde", - "toml", + "toml 0.8.12", "uncased", "version_check", ] @@ -1360,6 +1552,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "float-cmp" version = "0.9.0" @@ -1401,27 +1605,26 @@ dependencies = [ [[package]] name = "fred" -version = "8.0.1" +version = "8.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b2a2ac060e3266004c552235c241b481e438e2b1ea75715ea1176914ef2868" +checksum = "4b8e3a1339ed45ad8fde94530c4bdcbd5f371a3c6bd3bf57682923792830aa37" dependencies = [ "arc-swap", "async-trait", - "bytes 1.5.0", + "bytes 1.6.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", + "semver 1.0.22", + "socket2 0.5.6", + "tokio 1.37.0", "tokio-stream", - "tokio-util 0.7.2", + "tokio-util", "url", "urlencoding", ] @@ -1432,11 +1635,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1449,9 +1658,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1459,15 +1668,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1476,25 +1685,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand", + "fastrand 1.9.0", "futures-core", "futures-io", "memchr", "parking", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.0.2", + "futures-core", + "futures-io", + "parking", + "pin-project-lite 0.2.14", +] + [[package]] name = "futures-locks" version = "0.7.1" @@ -1503,43 +1725,43 @@ checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" dependencies = [ "futures-channel", "futures-task", - "tokio 1.35.1", + "tokio 1.37.0", ] [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1548,52 +1770,55 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", "pin-utils", "slab", ] [[package]] name = "generator" -version = "0.7.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1d9279ca822891c1a4dae06d185612cf8fc6acfe5dff37781b41297811b12ee" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" dependencies = [ "cc", "libc", "log", "rustversion", - "winapi", + "windows", ] [[package]] name = "generic-array" -version = "0.12.4" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", + "version_check", + "zeroize", ] [[package]] name = "generic-array" -version = "0.14.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "fe739944a5406424e080edccb6add95685130b9f160d5407c639c7df0c5836b0" dependencies = [ "typenum", - "version_check", ] [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi", + "wasm-bindgen", ] [[package]] @@ -1604,33 +1829,33 @@ checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" dependencies = [ "proc-macro-error", "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] name = "ghash" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ - "opaque-debug 0.3.0", + "opaque-debug", "polyval", ] [[package]] name = "gimli" -version = "0.26.1" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "git2" -version = "0.15.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" +checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libgit2-sys", "log", @@ -1639,15 +1864,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gloo-timers" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ "futures-channel", "futures-core", @@ -1657,39 +1882,39 @@ dependencies = [ [[package]] name = "group" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] [[package]] name = "h2" -version = "0.3.13" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "fnv", "futures-core", "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.2.6", "slab", - "tokio 1.35.1", - "tokio-util 0.7.2", + "tokio 1.37.0", + "tokio-util", "tracing", ] [[package]] name = "handlebars" -version = "4.3.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d113a9853e5accd30f43003560b5563ffbb007e3f325e8b103fa0d0029c6e6df" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" dependencies = [ "log", "pest", @@ -1701,20 +1926,11 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.8", ] [[package]] @@ -1725,19 +1941,25 @@ checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.11", "allocator-api2", ] [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -1748,6 +1970,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "hex" version = "0.4.3" @@ -1756,9 +1984,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] @@ -1769,7 +1997,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -1784,7 +2012,7 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -1793,7 +2021,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4ce1f4656bae589a3fab938f9f09bf58645b7ed01a2c5f8a3c238e01a4ef78a" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -1809,37 +2037,37 @@ dependencies = [ [[package]] name = "http" -version = "0.2.7" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "fnv", "itoa", ] [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "http", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", ] [[package]] name = "httparse" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -1852,11 +2080,11 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.19" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "futures-channel", "futures-core", "futures-util", @@ -1866,9 +2094,9 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.13", - "socket2 0.4.4", - "tokio 1.35.1", + "pin-project-lite 0.2.14", + "socket2 0.5.6", + "tokio 1.37.0", "tower-service", "tracing", "want", @@ -1880,13 +2108,36 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "hyper", "native-tls", - "tokio 1.35.1", + "tokio 1.37.0", "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1904,6 +2155,26 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -1932,17 +2203,36 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg 1.1.0", - "hashbrown 0.12.1", + "autocfg 1.2.0", + "hashbrown 0.12.3", "serde", ] +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + [[package]] name = "inlinable_string" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "instant" version = "0.1.12" @@ -1952,23 +2242,34 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "ipconfig" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.4.4", + "socket2 0.5.6", "widestring", - "winapi", - "winreg 0.7.0", + "windows-sys 0.48.0", + "winreg", ] [[package]] name = "ipnet" -version = "2.5.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "isahc" @@ -1976,19 +2277,19 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" dependencies = [ - "async-channel", + "async-channel 1.9.0", "castaway", "crossbeam-utils", "curl", "curl-sys", "encoding_rs", - "event-listener", - "futures-lite", + "event-listener 2.5.3", + "futures-lite 1.13.0", "http", "log", "mime", "once_cell", - "polling", + "polling 2.8.0", "serde", "serde_json", "slab", @@ -2001,47 +2302,65 @@ dependencies = [ [[package]] name = "iso8601-timestamp" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56480a7792bfa284a4c7d1781f711de58e357dbb669c1b1a50c37c1777c9be92" +checksum = "b088f3296223f11d6bd70998132653b30b6f39e6ab4be055c0bda486fa8d11c7" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.7", "serde", - "time 0.3.17", + "time", ] [[package]] name = "iso8601-timestamp" -version = "0.2.11" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd7663ae5b511b3d13c7d374dbe73b03da3da8e66b83950390173bc7c7325cc" +checksum = "24d4e5d712dd664b11e778d1cfc06c79ba2700d6bc1771e44fb7b6a4656b487d" dependencies = [ - "generic-array 0.14.5", + "generic-array 1.0.0", "schemars", "serde", - "time 0.3.17", + "time", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", ] [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.57" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -2057,11 +2376,24 @@ dependencies = [ "serde", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "ring 0.17.8", + "serde", + "serde_json", +] + [[package]] name = "jwt-simple" -version = "0.11.2" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529a00f2d42d7dc349c994e65917c81bf53225831a65361f6c0454124c550f63" +checksum = "357892bb32159d763abdea50733fadcb9a8e1c319a9aa77592db8555d05af83e" dependencies = [ "anyhow", "binstring", @@ -2078,21 +2410,23 @@ dependencies = [ "rsa", "serde", "serde_json", - "spki", + "spki 0.6.0", "thiserror", "zeroize", ] [[package]] name = "k256" -version = "0.11.6" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", + "once_cell", "sha2", + "signature 2.2.0", ] [[package]] @@ -2115,23 +2449,25 @@ dependencies = [ [[package]] name = "lettre" -version = "0.10.0-rc.6" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f6c70001f7ee6c93b6687a06607c7a38f9a7ae460139a496c23da21e95bc289" +checksum = "76bd09637ae3ec7bd605b8e135e757980b3968430ff2b1a4a94fb7769e50166d" dependencies = [ - "base64 0.13.0", + "base64 0.21.7", "email-encoding", - "fastrand", + "email_address", + "fastrand 1.9.0", "futures-util", "hostname", "httpdate", - "idna 0.2.3", + "idna 0.3.0", "mime", "native-tls", "nom", "once_cell", "quoted_printable", - "regex", + "socket2 0.4.10", + "tokio 1.37.0", ] [[package]] @@ -2142,9 +2478,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libgit2-sys" -version = "0.14.1+1.5.0" +version = "0.14.2+1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a07fb2692bc3593bda59de45a502bb3071659f2c515e28c71e728306b038e17" +checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" dependencies = [ "cc", "libc", @@ -2154,15 +2490,15 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libnghttp2-sys" -version = "0.1.8+1.55.1" +version = "0.1.9+1.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fae956c192dadcdb5dace96db71fa0b827333cce7c7b38dc71446f024d8a340" +checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" dependencies = [ "cc", "libc", @@ -2170,9 +2506,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" dependencies = [ "cc", "libc", @@ -2182,9 +2518,9 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linkify" @@ -2204,23 +2540,72 @@ dependencies = [ "memchr", ] +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "livekit-api" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e20c3fc3de5944ce6b5c8da4084cf828bbae7216671e32e83b767ce61feeb7e0" +dependencies = [ + "base64 0.21.7", + "jsonwebtoken", + "livekit-protocol", + "log", + "parking_lot", + "prost", + "reqwest", + "scopeguard", + "serde", + "serde_json", + "sha2", + "thiserror", + "url", +] + +[[package]] +name = "livekit-protocol" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a1bd23257110be29d024d8d816adff70df18ea1d22ceb1aab6f3ad4aab0d523" +dependencies = [ + "futures-util", + "parking_lot", + "pbjson", + "pbjson-types", + "prost", + "prost-types", + "serde", + "thiserror", + "tokio 1.37.0", +] + [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.2.0", "scopeguard", ] [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" dependencies = [ - "cfg-if", "value-bag", ] @@ -2241,20 +2626,20 @@ dependencies = [ [[package]] name = "lru" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8015d95cb7b2ddd3c0d32ca38283ceb1eea09b4713ee380bceb942d85a244228" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" dependencies = [ - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] name = "lru" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eedb2bdbad7e0634f83989bf596f497b070130daaa398ab22d84c39e266deec5" +checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" dependencies = [ - "hashbrown 0.14.0", + "hashbrown 0.14.3", ] [[package]] @@ -2266,12 +2651,6 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "match_cfg" version = "0.1.0" @@ -2284,29 +2663,30 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "md-5" -version = "0.10.1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ - "digest 0.10.7", + "cfg-if", + "digest", ] [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "metrics" @@ -2314,7 +2694,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e52eb6380b6d2a10eb3434aec0885374490f5b82c8aaf5cd487a183c98be834" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.8", "metrics-macros", ] @@ -2325,15 +2705,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49e30813093f757be5cf21e50389a24dc7dbb22c49f23b7e8f51d69b508a5ffa" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" @@ -2343,29 +2723,29 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] [[package]] name = "mobc" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc79c4a77e312fee9c7bd4b957c12ad1196db73c4a81e5c0b13f02083c4f7f2f" +checksum = "90eb49dc5d193287ff80e72a86f34cfb27aae562299d22fea215e06ea1059dd3" dependencies = [ "async-std", "async-trait", @@ -2376,43 +2756,44 @@ dependencies = [ "log", "metrics", "thiserror", - "tokio 1.35.1", + "tokio 1.37.0", "tracing", "tracing-subscriber", ] [[package]] name = "mobc-redis" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd8e2fd6bf7e35263b86662e663a9496a0352ceddd413b6c33313c36d5068fd" +checksum = "5e90cc63bd181da5c1f826427d26a63ab0ad1927462b2411aaa86e5f6be3d6b1" dependencies = [ "mobc", - "redis 0.22.3", + "redis", ] [[package]] name = "mongodb" -version = "2.2.2" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f3943e379e9dcaaab9dc319c308a8caaf9e7ff083c6838dff740afbba59df7" +checksum = "16928502631c0db72214720aa479c722397fe5aed6bf1c740a3830b3fe4bfcfe" dependencies = [ "async-std", "async-std-resolver", "async-trait", - "base64 0.13.0", - "bitflags", + "base64 0.13.1", + "bitflags 1.3.2", "bson", "chrono", "derivative", + "derive_more", "futures-core", "futures-executor", + "futures-io", "futures-util", "hex", "hmac", "lazy_static", "md-5", - "os_info", "pbkdf2", "percent-encoding", "rand 0.8.5", @@ -2421,30 +2802,30 @@ dependencies = [ "rustls-pemfile", "serde", "serde_with", - "sha-1 0.10.0", + "sha-1", "sha2", - "socket2 0.4.4", + "socket2 0.4.10", "stringprep", "strsim", "take_mut", "thiserror", - "tokio 1.35.1", + "tokio 1.37.0", "tokio-rustls", - "tokio-util 0.7.2", + "tokio-util", "trust-dns-proto", "trust-dns-resolver", "typed-builder", - "uuid 0.8.2", + "uuid", "webpki-roots", ] [[package]] name = "multer" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f8f35e687561d5c1667590911e6698a8cb714a134a7505718a182e7bc9d3836" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "encoding_rs", "futures-util", "http", @@ -2452,12 +2833,18 @@ dependencies = [ "log", "memchr", "mime", - "spin 0.9.3", - "tokio 1.35.1", - "tokio-util 0.6.10", + "spin 0.9.8", + "tokio 1.37.0", + "tokio-util", "version_check", ] +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + [[package]] name = "nanoid" version = "0.4.0" @@ -2469,9 +2856,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -2487,9 +2874,9 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -2497,10 +2884,20 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.4.0" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ + "overload", "winapi", ] @@ -2521,54 +2918,59 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg 1.1.0", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.2.0", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.2.0", "libm", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "num_enum_derive 0.5.7", + "num_enum_derive 0.5.11", ] [[package]] @@ -2582,14 +2984,14 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] @@ -2600,44 +3002,38 @@ checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.26", - "syn 2.0.15", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] name = "object" -version = "0.28.4" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "opaque-debug" -version = "0.2.3" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.40" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -2648,13 +3044,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] @@ -2665,11 +3061,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.73" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ - "autocfg 1.1.0", "cc", "libc", "pkg-config", @@ -2683,47 +3078,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ "dlv-list", - "hashbrown 0.12.1", + "hashbrown 0.12.3", ] [[package]] name = "os_info" -version = "3.7.0" +version = "3.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", "serde", - "winapi", + "windows-sys 0.52.0", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "p256" -version = "0.11.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa", "elliptic-curve", + "primeorder", "sha2", ] [[package]] name = "p384" -version = "0.11.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" dependencies = [ "ecdsa", "elliptic-curve", + "primeorder", "sha2", ] [[package]] name = "parking" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -2737,22 +3140,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.36.1", + "windows-targets 0.48.5", ] [[package]] name = "paste" -version = "1.0.7" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pathdiff" @@ -2760,36 +3163,73 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +[[package]] +name = "pbjson" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" +dependencies = [ + "base64 0.21.7", + "serde", +] + +[[package]] +name = "pbjson-build" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" +dependencies = [ + "heck 0.4.1", + "itertools 0.11.0", + "prost", + "prost-types", +] + +[[package]] +name = "pbjson-types" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" +dependencies = [ + "bytes 1.6.0", + "chrono", + "pbjson", + "pbjson-build", + "prost", + "prost-build", + "serde", +] + [[package]] name = "pbkdf2" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] name = "pear" -version = "0.2.3" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" dependencies = [ "inlinable_string", "pear_codegen", - "yansi", + "yansi 1.0.1", ] [[package]] name = "pear_codegen" -version = "0.2.3" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" dependencies = [ "proc-macro2", - "proc-macro2-diagnostics", - "quote 1.0.26", - "syn 1.0.107", + "proc-macro2-diagnostics 0.10.1", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] @@ -2798,7 +3238,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "once_cell", "regex", ] @@ -2809,7 +3249,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", ] [[package]] @@ -2821,6 +3261,15 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2829,18 +3278,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.1.3" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ + "memchr", + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.1.0" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" dependencies = [ "pest", "pest_generator", @@ -2848,46 +3299,56 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.3" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" dependencies = [ "pest", "pest_meta", "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] name = "pest_meta" -version = "2.1.3" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" dependencies = [ - "maplit", + "once_cell", "pest", - "sha-1 0.8.2", + "sha2", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.2.6", ] [[package]] name = "pin-project" -version = "1.0.10" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.10" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] @@ -2898,9 +3359,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2908,6 +3369,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.2", + "futures-io", +] + [[package]] name = "pkcs1" version = "0.4.1" @@ -2915,8 +3387,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719" dependencies = [ "der 0.6.1", - "pkcs8", - "spki", + "pkcs8 0.9.0", + "spki 0.6.0", "zeroize", ] @@ -2927,45 +3399,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ "der 0.6.1", - "spki", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.9", + "spki 0.7.3", ] [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "polling" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg 1.2.0", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite 0.2.14", + "windows-sys 0.48.0", +] [[package]] name = "polling" -version = "2.2.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" dependencies = [ "cfg-if", - "libc", - "log", - "wepoll-ffi", - "winapi", + "concurrent-queue", + "hermit-abi 0.3.9", + "pin-project-lite 0.2.14", + "rustix 0.38.32", + "tracing", + "windows-sys 0.52.0", ] [[package]] name = "polyval" -version = "0.5.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug", "universal-hash", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pretty_env_logger" @@ -2977,14 +3483,33 @@ dependencies = [ "log", ] +[[package]] +name = "prettyplease" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +dependencies = [ + "proc-macro2", + "syn 2.0.58", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" -version = "1.1.3" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ - "thiserror", - "toml", + "once_cell", + "toml_edit 0.19.15", ] [[package]] @@ -2995,8 +3520,8 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", "version_check", ] @@ -3007,15 +3532,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", - "quote 1.0.26", + "quote 1.0.35", "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -3027,10 +3552,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", + "version_check", + "yansi 0.5.1", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote 1.0.35", + "syn 2.0.58", "version_check", - "yansi", + "yansi 1.0.1", ] [[package]] @@ -3047,6 +3585,59 @@ dependencies = [ "thiserror", ] +[[package]] +name = "prost" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +dependencies = [ + "bytes 1.6.0", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" +dependencies = [ + "bytes 1.6.0", + "heck 0.5.0", + "itertools 0.12.1", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.58", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote 1.0.35", + "syn 2.0.58", +] + +[[package]] +name = "prost-types" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +dependencies = [ + "prost", +] + [[package]] name = "querystring" version = "1.1.0" @@ -3067,18 +3658,24 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.26" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "quoted_printable" -version = "0.4.5" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49" + +[[package]] +name = "radium" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fee2dce59f7a43418e3382c766554c614e06a552d53a8f07ef499ea4b332c0f" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" @@ -3107,7 +3704,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3127,7 +3724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3147,9 +3744,9 @@ checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] @@ -3228,41 +3825,21 @@ dependencies = [ [[package]] name = "redis" -version = "0.22.3" -source = "git+https://github.com/revoltchat/redis-rs?rev=1a41faf356fd21aebba71cea7eb7eb2653e5f0ef#1a41faf356fd21aebba71cea7eb7eb2653e5f0ef" +version = "0.23.1" +source = "git+https://github.com/revoltchat/redis-rs?rev=f8ca28ab85da59d2ccde526b4d2fb390eff5a5f9#f8ca28ab85da59d2ccde526b4d2fb390eff5a5f9" dependencies = [ "async-std", "async-trait", - "bytes 1.5.0", + "bytes 1.6.0", "combine", "futures-util", "itoa", "percent-encoding", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", "ryu", "sha1_smol", - "tokio 1.35.1", - "tokio-util 0.7.2", - "url", -] - -[[package]] -name = "redis" -version = "0.23.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f49cdc0bb3f412bf8e7d1bd90fe1d9eb10bc5c399ba90973c14662a27b3f8ba" -dependencies = [ - "async-std", - "async-trait", - "bytes 1.5.0", - "combine", - "futures-util", - "itoa", - "percent-encoding", - "pin-project-lite 0.2.13", - "ryu", - "tokio 1.35.1", - "tokio-util 0.7.2", + "tokio 1.37.0", + "tokio-util", "url", ] @@ -3276,7 +3853,7 @@ dependencies = [ "lazy_static", "mobc", "mobc-redis", - "redis 0.23.3", + "redis", "rmp-serde", "serde", "serde_json", @@ -3288,7 +3865,7 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c31deddf734dc0a39d3112e73490e88b61a05e83e074d211f348404cee4d2c6" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "bytes-utils", "cookie-factory", "crc16", @@ -3298,42 +3875,43 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "ref-cast" -version = "1.0.7" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685d58625b6c2b83e4cc88a27c4bf65adb7b6b16dbdc413e515c9405b47432ab" +checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.7" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a043824e29c94169374ac5183ac0ed43f5724dc4556b19568007486bd840fa1f" +checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] name = "regex" -version = "1.5.6" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -3342,32 +3920,40 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", ] [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.10" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.13.0", - "bytes 1.5.0", + "base64 0.21.7", + "bytes 1.6.0", "encoding_rs", "futures-core", "futures-util", @@ -3378,22 +3964,26 @@ dependencies = [ "hyper-tls", "ipnet", "js-sys", - "lazy_static", "log", "mime", "native-tls", + "once_cell", "percent-encoding", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", - "tokio 1.35.1", + "sync_wrapper", + "system-configuration", + "tokio 1.37.0", "tokio-native-tls", + "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.10.1", + "winreg", ] [[package]] @@ -3416,7 +4006,7 @@ dependencies = [ "fred", "futures", "log", - "lru 0.7.6", + "lru 0.7.8", "once_cell", "querystring", "redis-kiss", @@ -3453,23 +4043,23 @@ dependencies = [ name = "revolt-database" version = "0.7.1" dependencies = [ - "async-lock", + "async-lock 2.8.0", "async-recursion", "async-std", "async-trait", "authifier", - "base64 0.21.3", + "base64 0.21.7", "bson", "deadqueue", "decancer", "fcm", "futures", - "indexmap", + "indexmap 1.9.3", "isahc", - "iso8601-timestamp 0.2.11", + "iso8601-timestamp 0.2.17", "linkify 0.8.1", "log", - "lru 0.11.0", + "lru 0.11.1", "mongodb", "nanoid", "once_cell", @@ -3488,10 +4078,10 @@ dependencies = [ "schemars", "serde", "serde_json", - "ulid 1.0.0", + "ulid 1.1.2", "unicode-segmentation", "url-escape", - "validator 0.16.0", + "validator 0.16.1", "web-push", ] @@ -3499,7 +4089,7 @@ dependencies = [ name = "revolt-delta" version = "0.7.1" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-std", "authifier", "bitfield", @@ -3509,13 +4099,14 @@ dependencies = [ "env_logger", "futures", "impl_ops", - "iso8601-timestamp 0.2.11", + "iso8601-timestamp 0.2.17", "lettre", "linkify 0.6.0", + "livekit-api", "log", - "lru 0.7.6", + "lru 0.7.8", "nanoid", - "num_enum 0.5.7", + "num_enum 0.5.11", "once_cell", "rand 0.8.5", "redis-kiss", @@ -3537,7 +4128,7 @@ dependencies = [ "serde_json", "ulid 0.4.1", "url", - "validator 0.16.0", + "validator 0.16.1", "vergen", ] @@ -3545,8 +4136,8 @@ dependencies = [ name = "revolt-models" version = "0.7.1" dependencies = [ - "indexmap", - "iso8601-timestamp 0.2.11", + "indexmap 1.9.3", + "iso8601-timestamp 0.2.17", "once_cell", "regex", "revolt-config", @@ -3555,7 +4146,7 @@ dependencies = [ "rocket", "schemars", "serde", - "validator 0.16.0", + "validator 0.16.1", ] [[package]] @@ -3596,6 +4187,32 @@ dependencies = [ "serde_json", ] +[[package]] +name = "revolt-voice-ingress" +version = "0.7.1" +dependencies = [ + "async-std", + "futures", + "livekit-api", + "livekit-protocol", + "log", + "lru 0.7.8", + "redis-kiss", + "revolt-config", + "revolt-database", + "revolt-models", + "revolt-permissions", + "revolt-presence", + "revolt-result", + "rmp-serde", + "rocket", + "rocket_empty", + "sentry", + "serde", + "serde_json", + "ulid 0.5.0", +] + [[package]] name = "revolt_okapi" version = "0.9.1" @@ -3642,20 +4259,19 @@ checksum = "cc6620569d8ac8f0a1690fcca13f488503807a60e96ebf729749b59aca1dbef9" dependencies = [ "darling 0.13.4", "proc-macro2", - "quote 1.0.26", - "rocket_http", - "syn 1.0.107", + "quote 1.0.35", + "rocket_http 0.5.0", + "syn 1.0.109", ] [[package]] name = "rfc6979" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "crypto-bigint", "hmac", - "zeroize", + "subtle", ] [[package]] @@ -3668,16 +4284,31 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + [[package]] name = "rmp" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" dependencies = [ "byteorder", "num-traits", @@ -3686,9 +4317,9 @@ dependencies = [ [[package]] name = "rmp-serde" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25786b0d276110195fa3d6f3f31299900cf71dfbd6c28450f3f58a0e7f7a347e" +checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" dependencies = [ "byteorder", "rmp", @@ -3698,40 +4329,39 @@ dependencies = [ [[package]] name = "rocket" version = "0.5.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ead083fce4a405feb349cf09abdf64471c6077f14e0ce59364aa90d4b99317" +source = "git+https://github.com/rwf2/Rocket/?rev=4dcd928#4dcd92837f3a4626a28d4b25da8e6e560878d004" dependencies = [ "async-stream", "async-trait", - "atomic", + "atomic 0.5.3", "atty", "binascii", - "bytes 1.5.0", + "bytes 1.6.0", "either", "figment", "futures", - "indexmap", + "indexmap 1.9.3", "log", "memchr", "multer", "num_cpus", "parking_lot", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", "rand 0.8.5", "ref-cast", "rocket_codegen", - "rocket_http", + "rocket_http 0.5.0-rc.2", "serde", "serde_json", - "state", + "state 0.5.3", "tempfile", - "time 0.3.17", - "tokio 1.35.1", + "time", + "tokio 1.37.0", "tokio-stream", - "tokio-util 0.7.2", + "tokio-util", "ubyte", "version_check", - "yansi", + "yansi 0.5.1", ] [[package]] @@ -3741,7 +4371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f89a12311f60e9288833fc3ce6029bce5d5c61870ceef74d4a50668a8b520ad" dependencies = [ "authifier", - "iso8601-timestamp 0.1.10", + "iso8601-timestamp 0.1.11", "revolt_okapi", "revolt_rocket_okapi", "rocket", @@ -3753,17 +4383,16 @@ dependencies = [ [[package]] name = "rocket_codegen" version = "0.5.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47" +source = "git+https://github.com/rwf2/Rocket/?rev=4dcd928#4dcd92837f3a4626a28d4b25da8e6e560878d004" dependencies = [ "devise", "glob", - "indexmap", + "indexmap 1.9.3", "proc-macro2", - "quote 1.0.26", - "rocket_http", - "syn 1.0.107", - "unicode-xid 0.2.3", + "quote 1.0.35", + "rocket_http 0.5.0-rc.2", + "syn 1.0.109", + "unicode-xid 0.2.4", ] [[package]] @@ -3796,27 +4425,52 @@ dependencies = [ [[package]] name = "rocket_http" version = "0.5.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2" +source = "git+https://github.com/rwf2/Rocket/?rev=4dcd928#4dcd92837f3a4626a28d4b25da8e6e560878d004" dependencies = [ - "cookie", + "cookie 0.16.2", "either", "futures", "http", "hyper", - "indexmap", + "indexmap 1.9.3", "log", "memchr", "pear", "percent-encoding", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", "ref-cast", "serde", "smallvec", "stable-pattern", - "state", - "time 0.3.17", - "tokio 1.35.1", + "state 0.5.3", + "time", + "tokio 1.37.0", + "uncased", +] + +[[package]] +name = "rocket_http" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e" +dependencies = [ + "cookie 0.18.1", + "either", + "futures", + "http", + "hyper", + "indexmap 2.2.6", + "log", + "memchr", + "pear", + "percent-encoding", + "pin-project-lite 0.2.14", + "ref-cast", + "smallvec", + "stable-pattern", + "state 0.6.0", + "time", + "tokio 1.37.0", "uncased", ] @@ -3836,8 +4490,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" dependencies = [ - "base64 0.13.0", - "bitflags", + "base64 0.13.1", + "bitflags 1.3.2", "serde", ] @@ -3848,15 +4502,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "094052d5470cbcef561cb848a7209968c9f12dfa6d668f4bca048ac5de51099c" dependencies = [ "byteorder", - "digest 0.10.7", + "digest", "num-bigint-dig", "num-integer", "num-iter", "num-traits", "pkcs1", - "pkcs8", - "rand_core 0.6.3", - "signature", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "signature 1.6.4", "smallvec", "subtle", "zeroize", @@ -3864,11 +4518,11 @@ dependencies = [ [[package]] name = "rust-argon2" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50162d19404029c1ceca6f6980fe40d45c8b369f6f44446fa14bb39573b5bb9" +checksum = "a5885493fdf0be6cdff808d1533ce878d21cfa49c7086fa00c66355cd9141bfc" dependencies = [ - "base64 0.13.0", + "base64 0.21.7", "blake2b_simd", "constant_time_eq", "crossbeam-utils", @@ -3886,9 +4540,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc_version" @@ -3905,7 +4559,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.9", + "semver 1.0.22", ] [[package]] @@ -3918,57 +4572,83 @@ dependencies = [ "semver 0.9.0", ] +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" -version = "0.20.6" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", - "ring", + "ring 0.16.20", "sct", "webpki", ] [[package]] name = "rustls-pemfile" -version = "0.3.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.13.0", + "base64 0.21.7", ] [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys 0.52.0", ] [[package]] name = "schemars" -version = "0.8.10" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" dependencies = [ "dyn-clone", - "indexmap", + "indexmap 1.9.3", "schemars_derive", "serde", "serde_json", @@ -3976,48 +4656,48 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.10" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" dependencies = [ "proc-macro2", - "quote 1.0.26", + "quote 1.0.35", "serde_derive_internals", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "scoped-tls" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] name = "sec1" -version = "0.3.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", - "der 0.6.1", - "generic-array 0.14.5", - "pkcs8", + "der 0.7.9", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -4035,11 +4715,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.6.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -4048,9 +4728,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -4067,9 +4747,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.9" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "semver-parser" @@ -4079,9 +4759,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "sentry" -version = "0.31.5" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b0ad16faa5d12372f914ed40d00bda21a6d1bdcc99264c5e5e1c9495cf3654" +checksum = "6ce4b57f1b521f674df7a1d200be8ff5d74e3712020ee25b553146657b5377d5" dependencies = [ "httpdate", "native-tls", @@ -4092,15 +4772,15 @@ dependencies = [ "sentry-debug-images", "sentry-panic", "sentry-tracing", - "tokio 1.35.1", + "tokio 1.37.0", "ureq", ] [[package]] name = "sentry-backtrace" -version = "0.31.5" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f2ee8f147bb5f22ac59b5c35754a759b9a6f6722402e2a14750b2a63fc59bd" +checksum = "58cc8d4e04a73de8f718dc703943666d03f25d3e9e4d0fb271ca0b8c76dfa00e" dependencies = [ "backtrace", "once_cell", @@ -4110,9 +4790,9 @@ dependencies = [ [[package]] name = "sentry-contexts" -version = "0.31.5" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcd133362c745151eeba0ac61e3ba8350f034e9fe7509877d08059fe1d7720c6" +checksum = "6436c1bad22cdeb02179ea8ef116ffc217797c028927def303bc593d9320c0d1" dependencies = [ "hostname", "libc", @@ -4124,9 +4804,9 @@ dependencies = [ [[package]] name = "sentry-core" -version = "0.31.5" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7163491708804a74446642ff2c80b3acd668d4b9e9f497f85621f3d250fd012b" +checksum = "901f761681f97db3db836ef9e094acdd8756c40215326c194201941947164ef1" dependencies = [ "once_cell", "rand 0.8.5", @@ -4137,9 +4817,9 @@ dependencies = [ [[package]] name = "sentry-debug-images" -version = "0.31.5" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a5003d7ff08aa3b2b76994080b183e8cfa06c083e280737c9cee02ca1c70f5e" +checksum = "afdb263e73d22f39946f6022ed455b7561b22ff5553aca9be3c6a047fa39c328" dependencies = [ "findshlibs", "once_cell", @@ -4148,9 +4828,9 @@ dependencies = [ [[package]] name = "sentry-panic" -version = "0.31.5" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4dfe8371c9b2e126a8b64f6fefa54cef716ff2a50e63b5558a48b899265bccd" +checksum = "74fbf1c163f8b6a9d05912e1b272afa27c652e8b47ea60cb9a57ad5e481eea99" dependencies = [ "sentry-backtrace", "sentry-core", @@ -4158,9 +4838,9 @@ dependencies = [ [[package]] name = "sentry-tracing" -version = "0.31.5" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca8b88978677a27ee1a91beafe4052306c474c06f582321fde72d2e2cc2f7f" +checksum = "82eabcab0a047040befd44599a1da73d3adb228ff53b5ed9795ae04535577704" dependencies = [ "sentry-backtrace", "sentry-core", @@ -4170,48 +4850,48 @@ dependencies = [ [[package]] name = "sentry-types" -version = "0.31.5" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7a88e0c1922d19b3efee12a8215f6a8a806e442e665ada71cc222cab72985f" +checksum = "da956cca56e0101998c8688bc65ce1a96f00673a0e58e663664023d4c7911e82" dependencies = [ "debugid", - "getrandom", "hex", + "rand 0.8.5", "serde", "serde_json", "thiserror", - "time 0.3.17", + "time", "url", - "uuid 1.4.1", + "uuid", ] [[package]] name = "serde" -version = "1.0.160" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.6" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 2.0.15", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] @@ -4221,22 +4901,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ - "indexmap", + "indexmap 2.2.6", "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4267,31 +4956,30 @@ checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] name = "sha-1" -version = "0.8.2" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "sha-1" -version = "0.10.0" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -4302,58 +4990,61 @@ checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] -name = "signal-hook" -version = "0.3.14" +name = "signal-hook-registry" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", - "signal-hook-registry", ] [[package]] -name = "signal-hook-registry" -version = "1.4.0" +name = "signature" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "libc", + "digest", + "rand_core 0.6.4", ] [[package]] name = "signature" -version = "1.6.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest 0.10.7", - "rand_core 0.6.3", + "digest", + "rand_core 0.6.4", ] [[package]] name = "slab" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg 1.2.0", +] [[package]] name = "sluice" @@ -4361,22 +5052,22 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" dependencies = [ - "async-channel", + "async-channel 1.9.0", "futures-core", "futures-io", ] [[package]] name = "smallvec" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -4384,12 +5075,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4400,9 +5091,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.3" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" @@ -4411,7 +5102,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der 0.6.1", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.9", ] [[package]] @@ -4432,12 +5133,22 @@ dependencies = [ "loom", ] +[[package]] +name = "state" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + [[package]] name = "stringprep" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" dependencies = [ + "finl_unicode", "unicode-bidi", "unicode-normalization", ] @@ -4450,9 +5161,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -4467,26 +5178,32 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote 1.0.26", + "quote 1.0.35", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.15" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", - "quote 1.0.26", + "quote 1.0.35", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synom" version = "0.11.3" @@ -4503,16 +5220,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", - "unicode-xid 0.2.3", + "quote 1.0.35", + "syn 1.0.109", + "unicode-xid 0.2.4", ] [[package]] name = "sysinfo" -version = "0.27.6" +version = "0.27.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c215311383d25d03753375c53ab9fad8fc0cf46953c409211e065edeabf577ee" +checksum = "a902e9050fca0a5d6877550b769abd2bd1ce8c04634b941dbe2809735e1a1e33" dependencies = [ "cfg-if", "core-foundation-sys", @@ -4522,82 +5239,100 @@ dependencies = [ "winapi", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "take_mut" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" -version = "3.3.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "fastrand 2.0.2", + "rustix 0.38.32", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ + "cfg-if", "once_cell", ] [[package]] name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.17" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ + "deranged", "itoa", + "num-conv", + "powerfmt", "serde", "time-core", "time-macros", @@ -4605,16 +5340,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -4629,9 +5365,9 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" @@ -4646,19 +5382,19 @@ dependencies = [ [[package]] name = "tokio" -version = "1.35.1" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", - "bytes 1.5.0", + "bytes 1.6.0", "libc", "mio", "num_cpus", "parking_lot", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.6", "tokio-macros", "windows-sys 0.48.0", ] @@ -4670,18 +5406,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 2.0.15", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", - "tokio 1.35.1", + "tokio 1.37.0", ] [[package]] @@ -4691,108 +5427,138 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ "rustls", - "tokio 1.35.1", + "tokio 1.37.0", "webpki", ] [[package]] name = "tokio-stream" -version = "0.1.8" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", - "pin-project-lite 0.2.13", - "tokio 1.35.1", + "pin-project-lite 0.2.14", + "tokio 1.37.0", ] [[package]] name = "tokio-util" -version = "0.6.10" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "futures-core", + "futures-io", "futures-sink", - "log", - "pin-project-lite 0.2.13", - "tokio 1.35.1", + "pin-project-lite 0.2.14", + "tokio 1.37.0", + "tracing", ] [[package]] -name = "tokio-util" -version = "0.7.2" +name = "toml" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "bytes 1.5.0", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite 0.2.13", - "tokio 1.35.1", - "tracing", + "serde", ] [[package]] name = "toml" -version = "0.5.9" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.9", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +dependencies = [ + "indexmap 2.2.6", "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.5", ] [[package]] name = "totp-lite" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc496875d9c8fe9a0ce19e3ee8e8808c60376831a439543f0aac71c9dd129fa" +checksum = "f8e43134db17199f7f721803383ac5854edd0d3d523cc34dba321d6acfbe76c3" dependencies = [ - "digest 0.10.7", + "digest", "hmac", - "sha-1 0.10.0", + "sha1", "sha2", ] [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", - "pin-project-lite 0.2.13", + "pin-project-lite 0.2.14", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.21" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", ] [[package]] name = "tracing-core" -version = "0.1.26" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "lazy_static", + "once_cell", "valuable", ] @@ -4808,24 +5574,24 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" -version = "0.3.11" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ - "ansi_term", - "lazy_static", "matchers", + "nu-ansi-term", + "once_cell", "regex", "sharded-slab", "smallvec", @@ -4856,7 +5622,7 @@ dependencies = [ "smallvec", "thiserror", "tinyvec", - "tokio 1.35.1", + "tokio 1.37.0", "url", ] @@ -4876,30 +5642,30 @@ dependencies = [ "resolv-conf", "smallvec", "thiserror", - "tokio 1.35.1", + "tokio 1.37.0", "trust-dns-proto", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "byteorder", - "bytes 1.5.0", + "bytes 1.6.0", "http", "httparse", "log", "rand 0.8.5", - "sha-1 0.10.0", + "sha-1", "thiserror", "url", "utf-8", @@ -4912,30 +5678,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] name = "typenum" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ubyte" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a58e29f263341a29bb79e14ad7fda5f63b1c7e48929bad4c685d7876b1d04e94" +checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" dependencies = [ "serde", ] [[package]] name = "ucd-trie" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "ulid" @@ -4960,11 +5726,13 @@ dependencies = [ [[package]] name = "ulid" -version = "1.0.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a3aaa69b04e5b66cc27309710a569ea23593612387d67daaf102e73aa974fd" +checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259" dependencies = [ + "getrandom", "rand 0.8.5", + "web-time", ] [[package]] @@ -4978,9 +5746,9 @@ dependencies = [ [[package]] name = "uncased" -version = "0.9.7" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b01702b0fd0b3fadcf98e098780badda8742d4f4a7676615cad90e8ac73622" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" dependencies = [ "serde", "version_check", @@ -4988,9 +5756,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] @@ -5013,24 +5781,24 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-xid" @@ -5040,17 +5808,17 @@ checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "generic-array 0.14.5", + "crypto-common", "subtle", ] @@ -5060,13 +5828,19 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "ureq" -version = "2.7.1" +version = "2.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9" +checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" dependencies = [ - "base64 0.21.3", + "base64 0.21.7", "log", "native-tls", "once_cell", @@ -5108,19 +5882,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "uuid" -version = "1.4.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", "serde", @@ -5143,11 +5907,11 @@ dependencies = [ [[package]] name = "validator" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ad5bf234c7d3ad1042e5252b7eddb2c4669ee23f32c7dd0e9b7705f07ef591" +checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" dependencies = [ - "idna 0.2.3", + "idna 0.4.0", "lazy_static", "regex", "serde", @@ -5167,9 +5931,9 @@ dependencies = [ "lazy_static", "proc-macro-error", "proc-macro2", - "quote 1.0.26", + "quote 1.0.35", "regex", - "syn 1.0.107", + "syn 1.0.109", "validator_types", ] @@ -5180,7 +5944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" dependencies = [ "proc-macro2", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -5191,13 +5955,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.0.0-alpha.9" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" -dependencies = [ - "ctor", - "version_check", -] +checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" [[package]] name = "vcpkg" @@ -5207,9 +5967,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "7.5.0" +version = "7.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571b69f690c855821462709b6f41d42ceccc316fbd17b60bd06d06928cfe6a99" +checksum = "f21b881cd6636ece9735721cf03c1fe1e774fe258683d084bb2812ab67435749" dependencies = [ "anyhow", "cfg-if", @@ -5220,7 +5980,7 @@ dependencies = [ "rustversion", "sysinfo", "thiserror", - "time 0.3.17", + "time", ] [[package]] @@ -5231,37 +5991,39 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "waker-fn" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] [[package]] name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "wasix" +version = "0.12.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" +dependencies = [ + "wasi", +] [[package]] name = "wasm-bindgen" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -5269,24 +6031,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.30" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -5296,44 +6058,44 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote 1.0.26", + "quote 1.0.35", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", - "quote 1.0.26", - "syn 1.0.107", + "quote 1.0.35", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-push" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdf799b4be6f04bfee94807548718de568834c9ea020e73b59af3dac66d72c" +checksum = "7b5c5c6deab45e8820b77c9c8ae168f1ded4595767c746584bb67b9100f2b71d" dependencies = [ "async-trait", - "base64 0.13.0", + "base64 0.13.1", "chrono", "ece", - "futures-lite", + "futures-lite 1.13.0", "http", "isahc", "jwt-simple", @@ -5347,47 +6109,48 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "webpki" -version = "0.22.0" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ - "ring", - "untrusted", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "webpki-roots" -version = "0.22.3" +name = "webpki" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "webpki", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] -name = "wepoll-ffi" -version = "0.1.2" +name = "webpki-roots" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ - "cc", + "webpki", ] [[package]] name = "widestring" -version = "0.5.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -5407,9 +6170,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -5421,16 +6184,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.36.1" +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows-targets 0.52.4", ] [[package]] @@ -5439,7 +6207,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", ] [[package]] @@ -5448,15 +6225,30 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.48.5", "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_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -5464,10 +6256,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" +name = "windows_aarch64_gnullvm" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -5476,10 +6268,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] -name = "windows_i686_gnu" -version = "0.36.1" +name = "windows_aarch64_msvc" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -5488,10 +6280,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] -name = "windows_i686_msvc" -version = "0.36.1" +name = "windows_i686_gnu" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -5500,10 +6292,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" +name = "windows_i686_msvc" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -5511,6 +6303,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -5518,10 +6316,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" +name = "windows_x86_64_gnullvm" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -5530,21 +6328,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "winreg" -version = "0.7.0" +name = "windows_x86_64_msvc" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ - "winapi", + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", ] [[package]] @@ -5562,13 +6385,39 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote 1.0.35", + "syn 2.0.58", +] + [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" [[patch.unused]] name = "redis" -version = "0.23.1" -source = "git+https://github.com/revoltchat/redis-rs?rev=f8ca28ab85da59d2ccde526b4d2fb390eff5a5f9#f8ca28ab85da59d2ccde526b4d2fb390eff5a5f9" +version = "0.22.3" +source = "git+https://github.com/revoltchat/redis-rs?rev=1a41faf356fd21aebba71cea7eb7eb2653e5f0ef#1a41faf356fd21aebba71cea7eb7eb2653e5f0ef" diff --git a/Cargo.toml b/Cargo.toml index b55327166..03eb89cb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["crates/delta", "crates/bonfire", "crates/core/*"] +members = ["crates/delta", "crates/bonfire", "crates/voice-ingress", "crates/core/*"] [patch.crates-io] # mobc-redis = { git = "https://github.com/insertish/mobc", rev = "8b880bb59f2ba80b4c7bc40c649c113d8857a186" } @@ -8,3 +8,4 @@ redis22 = { package = "redis", version = "0.22.3", git = "https://github.com/rev redis23 = { package = "redis", version = "0.23.1", git = "https://github.com/revoltchat/redis-rs", rev = "f8ca28ab85da59d2ccde526b4d2fb390eff5a5f9" } # authifier = { package = "authifier", version = "1.0.8", path = "../authifier/crates/authifier" } # rocket_authifier = { package = "rocket_authifier", version = "1.0.8", path = "../authifier/crates/rocket_authifier" } +rocket = { git = "https://github.com/rwf2/Rocket/", rev = "4dcd928" } \ No newline at end of file diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index 4880edf0c..d5ed027db 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use revolt_database::{ - events::client::EventV1, util::permissions::DatabasePermissionQuery, Channel, Database, Member, + events::client::{ChannelVoiceState, EventV1, ReadyServer, UserVoiceState}, util::permissions::DatabasePermissionQuery, Channel, Database, Member, MemberCompositeKey, Presence, RelationshipStatus, }; use revolt_models::v0; @@ -9,6 +9,8 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_presence::filter_online; use revolt_result::Result; +use redis_kiss::{get_connection, AsyncCommands}; + use super::state::{Cache, State}; /// Cache Manager @@ -197,9 +199,29 @@ impl State { self.insert_subscription(channel.id().to_string()); } + let mut conn = get_connection().await.unwrap(); + let mut new_servers = Vec::with_capacity(servers.len()); + + for server in servers { + let mut voice_states = vec![]; + + for channel in &server.channels { + let members = conn.smembers::<_, Vec>(format!("vc-members-{channel}")).await.unwrap(); + + if !members.is_empty() { + voice_states.push(ChannelVoiceState { + id: channel.clone(), + participants: members.into_iter().map(|id| UserVoiceState { id }).collect() + }) + } + } + + new_servers.push(ReadyServer { server: server.into(), voice_states }) + } + Ok(EventV1::Ready { users, - servers: servers.into_iter().map(Into::into).collect(), + servers: new_servers, channels: channels.into_iter().map(Into::into).collect(), members: members.into_iter().map(Into::into).collect(), emojis: emojis.into_iter().map(Into::into).collect(), diff --git a/crates/core/config/Revolt.toml b/crates/core/config/Revolt.toml index 85823e63e..c6c44f7c1 100644 --- a/crates/core/config/Revolt.toml +++ b/crates/core/config/Revolt.toml @@ -69,3 +69,4 @@ emoji_size = 500000 [sentry] api = "" events = "" +voice_ingress = "" \ No newline at end of file diff --git a/crates/core/config/src/lib.rs b/crates/core/config/src/lib.rs index 60fe88fb4..df8c2c884 100644 --- a/crates/core/config/src/lib.rs +++ b/crates/core/config/src/lib.rs @@ -44,8 +44,7 @@ pub struct Hosts { pub events: String, pub autumn: String, pub january: String, - pub voso_legacy: String, - pub voso_legacy_ws: String, + pub livekit: String } #[derive(Deserialize, Debug, Clone)] @@ -94,6 +93,13 @@ pub struct ApiWorkers { pub max_concurrent_connections: usize, } +#[derive(Deserialize, Debug, Clone)] +pub struct ApiLiveKit { + pub url: String, + pub key: String, + pub secret: String +} + #[derive(Deserialize, Debug, Clone)] pub struct Api { pub registration: ApiRegistration, @@ -102,6 +108,7 @@ pub struct Api { pub fcm: ApiFcm, pub security: ApiSecurity, pub workers: ApiWorkers, + pub livekit: ApiLiveKit, } #[derive(Deserialize, Debug, Clone)] @@ -144,6 +151,7 @@ pub struct Features { pub struct Sentry { pub api: String, pub events: String, + pub voice_ingress: String } #[derive(Deserialize, Debug, Clone)] diff --git a/crates/core/database/src/drivers/mod.rs b/crates/core/database/src/drivers/mod.rs index 51884c4f2..e209cd48c 100644 --- a/crates/core/database/src/drivers/mod.rs +++ b/crates/core/database/src/drivers/mod.rs @@ -22,7 +22,7 @@ pub enum DatabaseInfo { } /// Database -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Database { /// Mock database Reference(ReferenceDb), @@ -35,7 +35,7 @@ impl DatabaseInfo { #[async_recursion] pub async fn connect(self) -> Result { let config = config().await; - + println!("{config:?}"); Ok(match self { DatabaseInfo::Auto => { if std::env::var("TEST_DB").is_ok() { diff --git a/crates/core/database/src/drivers/mongodb.rs b/crates/core/database/src/drivers/mongodb.rs index a4bbe56bd..720f5a471 100644 --- a/crates/core/database/src/drivers/mongodb.rs +++ b/crates/core/database/src/drivers/mongodb.rs @@ -12,6 +12,7 @@ use serde::Serialize; database_derived!( #[cfg(feature = "mongodb")] /// MongoDB implementation + #[derive(Debug)] pub struct MongoDb(pub ::mongodb::Client, pub String); ); diff --git a/crates/core/database/src/drivers/reference.rs b/crates/core/database/src/drivers/reference.rs index 90458d247..657aef0aa 100644 --- a/crates/core/database/src/drivers/reference.rs +++ b/crates/core/database/src/drivers/reference.rs @@ -10,7 +10,7 @@ use crate::{ database_derived!( /// Reference implementation - #[derive(Default)] + #[derive(Default, Debug)] pub struct ReferenceDb { pub bots: Arc>>, pub channels: Arc>>, diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index 4d3b6c0de..04fb7f1b0 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -5,7 +5,7 @@ use revolt_models::v0::{ AppendMessage, Channel, Emoji, FieldsChannel, FieldsMember, FieldsRole, FieldsServer, FieldsUser, FieldsWebhook, Member, MemberCompositeKey, Message, PartialChannel, PartialMember, PartialMessage, PartialRole, PartialServer, PartialUser, PartialWebhook, Report, Server, User, - UserSettings, Webhook, + UserSettings, Webhook }; use revolt_result::Error; @@ -39,6 +39,25 @@ pub enum ErrorEvent { APIError(Error), } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct UserVoiceState { + pub id: String + // TODO - muted, etc +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ChannelVoiceState { + pub id: String, + pub participants: Vec +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ReadyServer { + #[serde(flatten)] + pub server: Server, + pub voice_states: Vec +} + /// Protocol Events #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(tag = "type")] @@ -51,7 +70,7 @@ pub enum EventV1 { /// Basic data to cache Ready { users: Vec, - servers: Vec, + servers: Vec, channels: Vec, members: Vec, emojis: Vec, @@ -225,6 +244,16 @@ pub enum EventV1 { /// Auth events Auth(AuthifierEvent), + + /// Voice events + VoiceChannelJoin { + id: String, + user: String, + }, + VoiceChannelLeave { + id: String, + user: String, + } } impl EventV1 { diff --git a/crates/core/database/src/models/channels/model.rs b/crates/core/database/src/models/channels/model.rs index eafd88a6f..2b671c382 100644 --- a/crates/core/database/src/models/channels/model.rs +++ b/crates/core/database/src/models/channels/model.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use revolt_config::config; -use revolt_models::v0::{self, MessageAuthor}; +use revolt_models::v0::{self, MessageAuthor, VoiceInformation}; use revolt_permissions::OverrideField; use revolt_result::Result; use serde::{Deserialize, Serialize}; @@ -103,6 +103,10 @@ auto_derived!( /// Whether this channel is marked as not safe for work #[serde(skip_serializing_if = "crate::if_false", default)] nsfw: bool, + + /// Voice Information for when this channel is also a voice channel + #[serde(skip_serializing_if = "Option::is_none")] + voice: Option }, /// Voice channel belonging to a server VoiceChannel { @@ -219,6 +223,7 @@ impl Channel { default_permissions: None, role_permissions: HashMap::new(), nsfw: data.nsfw.unwrap_or(false), + voice: data.voice }, v0::LegacyServerChannelType::Voice => Channel::VoiceChannel { id: id.clone(), diff --git a/crates/core/database/src/models/users/model.rs b/crates/core/database/src/models/users/model.rs index f09449d72..624cbefc0 100644 --- a/crates/core/database/src/models/users/model.rs +++ b/crates/core/database/src/models/users/model.rs @@ -4,6 +4,7 @@ use crate::{events::client::EventV1, Database, File, RatelimitEvent}; use once_cell::sync::Lazy; use rand::seq::SliceRandom; +use redis_kiss::{get_connection, AsyncCommands}; use revolt_config::config; use revolt_models::v0; use revolt_presence::filter_online; @@ -567,6 +568,15 @@ impl User { } } + /// Gets current voice channel + pub async fn current_voice_channel(&self) -> Result> { + let mut conn = get_connection().await.map_err(|_| create_error!(InternalError))?; + + conn.get::<_, Option>(format!("vc-{}", &self.id)) + .await + .map_err(|_| create_error!(InternalError)) + } + /// Update user data pub async fn update<'a>( &mut self, diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index d8e184db6..2d49fd026 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -186,6 +186,7 @@ impl From for Channel { default_permissions, role_permissions, nsfw, + voice } => Channel::TextChannel { id, server, @@ -196,6 +197,7 @@ impl From for Channel { default_permissions, role_permissions, nsfw, + voice }, crate::Channel::VoiceChannel { id, @@ -266,6 +268,7 @@ impl From for crate::Channel { default_permissions, role_permissions, nsfw, + voice } => crate::Channel::TextChannel { id, server, @@ -276,6 +279,7 @@ impl From for crate::Channel { default_permissions, role_permissions, nsfw, + voice }, Channel::VoiceChannel { id, diff --git a/crates/core/models/src/v0/channels.rs b/crates/core/models/src/v0/channels.rs index dfabb2af7..f30c75336 100644 --- a/crates/core/models/src/v0/channels.rs +++ b/crates/core/models/src/v0/channels.rs @@ -107,8 +107,12 @@ auto_derived!( serde(skip_serializing_if = "crate::if_false", default) )] nsfw: bool, + + /// Voice Information for when this channel is also a voice channel + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + voice: Option }, - /// Voice channel belonging to a server + /// DEPRECATED (use TextChannel { voice }): Voice channel belonging to a server VoiceChannel { /// Unique Id #[cfg_attr(feature = "serde", serde(rename = "_id"))] @@ -147,6 +151,15 @@ auto_derived!( }, } + /// Voice information for a channel + #[derive(Default)] + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct VoiceInformation { + /// Maximium amount of users allowed in the voice channel at once + #[cfg_attr(feature = "validator", validate(range(min = 1)))] + pub max_users: Option + } + /// Partial representation of a channel #[derive(Default)] pub struct PartialChannel { @@ -260,6 +273,10 @@ auto_derived!( /// Whether this channel is age restricted #[serde(skip_serializing_if = "Option::is_none")] pub nsfw: Option, + + /// Voice Information for when this channel is also a voice channel + #[serde(skip_serializing_if = "Option::is_none")] + pub voice: Option } /// New default permissions diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index 5608509df..c8e6c1c9a 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -130,8 +130,9 @@ pub enum ErrorType { error: String, }, - // ? Legacy errors - VosoUnavailable, + // ? Voice errors + LiveKitUnavailable, + AlreadyInVoiceChannel } #[macro_export] diff --git a/crates/core/result/src/rocket.rs b/crates/core/result/src/rocket.rs index d5ca34934..756da38e4 100644 --- a/crates/core/result/src/rocket.rs +++ b/crates/core/result/src/rocket.rs @@ -71,10 +71,12 @@ impl<'r> Responder<'r, 'static> for Error { ErrorType::InvalidProperty => Status::BadRequest, ErrorType::InvalidSession => Status::Unauthorized, ErrorType::DuplicateNonce => Status::Conflict, - ErrorType::VosoUnavailable => Status::BadRequest, ErrorType::NotFound => Status::NotFound, ErrorType::NoEffect => Status::Ok, ErrorType::FailedValidation { .. } => Status::BadRequest, + + ErrorType::LiveKitUnavailable => Status::BadRequest, + ErrorType::AlreadyInVoiceChannel => Status::BadRequest, }; // Serialize the error data structure into JSON. diff --git a/crates/delta/Cargo.toml b/crates/delta/Cargo.toml index cc4d0d39d..e5227d042 100644 --- a/crates/delta/Cargo.toml +++ b/crates/delta/Cargo.toml @@ -80,5 +80,8 @@ revolt-models = { path = "../core/models", features = [ revolt-result = { path = "../core/result", features = ["rocket", "okapi"] } revolt-permissions = { path = "../core/permissions", features = ["schemas"] } +# voice +livekit-api = "0.3.2" + [build-dependencies] vergen = "7.5.0" diff --git a/crates/delta/src/main.rs b/crates/delta/src/main.rs index c5ec08a25..f910e660c 100644 --- a/crates/delta/src/main.rs +++ b/crates/delta/src/main.rs @@ -24,6 +24,7 @@ use authifier::config::{ }; use authifier::{Authifier, AuthifierEvent}; use rocket::data::ToByteUnit; +use livekit_api::services::room::RoomClient; pub async fn web() -> Rocket { // Get settings @@ -34,6 +35,7 @@ pub async fn web() -> Rocket { // Setup database let db = revolt_database::DatabaseInfo::Auto.connect().await.unwrap(); + log::info!("database_here {db:?}"); db.migrate_database().await.unwrap(); // Setup Authifier event channel @@ -97,6 +99,11 @@ pub async fn web() -> Rocket { ) .into(); + + log::info!("{:?}", &config.api.livekit); + + let room_client = RoomClient::with_api_key(&config.api.livekit.url, &config.api.livekit.key, &config.api.livekit.secret); + // Configure Rocket let rocket = rocket::build(); let prometheus = PrometheusMetrics::new(); @@ -110,6 +117,7 @@ pub async fn web() -> Rocket { .manage(authifier) .manage(db) .manage(cors.clone()) + .manage(room_client) .attach(util::ratelimiter::RatelimitFairing) .attach(cors) .configure(rocket::Config { diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index 33f4e05c6..7b252ceda 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -1,105 +1,75 @@ +use std::borrow::Cow; + use revolt_config::config; -use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Channel, Database, User, -}; use revolt_models::v0; +use revolt_database::{events::client::EventV1, util::{permissions::perms, reference::Reference}, Channel, Database, User}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; + +use livekit_api::{access_token::{AccessToken, VideoGrants}, services::room::{CreateRoomOptions, RoomClient}}; use rocket::{serde::json::Json, State}; +use serde::{Deserialize, Serialize}; + +/// # Voice Server Token Response +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct CreateVoiceUserResponse { + /// Token for authenticating with the voice server + token: String, +} /// # Join Call /// /// Asks the voice server for a token to join the call. #[openapi(tag = "Voice")] #[post("//join_call")] -pub async fn call( - db: &State, - user: User, - target: Reference, -) -> Result> { +pub async fn call(db: &State, rooms: &State, user: User, target: Reference) -> Result> { let channel = target.as_channel(db).await?; - let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel); - calculate_channel_permissions(&mut query) + + let mut permissions = perms(db, &user).channel(&channel); + + calculate_channel_permissions(&mut permissions) .await .throw_if_lacking_channel_permission(ChannelPermission::Connect)?; - let config = config().await; - if config.api.security.voso_legacy_token.is_empty() { - return Err(create_error!(VosoUnavailable)); + if user.current_voice_channel().await?.is_some() { + return Err(create_error!(AlreadyInVoiceChannel)) } - match channel { - Channel::SavedMessages { .. } | Channel::TextChannel { .. } => { - return Err(create_error!(CannotJoinCall)) - } - _ => {} - } + let config = config().await; - // To join a call: - // - Check if the room exists. - // - If not, create it. - let client = reqwest::Client::new(); - let result = client - .get(&format!( - "{}/room/{}", - config.hosts.voso_legacy, - channel.id() - )) - .header( - reqwest::header::AUTHORIZATION, - config.api.security.voso_legacy_token.clone(), - ) - .send() - .await; + if config.api.livekit.url.is_empty() { + return Err(create_error!(LiveKitUnavailable)); + }; - match result { - Err(_) => return Err(create_error!(VosoUnavailable)), - Ok(result) => match result.status() { - reqwest::StatusCode::OK => (), - reqwest::StatusCode::NOT_FOUND => { - if (client - .post(&format!( - "{}/room/{}", - config.hosts.voso_legacy, - channel.id() - )) - .header( - reqwest::header::AUTHORIZATION, - config.api.security.voso_legacy_token.clone(), - ) - .send() - .await) - .is_err() - { - return Err(create_error!(VosoUnavailable)); - } - } - _ => return Err(create_error!(VosoUnavailable)), - }, - } + let voice = match &channel { + Channel::DirectMessage { .. } | Channel::VoiceChannel { .. } => Cow::Owned(v0::VoiceInformation::default()), + Channel::TextChannel { voice: Some(voice), .. } => Cow::Borrowed(voice), + _ => return Err(create_error!(CannotJoinCall)) + }; - // Then create a user for the room. - if let Ok(response) = client - .post(&format!( - "{}/room/{}/user/{}", - config.hosts.voso_legacy, - channel.id(), - user.id - )) - .header( - reqwest::header::AUTHORIZATION, - config.api.security.voso_legacy_token, - ) - .send() - .await - { - response - .json() - .await - .map_err(|_| create_error!(InvalidOperation)) - .map(Json) - } else { - Err(create_error!(VosoUnavailable)) - } + let token = AccessToken::with_api_key(&config.api.livekit.key, &config.api.livekit.secret) + .with_name(&format!("{}#{}", user.username, user.discriminator)) + .with_identity(&user.id) + .with_metadata(&serde_json::to_string(&user).map_err(|_| create_error!(InternalError))?) + .with_grants(VideoGrants { + room_join: true, + room: channel.id().to_string(), + ..Default::default() + }) + .to_jwt() + .inspect_err(|e| log::error!("{e:?}")) + .map_err(|_| create_error!(InternalError))?; + + let room = rooms.create_room(&channel.id(), CreateRoomOptions { + max_participants: voice.max_users.unwrap_or(u32::MAX), + empty_timeout: 5 * 60, // 5 minutes + ..Default::default() + }) + .await + .inspect_err(|e| log::error!("{e:?}")) + .map_err(|_| create_error!(InternalError))?; + + log::info!("created room {room:?}"); + + Ok(Json(CreateVoiceUserResponse { token })) } diff --git a/crates/delta/src/routes/root.rs b/crates/delta/src/routes/root.rs index 1e237bff6..d8677f8b4 100644 --- a/crates/delta/src/routes/root.rs +++ b/crates/delta/src/routes/root.rs @@ -27,9 +27,7 @@ pub struct VoiceFeature { /// Whether voice is enabled pub enabled: bool, /// URL pointing to the voice API - pub url: String, - /// URL pointing to the voice WebSocket server - pub ws: String, + pub url: String } /// # Feature Configuration @@ -46,7 +44,7 @@ pub struct RevoltFeatures { /// Proxy service configuration pub january: Feature, /// Voice server configuration - pub voso: VoiceFeature, + pub livekit: VoiceFeature, } /// # Build Information @@ -106,10 +104,9 @@ pub async fn root() -> Result> { enabled: !config.hosts.january.is_empty(), url: config.hosts.january, }, - voso: VoiceFeature { - enabled: !config.hosts.voso_legacy.is_empty(), - url: config.hosts.voso_legacy, - ws: config.hosts.voso_legacy_ws, + livekit: VoiceFeature { + enabled: !config.api.livekit.url.is_empty(), + url: config.api.livekit.url.to_string(), }, }, ws: config.hosts.events, diff --git a/crates/delta/src/util/test.rs b/crates/delta/src/util/test.rs index 467e945be..681f60767 100644 --- a/crates/delta/src/util/test.rs +++ b/crates/delta/src/util/test.rs @@ -100,7 +100,7 @@ impl TestHarness { } let mut stream = self.sub.on_message(); - while let Some(item) = stream.next().await { + while let Some(Ok(item)) = stream.next().await { let msg_topic = item.get_channel_name(); let payload: EventV1 = redis_kiss::decode_payload(&item).unwrap(); diff --git a/crates/voice-ingress/.gitignore b/crates/voice-ingress/.gitignore new file mode 100644 index 000000000..c41cc9e35 --- /dev/null +++ b/crates/voice-ingress/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/crates/voice-ingress/Cargo.toml b/crates/voice-ingress/Cargo.toml new file mode 100644 index 000000000..b220968f3 --- /dev/null +++ b/crates/voice-ingress/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "revolt-voice-ingress" +version = "0.7.1" +license = "AGPL-3.0-or-later" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# util +log = "*" +sentry = "0.31.5" +lru = "0.7.6" +ulid = "0.5.0" +redis-kiss = "0.1.4" + +# serde +serde_json = "1.0.79" +rmp-serde = "1.0.0" +serde = "1.0.136" + +# http +rocket = { version = "0.5.0-rc.2", features = ["json"] } +rocket_empty = "0.1.1" + +# async +futures = "0.3.21" +async-std = { version = "1.8.0", features = [ + "tokio1", + "tokio02", + "attributes", +] } + +# core +revolt-result = { path = "../core/result" } +revolt-models = { path = "../core/models" } +revolt-config = { path = "../core/config" } +revolt-database = { path = "../core/database" } +revolt-permissions = { version = "0.7.1", path = "../core/permissions" } +revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] } + +# voice +livekit-api = "0.3.2" +livekit-protocol = "0.3.2" \ No newline at end of file diff --git a/crates/voice-ingress/Dockerfile b/crates/voice-ingress/Dockerfile new file mode 100644 index 000000000..773069863 --- /dev/null +++ b/crates/voice-ingress/Dockerfile @@ -0,0 +1,11 @@ +# Build Stage +FROM ghcr.io/revoltchat/base:latest AS builder + +# Bundle Stage +FROM debian:bullseye-slim +RUN apt-get update && \ + apt-get install -y ca-certificates && \ + apt-get clean +COPY --from=builder /home/rust/src/target/release/revolt-voice-ingress ./ +EXPOSE 9000 +CMD ["./revolt-voice-ingress"] diff --git a/crates/voice-ingress/src/main.rs b/crates/voice-ingress/src/main.rs new file mode 100644 index 000000000..43edde5c5 --- /dev/null +++ b/crates/voice-ingress/src/main.rs @@ -0,0 +1,75 @@ +use std::env; + +use redis_kiss::{get_connection, AsyncCommands}; + +use revolt_database::events::client::EventV1; +use rocket::{build, post, routes, serde::json::Json, Config}; +use rocket_empty::EmptyResponse; +use livekit_protocol::WebhookEvent; + +use revolt_config::Database; +use revolt_result::Result; + +#[async_std::main] +async fn main() { + revolt_config::configure!(voice_ingress); + + let rocket = build() + .manage(get_connection().await.unwrap()) + .mount("/", routes![ingress]) + .configure(Config { port: 8500, ..Default::default() }) + .launch() + .await + .unwrap(); +} + +#[post("/", data="")] +async fn ingress(body: Json) -> Result { + let mut redis = get_connection().await.unwrap(); + + log::debug!("received event: {body:?}"); + + let channel_id = body + .room + .as_ref() + .map(|r| &r.name); + + let user_id = body + .participant + .as_ref() + .map(|r| &r.identity); + + match body.event.as_str() { + "participant_joined" => { + let channel_id = channel_id.unwrap(); + let user_id = user_id.unwrap(); + + redis.sadd::<_, _, u64>(format!("vc-members-{}", channel_id), user_id).await.unwrap(); + redis.set::<_, _, String>(format!("vc-{}", user_id), &channel_id).await.unwrap(); + + EventV1::VoiceChannelJoin { + id: channel_id.clone(), + user: user_id.clone() + } + .p(channel_id.clone()) + .await + }, + "participant_left" => { + let channel_id = channel_id.unwrap(); + let user_id = user_id.unwrap(); + + redis.srem::<_, _, u64>(format!("vc-members-{}", channel_id), user_id).await.unwrap(); + redis.del::<_, u64>(format!("vc-{}", user_id)).await.unwrap(); + + EventV1::VoiceChannelLeave { + id: channel_id.clone(), + user: user_id.clone() + } + .p(channel_id.clone()) + .await + }, + _ => {} + }; + + Ok(EmptyResponse) +} \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..31578d3bf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "stable" \ No newline at end of file From ae87bc0b67e5b55f8b18d9500fb180060afb81ba Mon Sep 17 00:00:00 2001 From: Zomatree Date: Tue, 9 Apr 2024 22:36:48 +0100 Subject: [PATCH 02/62] move voice states to global --- crates/bonfire/src/events/impl.rs | 51 +++++++++++-------- crates/core/database/src/events/client.rs | 29 ++--------- crates/core/models/src/v0/channels.rs | 14 +++-- crates/core/models/src/v0/users.rs | 7 +++ .../delta/src/routes/channels/voice_join.rs | 14 ++--- crates/voice-ingress/src/main.rs | 7 ++- 6 files changed, 62 insertions(+), 60 deletions(-) diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index d5ed027db..b7e900829 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -1,15 +1,15 @@ use std::collections::HashSet; use revolt_database::{ - events::client::{ChannelVoiceState, EventV1, ReadyServer, UserVoiceState}, util::permissions::DatabasePermissionQuery, Channel, Database, Member, + events::client::EventV1, util::permissions::DatabasePermissionQuery, Channel, Database, Member, MemberCompositeKey, Presence, RelationshipStatus, }; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_presence::filter_online; -use revolt_result::Result; +use revolt_result::{create_error, Result}; -use redis_kiss::{get_connection, AsyncCommands}; +use redis_kiss::{get_connection, AsyncCommands, Conn}; use super::state::{Cache, State}; @@ -127,11 +127,18 @@ impl State { // Filter server channels by permission. let channels = self.cache.filter_accessible_channels(db, channels).await; + let mut voice_states = Vec::new(); + let mut conn = get_connection().await.unwrap(); + // Append known user IDs from DMs. for channel in &channels { match channel { - Channel::DirectMessage { recipients, .. } | Channel::Group { recipients, .. } => { + Channel::DirectMessage { id, recipients, .. } | Channel::Group { id, recipients, .. } => { user_ids.extend(&mut recipients.clone().into_iter()); + + if let Ok(Some(voice_state)) = self.fetch_voice_state(&mut conn, id).await { + voice_states.push(voice_state) + } } _ => {} } @@ -199,32 +206,21 @@ impl State { self.insert_subscription(channel.id().to_string()); } - let mut conn = get_connection().await.unwrap(); - let mut new_servers = Vec::with_capacity(servers.len()); - - for server in servers { - let mut voice_states = vec![]; - + for server in &servers { for channel in &server.channels { - let members = conn.smembers::<_, Vec>(format!("vc-members-{channel}")).await.unwrap(); - - if !members.is_empty() { - voice_states.push(ChannelVoiceState { - id: channel.clone(), - participants: members.into_iter().map(|id| UserVoiceState { id }).collect() - }) + if let Ok(Some(voice_state)) = self.fetch_voice_state(&mut conn, channel).await { + voice_states.push(voice_state) } } - - new_servers.push(ReadyServer { server: server.into(), voice_states }) } Ok(EventV1::Ready { users, - servers: new_servers, + servers: servers.into_iter().map(Into::into).collect(), channels: channels.into_iter().map(Into::into).collect(), members: members.into_iter().map(Into::into).collect(), emojis: emojis.into_iter().map(Into::into).collect(), + voice_states }) } @@ -571,4 +567,19 @@ impl State { true } + + async fn fetch_voice_state(&self, conn: &mut Conn, channel: &str) -> Result> { + let members = conn.smembers::<_, Vec>(format!("vc-members-{channel}")) + .await + .map_err(|_| create_error!(InternalError))?; + + if !members.is_empty() { + Ok(Some(v0::ChannelVoiceState { + id: channel.to_string(), + participants: members.into_iter().map(|id| v0::UserVoiceState { id }).collect() + })) + } else { + Ok(None) + } + } } diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index 04fb7f1b0..feab64d44 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -2,10 +2,7 @@ use authifier::AuthifierEvent; use serde::{Deserialize, Serialize}; use revolt_models::v0::{ - AppendMessage, Channel, Emoji, FieldsChannel, FieldsMember, FieldsRole, FieldsServer, - FieldsUser, FieldsWebhook, Member, MemberCompositeKey, Message, PartialChannel, PartialMember, - PartialMessage, PartialRole, PartialServer, PartialUser, PartialWebhook, Report, Server, User, - UserSettings, Webhook + AppendMessage, Channel, ChannelVoiceState, Emoji, FieldsChannel, FieldsMember, FieldsRole, FieldsServer, FieldsUser, FieldsWebhook, Member, MemberCompositeKey, Message, PartialChannel, PartialMember, PartialMessage, PartialRole, PartialServer, PartialUser, PartialWebhook, Report, Server, User, UserSettings, UserVoiceState, Webhook }; use revolt_result::Error; @@ -39,25 +36,6 @@ pub enum ErrorEvent { APIError(Error), } -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UserVoiceState { - pub id: String - // TODO - muted, etc -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ChannelVoiceState { - pub id: String, - pub participants: Vec -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ReadyServer { - #[serde(flatten)] - pub server: Server, - pub voice_states: Vec -} - /// Protocol Events #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(tag = "type")] @@ -70,10 +48,11 @@ pub enum EventV1 { /// Basic data to cache Ready { users: Vec, - servers: Vec, + servers: Vec, channels: Vec, members: Vec, emojis: Vec, + voice_states: Vec }, /// Ping response @@ -248,7 +227,7 @@ pub enum EventV1 { /// Voice events VoiceChannelJoin { id: String, - user: String, + state: UserVoiceState, }, VoiceChannelLeave { id: String, diff --git a/crates/core/models/src/v0/channels.rs b/crates/core/models/src/v0/channels.rs index f30c75336..fd88f093a 100644 --- a/crates/core/models/src/v0/channels.rs +++ b/crates/core/models/src/v0/channels.rs @@ -1,4 +1,4 @@ -use super::File; +use super::{File, UserVoiceState}; use revolt_permissions::{Override, OverrideField}; use std::collections::{HashMap, HashSet}; @@ -306,10 +306,18 @@ auto_derived!( } /// Voice server token response - pub struct LegacyCreateVoiceUserResponse { + pub struct CreateVoiceUserResponse { /// Token for authenticating with the voice server - token: String, + pub token: String, } + + /// Voice state for a channel + pub struct ChannelVoiceState { + pub id: String, + /// The states of the users who are connected to the channel + pub participants: Vec + } + ); impl Channel { diff --git a/crates/core/models/src/v0/users.rs b/crates/core/models/src/v0/users.rs index 1966e35ca..1e6c4fffe 100644 --- a/crates/core/models/src/v0/users.rs +++ b/crates/core/models/src/v0/users.rs @@ -270,6 +270,13 @@ auto_derived!( /// Username and discriminator combo separated by # pub username: String, } + + /// Voice State information for a user + pub struct UserVoiceState { + pub id: String + // TODO - muted, etc + } + ); pub trait CheckRelationship { diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index 7b252ceda..e6afd62b6 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -2,27 +2,19 @@ use std::borrow::Cow; use revolt_config::config; use revolt_models::v0; -use revolt_database::{events::client::EventV1, util::{permissions::perms, reference::Reference}, Channel, Database, User}; +use revolt_database::{util::{permissions::perms, reference::Reference}, Channel, Database, User}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; use livekit_api::{access_token::{AccessToken, VideoGrants}, services::room::{CreateRoomOptions, RoomClient}}; use rocket::{serde::json::Json, State}; -use serde::{Deserialize, Serialize}; - -/// # Voice Server Token Response -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct CreateVoiceUserResponse { - /// Token for authenticating with the voice server - token: String, -} /// # Join Call /// /// Asks the voice server for a token to join the call. #[openapi(tag = "Voice")] #[post("//join_call")] -pub async fn call(db: &State, rooms: &State, user: User, target: Reference) -> Result> { +pub async fn call(db: &State, rooms: &State, user: User, target: Reference) -> Result> { let channel = target.as_channel(db).await?; let mut permissions = perms(db, &user).channel(&channel); @@ -71,5 +63,5 @@ pub async fn call(db: &State, rooms: &State, user: User, t log::info!("created room {room:?}"); - Ok(Json(CreateVoiceUserResponse { token })) + Ok(Json(v0::CreateVoiceUserResponse { token })) } diff --git a/crates/voice-ingress/src/main.rs b/crates/voice-ingress/src/main.rs index 43edde5c5..6fc6c0d16 100644 --- a/crates/voice-ingress/src/main.rs +++ b/crates/voice-ingress/src/main.rs @@ -3,6 +3,7 @@ use std::env; use redis_kiss::{get_connection, AsyncCommands}; use revolt_database::events::client::EventV1; +use revolt_models::v0::UserVoiceState; use rocket::{build, post, routes, serde::json::Json, Config}; use rocket_empty::EmptyResponse; use livekit_protocol::WebhookEvent; @@ -47,9 +48,13 @@ async fn ingress(body: Json) -> Result { redis.sadd::<_, _, u64>(format!("vc-members-{}", channel_id), user_id).await.unwrap(); redis.set::<_, _, String>(format!("vc-{}", user_id), &channel_id).await.unwrap(); + let voice_state = UserVoiceState { + id: user_id.clone() + }; + EventV1::VoiceChannelJoin { id: channel_id.clone(), - user: user_id.clone() + state: voice_state } .p(channel_id.clone()) .await From 7a4421089ce5a44f58d4a999a2d43b2b423395c7 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Thu, 11 Apr 2024 01:07:03 +0100 Subject: [PATCH 03/62] track current state --- crates/bonfire/src/events/impl.rs | 53 +++++--- crates/core/database/src/events/client.rs | 7 +- .../database/src/models/channels/model.rs | 8 ++ .../core/database/src/models/users/model.rs | 10 +- crates/core/models/src/v0/users.rs | 13 +- .../delta/src/routes/channels/voice_join.rs | 27 +++- crates/voice-ingress/src/main.rs | 127 +++++++++++++++--- 7 files changed, 199 insertions(+), 46 deletions(-) diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index b7e900829..afe5877b9 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -133,12 +133,8 @@ impl State { // Append known user IDs from DMs. for channel in &channels { match channel { - Channel::DirectMessage { id, recipients, .. } | Channel::Group { id, recipients, .. } => { + Channel::DirectMessage { recipients, .. } | Channel::Group { recipients, .. } => { user_ids.extend(&mut recipients.clone().into_iter()); - - if let Ok(Some(voice_state)) = self.fetch_voice_state(&mut conn, id).await { - voice_states.push(voice_state) - } } _ => {} } @@ -206,11 +202,9 @@ impl State { self.insert_subscription(channel.id().to_string()); } - for server in &servers { - for channel in &server.channels { - if let Ok(Some(voice_state)) = self.fetch_voice_state(&mut conn, channel).await { - voice_states.push(voice_state) - } + for channel in &channels { + if let Ok(Some(voice_state)) = self.fetch_voice_state(&mut conn, channel).await { + voice_states.push(voice_state) } } @@ -220,7 +214,7 @@ impl State { channels: channels.into_iter().map(Into::into).collect(), members: members.into_iter().map(Into::into).collect(), emojis: emojis.into_iter().map(Into::into).collect(), - voice_states + voice_states, }) } @@ -568,15 +562,44 @@ impl State { true } - async fn fetch_voice_state(&self, conn: &mut Conn, channel: &str) -> Result> { - let members = conn.smembers::<_, Vec>(format!("vc-members-{channel}")) + async fn fetch_voice_state( + &self, + conn: &mut Conn, + channel: &Channel, + ) -> Result> { + let members = conn + .smembers::<_, Vec>(format!("vc-members-{}", channel.id())) .await .map_err(|_| create_error!(InternalError))?; + let channel_or_server_id = channel.server().unwrap_or_else(|| channel.id()); + + if !members.is_empty() { + let mut participants = Vec::with_capacity(members.len()); + + for id in members { + let unique_key = format!("{channel_or_server_id}-{id}"); + + let (audio, deafened, screensharing, camera) = conn + .mget::<_, (bool, bool, bool, bool)>(&[ + format!("audio-{unique_key}"), + format!("deafened-{unique_key}"), + format!("screensharing-{unique_key}"), + format!("camera-{unique_key}") + + ]) + .await + .map_err(|_| create_error!(InternalError))?; + + let voice_state = v0::UserVoiceState { id, audio, deafened, screensharing, camera }; + + participants.push(voice_state); + } + Ok(Some(v0::ChannelVoiceState { - id: channel.to_string(), - participants: members.into_iter().map(|id| v0::UserVoiceState { id }).collect() + id: channel.id(), + participants, })) } else { Ok(None) diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index feab64d44..473c5cb32 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -2,7 +2,7 @@ use authifier::AuthifierEvent; use serde::{Deserialize, Serialize}; use revolt_models::v0::{ - AppendMessage, Channel, ChannelVoiceState, Emoji, FieldsChannel, FieldsMember, FieldsRole, FieldsServer, FieldsUser, FieldsWebhook, Member, MemberCompositeKey, Message, PartialChannel, PartialMember, PartialMessage, PartialRole, PartialServer, PartialUser, PartialWebhook, Report, Server, User, UserSettings, UserVoiceState, Webhook + AppendMessage, Channel, ChannelVoiceState, Emoji, FieldsChannel, FieldsMember, FieldsRole, FieldsServer, FieldsUser, FieldsWebhook, Member, MemberCompositeKey, Message, PartialChannel, PartialMember, PartialMessage, PartialRole, PartialServer, PartialUser, PartialUserVoiceState, PartialWebhook, Report, Server, User, UserSettings, UserVoiceState, Webhook }; use revolt_result::Error; @@ -232,6 +232,11 @@ pub enum EventV1 { VoiceChannelLeave { id: String, user: String, + }, + UserVoiceStateUpdate { + id: String, + channel_id: String, + data: PartialUserVoiceState } } diff --git a/crates/core/database/src/models/channels/model.rs b/crates/core/database/src/models/channels/model.rs index 2b671c382..775999930 100644 --- a/crates/core/database/src/models/channels/model.rs +++ b/crates/core/database/src/models/channels/model.rs @@ -430,6 +430,14 @@ impl Channel { } } + /// Clone this channel's server id + pub fn server(&self) -> Option { + match self { + Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => Some(server.clone()), + _ => None + } + } + /// Set role permission on a channel pub async fn set_role_permission( &mut self, diff --git a/crates/core/database/src/models/users/model.rs b/crates/core/database/src/models/users/model.rs index 624cbefc0..7d52ed38a 100644 --- a/crates/core/database/src/models/users/model.rs +++ b/crates/core/database/src/models/users/model.rs @@ -569,10 +569,14 @@ impl User { } /// Gets current voice channel - pub async fn current_voice_channel(&self) -> Result> { - let mut conn = get_connection().await.map_err(|_| create_error!(InternalError))?; + /// + /// current_context: Either the channel id if a dm or group, or server_id if in a server + pub async fn current_voice_channel(&self, current_context: &str) -> Result> { + let mut conn = get_connection() + .await + .map_err(|_| create_error!(InternalError))?; - conn.get::<_, Option>(format!("vc-{}", &self.id)) + conn.get::<_, Option>(format!("vc-{}-{current_context}", &self.id)) .await .map_err(|_| create_error!(InternalError)) } diff --git a/crates/core/models/src/v0/users.rs b/crates/core/models/src/v0/users.rs index 1e6c4fffe..50d8163c7 100644 --- a/crates/core/models/src/v0/users.rs +++ b/crates/core/models/src/v0/users.rs @@ -270,13 +270,18 @@ auto_derived!( /// Username and discriminator combo separated by # pub username: String, } +); +auto_derived_partial!( /// Voice State information for a user pub struct UserVoiceState { - pub id: String - // TODO - muted, etc - } - + pub id: String, + pub audio: bool, + pub deafened: bool, + pub screensharing: bool, + pub camera: bool + }, + "PartialUserVoiceState" ); pub trait CheckRelationship { diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index e6afd62b6..08330b806 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -19,13 +19,17 @@ pub async fn call(db: &State, rooms: &State, user: User, t let mut permissions = perms(db, &user).channel(&channel); - calculate_channel_permissions(&mut permissions) - .await - .throw_if_lacking_channel_permission(ChannelPermission::Connect)?; + let current_permissions = calculate_channel_permissions(&mut permissions).await; + current_permissions.throw_if_lacking_channel_permission(ChannelPermission::Connect)?; - if user.current_voice_channel().await?.is_some() { - return Err(create_error!(AlreadyInVoiceChannel)) - } + // TODO - decide if we should allow being in multiple voice channels for users + + // if user.current_voice_channel(&channel.server().unwrap_or_else(|| channel.id())) + // .await? + // .is_some() + // { + // return Err(create_error!(AlreadyInVoiceChannel)) + // } let config = config().await; @@ -39,12 +43,23 @@ pub async fn call(db: &State, rooms: &State, user: User, t _ => return Err(create_error!(CannotJoinCall)) }; + let mut allowed_sources = Vec::new(); + + if current_permissions.has(ChannelPermission::Speak as u64) { + allowed_sources.push("MICROPHONE".to_string()) + }; + + if current_permissions.has(ChannelPermission::Video as u64) { + allowed_sources.extend(["CAMERA".to_string(), "SCREEN_SHARE".to_string(), "SCREEN_SHARE_AUDIO".to_string()]); + }; + let token = AccessToken::with_api_key(&config.api.livekit.key, &config.api.livekit.secret) .with_name(&format!("{}#{}", user.username, user.discriminator)) .with_identity(&user.id) .with_metadata(&serde_json::to_string(&user).map_err(|_| create_error!(InternalError))?) .with_grants(VideoGrants { room_join: true, + can_publish_sources: allowed_sources, room: channel.id().to_string(), ..Default::default() }) diff --git a/crates/voice-ingress/src/main.rs b/crates/voice-ingress/src/main.rs index 6fc6c0d16..dbe9bbfce 100644 --- a/crates/voice-ingress/src/main.rs +++ b/crates/voice-ingress/src/main.rs @@ -2,31 +2,36 @@ use std::env; use redis_kiss::{get_connection, AsyncCommands}; -use revolt_database::events::client::EventV1; -use revolt_models::v0::UserVoiceState; -use rocket::{build, post, routes, serde::json::Json, Config}; +use revolt_database::{events::client::EventV1, util::reference::Reference, Database, DatabaseInfo}; +use revolt_models::v0::{PartialUserVoiceState, UserVoiceState}; +use rocket::{build, post, routes, serde::json::Json, Config, State}; use rocket_empty::EmptyResponse; use livekit_protocol::WebhookEvent; -use revolt_config::Database; use revolt_result::Result; -#[async_std::main] -async fn main() { +#[rocket::main] +async fn main() -> Result<(), rocket::Error> { revolt_config::configure!(voice_ingress); - let rocket = build() - .manage(get_connection().await.unwrap()) + let database = DatabaseInfo::Auto.connect().await.unwrap(); + + let _rocket = build() + .manage(database) .mount("/", routes![ingress]) .configure(Config { port: 8500, ..Default::default() }) + .ignite() + .await? .launch() - .await - .unwrap(); + .await?; + + Ok(()) } + #[post("/", data="")] -async fn ingress(body: Json) -> Result { - let mut redis = get_connection().await.unwrap(); +async fn ingress(db: &State, body: Json) -> Result { + let mut conn = get_connection().await.unwrap(); log::debug!("received event: {body:?}"); @@ -45,11 +50,27 @@ async fn ingress(body: Json) -> Result { let channel_id = channel_id.unwrap(); let user_id = user_id.unwrap(); - redis.sadd::<_, _, u64>(format!("vc-members-{}", channel_id), user_id).await.unwrap(); - redis.set::<_, _, String>(format!("vc-{}", user_id), &channel_id).await.unwrap(); + let channel = Reference::from_unchecked(channel_id.clone()) + .as_channel(db) + .await?; + + + let unique_key = format!("{}-{user_id}", channel.server().unwrap_or_else(|| channel.id())); + + conn.sadd::<_, _, u64>(format!("vc-members-{channel_id}"), user_id).await.unwrap(); + + conn.set::<_, _, String>(format!("vc-{unique_key}"), &channel_id).await.unwrap(); + conn.set::<_, _, String>(format!("audio-{unique_key}"), true).await.unwrap(); + conn.set::<_, _, String>(format!("deafened-{unique_key}"), false).await.unwrap(); + conn.set::<_, _, String>(format!("screensharing-{unique_key}"), false).await.unwrap(); + conn.set::<_, _, String>(format!("camera-{unique_key}"), false).await.unwrap(); let voice_state = UserVoiceState { - id: user_id.clone() + id: user_id.clone(), + audio: false, + deafened: false, + screensharing: false, + camera: false }; EventV1::VoiceChannelJoin { @@ -63,8 +84,20 @@ async fn ingress(body: Json) -> Result { let channel_id = channel_id.unwrap(); let user_id = user_id.unwrap(); - redis.srem::<_, _, u64>(format!("vc-members-{}", channel_id), user_id).await.unwrap(); - redis.del::<_, u64>(format!("vc-{}", user_id)).await.unwrap(); + let channel = Reference::from_unchecked(channel_id.clone()) + .as_channel(db) + .await?; + + conn.srem::<_, _, u64>(format!("vc-members-{channel_id}"), user_id).await.unwrap(); + + let unique_key = format!("{}-{user_id}", channel.server().unwrap_or_else(|| channel.id())); + + conn.del::<_, u64>(format!("vc-{unique_key}")).await.unwrap(); + + conn.del::<_, u64>(format!("audio-{unique_key}")).await.unwrap(); + conn.del::<_, u64>(format!("deafened-{unique_key}")).await.unwrap(); + conn.del::<_, u64>(format!("screensharing-{unique_key}")).await.unwrap(); + conn.del::<_, u64>(format!("camera-{unique_key}")).await.unwrap(); EventV1::VoiceChannelLeave { id: channel_id.clone(), @@ -73,6 +106,66 @@ async fn ingress(body: Json) -> Result { .p(channel_id.clone()) .await }, + "track_published" | "track_unpublished" => { + let value = body.event == "track_published"; // to avoid duplicating this entire case twice + + let channel_id = channel_id.unwrap(); + let user_id = user_id.unwrap(); + let track = body.track.as_ref().unwrap(); + + let user = Reference::from_unchecked(user_id.clone()) + .as_user(db) + .await?; + + let channel = Reference::from_unchecked(channel_id.clone()) + .as_channel(db) + .await?; + + let unique_key = if user.bot.is_some() { + format!("{}-{user_id}", channel.server().unwrap_or_else(|| channel.id())) + } else { + user_id.to_string() + }; + + let partial = match track.source { + /* TrackSource::Unknown */ 0 => { + PartialUserVoiceState::default() + } + /* TrackSource::Camera */ 1 => { + conn.set::<_, _, String>(format!("camera-{unique_key}"), value).await.unwrap(); + + PartialUserVoiceState { + camera: Some(value), + ..Default::default() + } + } + /* TrackSource::Microphone */ 2 => { + conn.set::<_, _, String>(format!("audio-{unique_key}"), value).await.unwrap(); + + PartialUserVoiceState { + audio: Some(value), + ..Default::default() + } + }, + /* TrackSource::ScreenShare | TrackSource::ScreenShareAudio */ 3 | 4 => { + conn.set::<_, _, String>(format!("screensharing-{unique_key}"), value).await.unwrap(); + + PartialUserVoiceState { + screensharing: Some(value), + ..Default::default() + } + }, + _ => unreachable!() + }; + + EventV1::UserVoiceStateUpdate { + id: user_id.clone(), + channel_id: channel_id.clone(), + data: partial + } + .p(channel_id.clone()) + .await; + }, _ => {} }; From 67569f932ed0d61dfe048c50d0b3c03d2be74cb1 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Wed, 24 Apr 2024 02:23:52 +0100 Subject: [PATCH 04/62] more stuff --- Cargo.lock | 1 + crates/bonfire/src/events/impl.rs | 12 +- .../admin_migrations/ops/mongodb/scripts.rs | 20 ++- .../src/models/server_members/model.rs | 13 +- crates/core/database/src/util/bridge/v0.rs | 16 +- crates/core/models/src/v0/server_members.rs | 12 ++ crates/core/models/src/v0/users.rs | 6 +- crates/core/result/src/lib.rs | 33 ++++ crates/delta/Cargo.toml | 1 + .../delta/src/routes/servers/member_edit.rs | 70 +++++++- crates/voice-ingress/src/main.rs | 155 ++++++++++-------- 11 files changed, 257 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10f7a1c2f..6df07905a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4103,6 +4103,7 @@ dependencies = [ "lettre", "linkify 0.6.0", "livekit-api", + "livekit-protocol", "log", "lru 0.7.8", "nanoid", diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index afe5877b9..3f6eb9548 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -574,7 +574,6 @@ impl State { let channel_or_server_id = channel.server().unwrap_or_else(|| channel.id()); - if !members.is_empty() { let mut participants = Vec::with_capacity(members.len()); @@ -586,13 +585,18 @@ impl State { format!("audio-{unique_key}"), format!("deafened-{unique_key}"), format!("screensharing-{unique_key}"), - format!("camera-{unique_key}") - + format!("camera-{unique_key}"), ]) .await .map_err(|_| create_error!(InternalError))?; - let voice_state = v0::UserVoiceState { id, audio, deafened, screensharing, camera }; + let voice_state = v0::UserVoiceState { + id, + can_receive: audio, + can_publish: deafened, + screensharing, + camera, + }; participants.push(voice_state); } diff --git a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs index ecc146721..bf36fed29 100644 --- a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs +++ b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs @@ -19,7 +19,7 @@ struct MigrationInfo { revision: i32, } -pub const LATEST_REVISION: i32 = 26; +pub const LATEST_REVISION: i32 = 27; pub async fn migrate_database(db: &MongoDb) { let migrations = db.col::("migrations"); @@ -983,6 +983,24 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { .expect("Failed to create ratelimit_events index."); } + if revision <= 26 { + info!("Running migration [revision 26 / 17-04-2024]: Add `can_publish` and `can_receive` to members"); + + db.col::("server_members") + .update_many( + doc! {}, + doc! { + "$set": { + "can_publish": true, + "can_receive": true + } + }, + None + ) + .await + .expect("Failed to update members"); + } + // Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`. // Reminder to update LATEST_REVISION when adding new migrations. diff --git a/crates/core/database/src/models/server_members/model.rs b/crates/core/database/src/models/server_members/model.rs index 63a35ec32..fd068aeeb 100644 --- a/crates/core/database/src/models/server_members/model.rs +++ b/crates/core/database/src/models/server_members/model.rs @@ -3,8 +3,8 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; use crate::{ - events::client::EventV1, util::permissions::DatabasePermissionQuery, Channel, Database, File, - Server, SystemMessage, User, + events::client::EventV1, if_false, util::permissions::DatabasePermissionQuery, Channel, + Database, File, Server, SystemMessage, User, }; auto_derived_partial!( @@ -30,6 +30,13 @@ auto_derived_partial!( /// Timestamp this member is timed out until #[serde(skip_serializing_if = "Option::is_none")] pub timeout: Option, + + /// Whether the member is server-wide voice muted + #[serde(skip_serializing_if = "if_false")] + pub can_publish: bool, + /// Whether the member is server-wide voice deafened + #[serde(skip_serializing_if = "if_false")] + pub can_receive: bool, }, "PartialMember" ); @@ -69,6 +76,8 @@ impl Default for Member { avatar: None, roles: vec![], timeout: None, + can_publish: false, + can_receive: false, } } } diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index 2d49fd026..042f10c8f 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -186,7 +186,7 @@ impl From for Channel { default_permissions, role_permissions, nsfw, - voice + voice, } => Channel::TextChannel { id, server, @@ -197,7 +197,7 @@ impl From for Channel { default_permissions, role_permissions, nsfw, - voice + voice, }, crate::Channel::VoiceChannel { id, @@ -268,7 +268,7 @@ impl From for crate::Channel { default_permissions, role_permissions, nsfw, - voice + voice, } => crate::Channel::TextChannel { id, server, @@ -279,7 +279,7 @@ impl From for crate::Channel { default_permissions, role_permissions, nsfw, - voice + voice, }, Channel::VoiceChannel { id, @@ -614,6 +614,8 @@ impl From for Member { avatar: value.avatar.map(|f| f.into()), roles: value.roles, timeout: value.timeout, + can_publish: value.can_publish, + can_receive: value.can_receive, } } } @@ -627,6 +629,8 @@ impl From for crate::Member { avatar: value.avatar.map(|f| f.into()), roles: value.roles, timeout: value.timeout, + can_publish: value.can_publish, + can_receive: value.can_receive, } } } @@ -640,6 +644,8 @@ impl From for PartialMember { avatar: value.avatar.map(|f| f.into()), roles: value.roles, timeout: value.timeout, + can_publish: value.can_publish, + can_receive: value.can_receive, } } } @@ -653,6 +659,8 @@ impl From for crate::PartialMember { avatar: value.avatar.map(|f| f.into()), roles: value.roles, timeout: value.timeout, + can_publish: value.can_publish, + can_receive: value.can_receive, } } } diff --git a/crates/core/models/src/v0/server_members.rs b/crates/core/models/src/v0/server_members.rs index 5b26b88a2..a13191436 100644 --- a/crates/core/models/src/v0/server_members.rs +++ b/crates/core/models/src/v0/server_members.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use super::{File, Role, User}; +use crate::if_false; use iso8601_timestamp::Timestamp; use once_cell::sync::Lazy; @@ -57,6 +58,13 @@ auto_derived_partial!( /// Timestamp this member is timed out until #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub timeout: Option, + + /// Whether the member is server-wide voice muted + #[serde(skip_serializing_if = "if_false")] + pub can_publish: bool, + /// Whether the member is server-wide voice deafened + #[serde(skip_serializing_if = "if_false")] + pub can_receive: bool, }, "PartialMember" ); @@ -126,5 +134,9 @@ auto_derived!( /// Fields to remove from channel object #[cfg_attr(feature = "validator", validate(length(min = 1)))] pub remove: Option>, + /// server-wide voice muted + pub can_publish: Option, + /// server-wide voice deafened + pub can_receive: Option, } ); diff --git a/crates/core/models/src/v0/users.rs b/crates/core/models/src/v0/users.rs index 50d8163c7..7b959d9a0 100644 --- a/crates/core/models/src/v0/users.rs +++ b/crates/core/models/src/v0/users.rs @@ -276,10 +276,10 @@ auto_derived_partial!( /// Voice State information for a user pub struct UserVoiceState { pub id: String, - pub audio: bool, - pub deafened: bool, + pub can_receive: bool, + pub can_publish: bool, pub screensharing: bool, - pub camera: bool + pub camera: bool, }, "PartialUserVoiceState" ); diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index c8e6c1c9a..3733a640d 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -1,3 +1,5 @@ +use std::panic::Location; + #[cfg(feature = "serde")] #[macro_use] extern crate serde; @@ -172,6 +174,37 @@ macro_rules! query { }; } +pub trait ToRevoltError { + #[track_caller] + fn to_internal_error(self) -> Result; +} + +impl ToRevoltError for Result { + fn to_internal_error(self) -> Result { + self.map_err(|_| { + let loc = Location::caller(); + + Error { + error_type: ErrorType::InternalError, + location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) + } + }) + } +} + +impl ToRevoltError for Option { + fn to_internal_error(self) -> Result { + self.ok_or_else(|| { + let loc = Location::caller(); + + Error { + error_type: ErrorType::InternalError, + location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) + } + }) + } +} + #[cfg(test)] mod tests { use crate::ErrorType; diff --git a/crates/delta/Cargo.toml b/crates/delta/Cargo.toml index e5227d042..e8a4b6844 100644 --- a/crates/delta/Cargo.toml +++ b/crates/delta/Cargo.toml @@ -82,6 +82,7 @@ revolt-permissions = { path = "../core/permissions", features = ["schemas"] } # voice livekit-api = "0.3.2" +livekit-protocol = "0.3.2" [build-dependencies] vergen = "7.5.0" diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index 8d4788129..941a36ff3 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -1,5 +1,8 @@ use std::collections::HashSet; +use livekit_api::services::room::{RoomClient, UpdateParticipantOptions}; +use livekit_protocol::ParticipantPermission; +use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, File, PartialMember, User, @@ -7,7 +10,7 @@ use revolt_database::{ use revolt_models::v0; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; -use revolt_result::{create_error, Result}; +use revolt_result::{create_error, Result, ToRevoltError}; use rocket::{serde::json::Json, State}; use validator::Validate; @@ -17,6 +20,7 @@ use validator::Validate; #[openapi(tag = "Server Members")] #[patch("//members/", data = "")] pub async fn edit( + room_client: &State, db: &State, user: User, server: Reference, @@ -87,6 +91,14 @@ pub async fn edit( permissions.throw_if_lacking_channel_permission(ChannelPermission::TimeoutMembers)?; } + if data.can_publish.is_some() { + permissions.throw_if_lacking_channel_permission(ChannelPermission::MuteMembers)?; + } + + if data.can_receive.is_some() { + permissions.throw_if_lacking_channel_permission(ChannelPermission::DeafenMembers)?; + } + // Resolve our ranking let our_ranking = query.get_member_rank().unwrap_or(i64::MIN); @@ -122,12 +134,16 @@ pub async fn edit( roles, timeout, remove, + can_publish, + can_receive, } = data; let mut partial = PartialMember { nickname, roles, timeout, + can_publish, + can_receive, ..Default::default() }; @@ -155,5 +171,57 @@ pub async fn edit( ) .await?; + if can_publish.is_some() || can_receive.is_some() { + let mut conn = get_connection().await.to_internal_error()?; + + // if we edit the member while they are in a voice channel we need to also update the perms + // otherwise it wont take place until they leave and rejoin + if let Some(channel) = conn + .get::<_, Option>(format!("vc-{}-{}", &member.id.user, &member.id.server)) + .await + .to_internal_error()? + { + let mut pipeline = Pipeline::new(); + let mut new_perms = ParticipantPermission::default(); + + if let Some(can_publish) = can_publish { + pipeline.set( + format!("can_publish-{}-{}", &channel, &member.id.user), + can_publish, + ); + + new_perms.can_publish = can_publish; + new_perms.can_publish_data = can_publish; + }; + + if let Some(can_receive) = can_receive { + pipeline.set( + format!("can_receive-{}-{}", &channel, &member.id.user), + can_receive, + ); + + new_perms.can_subscribe = can_receive; + }; + + pipeline + .query_async(&mut conn.into_inner()) + .await + .to_internal_error()?; + + room_client + .update_participant( + &channel, + &member.id.user, + UpdateParticipantOptions { + permission: Some(new_perms), + name: "".to_string(), + metadata: "".to_string(), + }, + ) + .await + .to_internal_error()?; + }; + }; + Ok(Json(member.into())) } diff --git a/crates/voice-ingress/src/main.rs b/crates/voice-ingress/src/main.rs index dbe9bbfce..d6b0a8131 100644 --- a/crates/voice-ingress/src/main.rs +++ b/crates/voice-ingress/src/main.rs @@ -1,14 +1,16 @@ use std::env; -use redis_kiss::{get_connection, AsyncCommands}; +use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; -use revolt_database::{events::client::EventV1, util::reference::Reference, Database, DatabaseInfo}; +use livekit_protocol::WebhookEvent; +use revolt_database::{ + events::client::EventV1, util::reference::Reference, Database, DatabaseInfo, +}; use revolt_models::v0::{PartialUserVoiceState, UserVoiceState}; use rocket::{build, post, routes, serde::json::Json, Config, State}; use rocket_empty::EmptyResponse; -use livekit_protocol::WebhookEvent; -use revolt_result::Result; +use revolt_result::{Result, ToRevoltError}; #[rocket::main] async fn main() -> Result<(), rocket::Error> { @@ -19,7 +21,10 @@ async fn main() -> Result<(), rocket::Error> { let _rocket = build() .manage(database) .mount("/", routes![ingress]) - .configure(Config { port: 8500, ..Default::default() }) + .configure(Config { + port: 8500, + ..Default::default() + }) .ignite() .await? .launch() @@ -28,90 +33,96 @@ async fn main() -> Result<(), rocket::Error> { Ok(()) } - -#[post("/", data="")] +#[post("/", data = "")] async fn ingress(db: &State, body: Json) -> Result { - let mut conn = get_connection().await.unwrap(); + let mut conn = get_connection().await.to_internal_error()?; log::debug!("received event: {body:?}"); - let channel_id = body - .room - .as_ref() - .map(|r| &r.name); + let channel_id = body.room.as_ref().map(|r| &r.name); - let user_id = body - .participant - .as_ref() - .map(|r| &r.identity); + let user_id = body.participant.as_ref().map(|r| &r.identity); match body.event.as_str() { "participant_joined" => { - let channel_id = channel_id.unwrap(); - let user_id = user_id.unwrap(); + let channel_id = channel_id.to_internal_error()?; + let user_id = user_id.to_internal_error()?; let channel = Reference::from_unchecked(channel_id.clone()) .as_channel(db) .await?; - - let unique_key = format!("{}-{user_id}", channel.server().unwrap_or_else(|| channel.id())); - - conn.sadd::<_, _, u64>(format!("vc-members-{channel_id}"), user_id).await.unwrap(); - - conn.set::<_, _, String>(format!("vc-{unique_key}"), &channel_id).await.unwrap(); - conn.set::<_, _, String>(format!("audio-{unique_key}"), true).await.unwrap(); - conn.set::<_, _, String>(format!("deafened-{unique_key}"), false).await.unwrap(); - conn.set::<_, _, String>(format!("screensharing-{unique_key}"), false).await.unwrap(); - conn.set::<_, _, String>(format!("camera-{unique_key}"), false).await.unwrap(); + let unique_key = format!( + "{}-{user_id}", + channel.server().unwrap_or_else(|| channel.id()) + ); + + Pipeline::new() + .sadd(format!("vc-members-{channel_id}"), user_id) + .set(format!("vc-{unique_key}"), channel_id) + .set(format!("publish-{unique_key}"), true) + .set(format!("subscribe-{unique_key}"), false) + .set(format!("screensharing-{unique_key}"), false) + .set(format!("camera-{unique_key}"), false) + .query_async(&mut conn.into_inner()) + .await + .to_internal_error()?; let voice_state = UserVoiceState { id: user_id.clone(), - audio: false, - deafened: false, + can_receive: false, + can_publish: false, screensharing: false, - camera: false + camera: false, }; EventV1::VoiceChannelJoin { id: channel_id.clone(), - state: voice_state + state: voice_state, } .p(channel_id.clone()) .await - }, + } "participant_left" => { - let channel_id = channel_id.unwrap(); - let user_id = user_id.unwrap(); + let channel_id = channel_id.to_internal_error()?; + let user_id = user_id.to_internal_error()?; let channel = Reference::from_unchecked(channel_id.clone()) .as_channel(db) .await?; - conn.srem::<_, _, u64>(format!("vc-members-{channel_id}"), user_id).await.unwrap(); - - let unique_key = format!("{}-{user_id}", channel.server().unwrap_or_else(|| channel.id())); - - conn.del::<_, u64>(format!("vc-{unique_key}")).await.unwrap(); - - conn.del::<_, u64>(format!("audio-{unique_key}")).await.unwrap(); - conn.del::<_, u64>(format!("deafened-{unique_key}")).await.unwrap(); - conn.del::<_, u64>(format!("screensharing-{unique_key}")).await.unwrap(); - conn.del::<_, u64>(format!("camera-{unique_key}")).await.unwrap(); + conn.srem::<_, _, u64>(format!("vc-members-{channel_id}"), user_id) + .await + .to_internal_error()?; + + let unique_key = format!( + "{}-{user_id}", + channel.server().unwrap_or_else(|| channel.id()) + ); + + conn.del::<_, Vec>(&[ + format!("vc-{unique_key}"), + format!("audio-{unique_key}"), + format!("deafened-{unique_key}"), + format!("screensharing-{unique_key}"), + format!("camera-{unique_key}"), + ]) + .await + .to_internal_error()?; EventV1::VoiceChannelLeave { id: channel_id.clone(), - user: user_id.clone() + user: user_id.clone(), } .p(channel_id.clone()) .await - }, + } "track_published" | "track_unpublished" => { let value = body.event == "track_published"; // to avoid duplicating this entire case twice - let channel_id = channel_id.unwrap(); - let user_id = user_id.unwrap(); - let track = body.track.as_ref().unwrap(); + let channel_id = channel_id.to_internal_error()?; + let user_id = user_id.to_internal_error()?; + let track = body.track.as_ref().to_internal_error()?; let user = Reference::from_unchecked(user_id.clone()) .as_user(db) @@ -122,52 +133,62 @@ async fn ingress(db: &State, body: Json) -> Result { - PartialUserVoiceState::default() - } - /* TrackSource::Camera */ 1 => { - conn.set::<_, _, String>(format!("camera-{unique_key}"), value).await.unwrap(); + /* TrackSource::Unknown */ 0 => PartialUserVoiceState::default(), + /* TrackSource::Camera */ + 1 => { + conn.set::<_, _, String>(format!("camera-{unique_key}"), value) + .await + .to_internal_error()?; PartialUserVoiceState { camera: Some(value), ..Default::default() } } - /* TrackSource::Microphone */ 2 => { - conn.set::<_, _, String>(format!("audio-{unique_key}"), value).await.unwrap(); + /* TrackSource::Microphone */ + 2 => { + conn.set::<_, _, String>(format!("audio-{unique_key}"), value) + .await + .to_internal_error()?; PartialUserVoiceState { - audio: Some(value), + can_publish: Some(value), ..Default::default() } - }, - /* TrackSource::ScreenShare | TrackSource::ScreenShareAudio */ 3 | 4 => { - conn.set::<_, _, String>(format!("screensharing-{unique_key}"), value).await.unwrap(); + } + /* TrackSource::ScreenShare | TrackSource::ScreenShareAudio */ + 3 | 4 => { + conn.set::<_, _, String>(format!("screensharing-{unique_key}"), value) + .await + .to_internal_error()?; PartialUserVoiceState { screensharing: Some(value), ..Default::default() } - }, - _ => unreachable!() + } + _ => unreachable!(), }; EventV1::UserVoiceStateUpdate { id: user_id.clone(), channel_id: channel_id.clone(), - data: partial + data: partial, } .p(channel_id.clone()) .await; - }, + } _ => {} }; Ok(EmptyResponse) -} \ No newline at end of file +} From 675c7d6c0d807fd9e2a51d17d0714ea75fdf4bb1 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Wed, 24 Apr 2024 02:29:31 +0100 Subject: [PATCH 05/62] fix redis key inconsistancy --- crates/bonfire/src/events/impl.rs | 10 +++++----- crates/voice-ingress/src/main.rs | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index 3f6eb9548..744a99f8f 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -580,10 +580,10 @@ impl State { for id in members { let unique_key = format!("{channel_or_server_id}-{id}"); - let (audio, deafened, screensharing, camera) = conn + let (can_publish, can_receive, screensharing, camera) = conn .mget::<_, (bool, bool, bool, bool)>(&[ - format!("audio-{unique_key}"), - format!("deafened-{unique_key}"), + format!("can_publish-{unique_key}"), + format!("can_receive-{unique_key}"), format!("screensharing-{unique_key}"), format!("camera-{unique_key}"), ]) @@ -592,8 +592,8 @@ impl State { let voice_state = v0::UserVoiceState { id, - can_receive: audio, - can_publish: deafened, + can_receive, + can_publish, screensharing, camera, }; diff --git a/crates/voice-ingress/src/main.rs b/crates/voice-ingress/src/main.rs index d6b0a8131..28147efc9 100644 --- a/crates/voice-ingress/src/main.rs +++ b/crates/voice-ingress/src/main.rs @@ -60,8 +60,8 @@ async fn ingress(db: &State, body: Json) -> Result, body: Json) -> Result, body: Json) -> Result>(&[ format!("vc-{unique_key}"), - format!("audio-{unique_key}"), - format!("deafened-{unique_key}"), + format!("can_publish-{unique_key}"), + format!("can_receive-{unique_key}"), format!("screensharing-{unique_key}"), format!("camera-{unique_key}"), ]) @@ -156,7 +156,7 @@ async fn ingress(db: &State, body: Json) -> Result { - conn.set::<_, _, String>(format!("audio-{unique_key}"), value) + conn.set::<_, _, String>(format!("can_receive-{unique_key}"), value) .await .to_internal_error()?; From ffbc89979299be88251bb1c6601a391254afc571 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Mon, 13 May 2024 14:09:08 +0100 Subject: [PATCH 06/62] split voice ops into its own library --- Cargo.lock | 22 +- crates/bonfire/Cargo.toml | 1 + crates/bonfire/src/events/impl.rs | 48 +--- crates/bonfire/src/main.rs | 15 +- .../database/src/models/channels/model.rs | 11 +- crates/core/models/src/v0/server_members.rs | 9 +- crates/core/permissions/src/models/mod.rs | 2 +- crates/core/result/src/lib.rs | 4 +- crates/core/result/src/rocket.rs | 2 + crates/core/voice/Cargo.toml | 33 +++ crates/core/voice/src/lib.rs | 266 ++++++++++++++++++ crates/delta/Cargo.toml | 1 + crates/delta/src/main.rs | 8 +- .../delta/src/routes/channels/voice_join.rs | 70 +---- .../delta/src/routes/servers/member_edit.rs | 55 +++- crates/voice-ingress/Cargo.toml | 4 +- crates/voice-ingress/src/main.rs | 112 +------- 17 files changed, 446 insertions(+), 217 deletions(-) create mode 100644 crates/core/voice/Cargo.toml create mode 100644 crates/core/voice/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 6df07905a..69e98842e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4016,6 +4016,7 @@ dependencies = [ "revolt-permissions", "revolt-presence", "revolt-result", + "revolt-voice", "rmp-serde", "sentry", "serde", @@ -4118,6 +4119,7 @@ dependencies = [ "revolt-models", "revolt-permissions", "revolt-result", + "revolt-voice", "revolt_rocket_okapi", "rocket", "rocket_authifier", @@ -4188,6 +4190,24 @@ dependencies = [ "serde_json", ] +[[package]] +name = "revolt-voice" +version = "0.1.0" +dependencies = [ + "async-std", + "futures", + "livekit-api", + "livekit-protocol", + "redis-kiss", + "revolt-config", + "revolt-database", + "revolt-models", + "revolt-permissions", + "revolt-result", + "serde", + "serde_json", +] + [[package]] name = "revolt-voice-ingress" version = "0.7.1" @@ -4203,8 +4223,8 @@ dependencies = [ "revolt-database", "revolt-models", "revolt-permissions", - "revolt-presence", "revolt-result", + "revolt-voice", "rmp-serde", "rocket", "rocket_empty", diff --git a/crates/bonfire/Cargo.toml b/crates/bonfire/Cargo.toml index 485ec527a..4e5a2f0a3 100644 --- a/crates/bonfire/Cargo.toml +++ b/crates/bonfire/Cargo.toml @@ -40,6 +40,7 @@ revolt-config = { path = "../core/config" } revolt-database = { path = "../core/database" } revolt-permissions = { version = "0.7.1", path = "../core/permissions" } revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] } +revolt-voice = { path = "../core/voice" } # redis fred = { version = "8.0.1", features = ["subscriber-client"] } diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index 744a99f8f..2f0141653 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -7,9 +7,9 @@ use revolt_database::{ use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_presence::filter_online; -use revolt_result::{create_error, Result}; +use revolt_result::Result; +use revolt_voice::{delete_voice_state, get_voice_channel_members, get_voice_state}; -use redis_kiss::{get_connection, AsyncCommands, Conn}; use super::state::{Cache, State}; @@ -127,9 +127,6 @@ impl State { // Filter server channels by permission. let channels = self.cache.filter_accessible_channels(db, channels).await; - let mut voice_states = Vec::new(); - let mut conn = get_connection().await.unwrap(); - // Append known user IDs from DMs. for channel in &channels { match channel { @@ -202,8 +199,11 @@ impl State { self.insert_subscription(channel.id().to_string()); } + // fetch voice states for all the channels we can see + let mut voice_states = Vec::new(); + for channel in &channels { - if let Ok(Some(voice_state)) = self.fetch_voice_state(&mut conn, channel).await { + if let Ok(Some(voice_state)) = self.fetch_voice_state(channel).await { voice_states.push(voice_state) } } @@ -564,41 +564,21 @@ impl State { async fn fetch_voice_state( &self, - conn: &mut Conn, channel: &Channel, ) -> Result> { - let members = conn - .smembers::<_, Vec>(format!("vc-members-{}", channel.id())) - .await - .map_err(|_| create_error!(InternalError))?; - - let channel_or_server_id = channel.server().unwrap_or_else(|| channel.id()); + let members = get_voice_channel_members(&channel.id()).await?; if !members.is_empty() { let mut participants = Vec::with_capacity(members.len()); - for id in members { - let unique_key = format!("{channel_or_server_id}-{id}"); - - let (can_publish, can_receive, screensharing, camera) = conn - .mget::<_, (bool, bool, bool, bool)>(&[ - format!("can_publish-{unique_key}"), - format!("can_receive-{unique_key}"), - format!("screensharing-{unique_key}"), - format!("camera-{unique_key}"), - ]) - .await - .map_err(|_| create_error!(InternalError))?; - - let voice_state = v0::UserVoiceState { - id, - can_receive, - can_publish, - screensharing, - camera, - }; + for user_id in members { + if let Some(voice_state) = get_voice_state(&channel.id(), channel.server().as_deref(), &user_id).await? { + participants.push(voice_state); + } else { + log::info!("Voice state not found but member in voice channel members, removing."); - participants.push(voice_state); + delete_voice_state(&channel.id(), channel.server().as_deref(), &user_id).await?; + } } Ok(Some(v0::ChannelVoiceState { diff --git a/crates/bonfire/src/main.rs b/crates/bonfire/src/main.rs index 856eb2436..54a2b56ea 100644 --- a/crates/bonfire/src/main.rs +++ b/crates/bonfire/src/main.rs @@ -1,7 +1,9 @@ -use std::env; +use std::{env, sync::Arc}; use async_std::net::TcpListener; use revolt_presence::clear_region; +use once_cell::sync::OnceCell; +use revolt_voice::VoiceClient; #[macro_use] extern crate log; @@ -12,6 +14,15 @@ pub mod events; mod database; mod websocket; +pub static VOICE_CLIENT: OnceCell> = OnceCell::new(); + +pub fn get_voice_client() -> Arc { + VOICE_CLIENT + .get() + .expect("get_voice_client called before set") + .clone() +} + #[async_std::main] async fn main() { // Configure requirements for Bonfire. @@ -21,6 +32,8 @@ async fn main() { // Clean up the current region information. clear_region(None).await; + VOICE_CLIENT.set(Arc::new(VoiceClient::from_revolt_config().await)).unwrap(); + // Setup a TCP listener to accept WebSocket connections on. // By default, we bind to port 9000 on all interfaces. let bind = env::var("HOST").unwrap_or_else(|_| "0.0.0.0:9000".into()); diff --git a/crates/core/database/src/models/channels/model.rs b/crates/core/database/src/models/channels/model.rs index 775999930..c96151fbb 100644 --- a/crates/core/database/src/models/channels/model.rs +++ b/crates/core/database/src/models/channels/model.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{borrow::Cow, collections::HashMap}; use revolt_config::config; use revolt_models::v0::{self, MessageAuthor, VoiceInformation}; @@ -438,6 +438,15 @@ impl Channel { } } + /// Gets this channel's voice information + pub fn voice(&self) -> Option> { + match self { + Self::DirectMessage { .. } | Channel::VoiceChannel { .. } => Some(Cow::Owned(v0::VoiceInformation::default())), + Self::TextChannel { voice: Some(voice), .. } => Some(Cow::Borrowed(voice)), + _ => None + } + } + /// Set role permission on a channel pub async fn set_role_permission( &mut self, diff --git a/crates/core/models/src/v0/server_members.rs b/crates/core/models/src/v0/server_members.rs index a13191436..0ed31fe0a 100644 --- a/crates/core/models/src/v0/server_members.rs +++ b/crates/core/models/src/v0/server_members.rs @@ -131,12 +131,15 @@ auto_derived!( pub roles: Option>, /// Timestamp this member is timed out until pub timeout: Option, - /// Fields to remove from channel object - #[cfg_attr(feature = "validator", validate(length(min = 1)))] - pub remove: Option>, /// server-wide voice muted pub can_publish: Option, /// server-wide voice deafened pub can_receive: Option, + /// voice channel to move to if already in a voice channel + pub voice_channel: Option, + /// Fields to remove from channel object + #[cfg_attr(feature = "validator", validate(length(min = 1)))] + pub remove: Option>, + } ); diff --git a/crates/core/permissions/src/models/mod.rs b/crates/core/permissions/src/models/mod.rs index 330fe7562..b8788230b 100644 --- a/crates/core/permissions/src/models/mod.rs +++ b/crates/core/permissions/src/models/mod.rs @@ -8,7 +8,7 @@ pub use server::*; pub use user::*; /// Holds a permission value to manipulate. -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct PermissionValue(u64); impl PermissionValue { diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index 3733a640d..e3bdb4f26 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -134,7 +134,9 @@ pub enum ErrorType { // ? Voice errors LiveKitUnavailable, - AlreadyInVoiceChannel + AlreadyInVoiceChannel, + NotAVoiceChannel, + AlreadyConnected } #[macro_export] diff --git a/crates/core/result/src/rocket.rs b/crates/core/result/src/rocket.rs index 756da38e4..fd2466dc8 100644 --- a/crates/core/result/src/rocket.rs +++ b/crates/core/result/src/rocket.rs @@ -77,6 +77,8 @@ impl<'r> Responder<'r, 'static> for Error { ErrorType::LiveKitUnavailable => Status::BadRequest, ErrorType::AlreadyInVoiceChannel => Status::BadRequest, + ErrorType::NotAVoiceChannel => Status::BadRequest, + ErrorType::AlreadyConnected => Status::BadRequest }; // Serialize the error data structure into JSON. diff --git a/crates/core/voice/Cargo.toml b/crates/core/voice/Cargo.toml new file mode 100644 index 000000000..029b61932 --- /dev/null +++ b/crates/core/voice/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "revolt-voice" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# voice +livekit-api = "0.3.2" +livekit-protocol = "0.3.2" + +# core +revolt-result = { path = "../result" } +revolt-models = { path = "../models" } +revolt-config = { path = "../config" } +revolt-database = { path = "../database" } +revolt-permissions = { version = "0.7.1", path = "../permissions" } + +# async +futures = "0.3.21" +async-std = { version = "1.8.0", features = [ + "tokio1", + "tokio02", + "attributes", +] } + +# util +redis-kiss = "0.1.4" + +# serde +serde_json = "1.0.79" +serde = "1.0.136" diff --git a/crates/core/voice/src/lib.rs b/crates/core/voice/src/lib.rs new file mode 100644 index 000000000..226364d54 --- /dev/null +++ b/crates/core/voice/src/lib.rs @@ -0,0 +1,266 @@ +use livekit_api::{access_token::{AccessToken, VideoGrants}, services::room::{CreateRoomOptions, RoomClient}}; +use livekit_protocol::Room; +use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; +use revolt_database::{Channel, User}; +use revolt_models::v0::{self, PartialUserVoiceState, UserVoiceState}; +use revolt_permissions::{ChannelPermission, PermissionValue}; +use revolt_result::{Result, ToRevoltError, create_error}; +use std::time::Duration; +use revolt_config::config; + +pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> { + let mut conn = get_connection() + .await + .to_internal_error()?; + + if user.bot.is_some() + // bots can be in as many voice channels as it wants so we just check if its already connected to the one its trying to connect to + && conn.sismember(format!("vc-{}", &user.id), target) + .await + .to_internal_error()? + { + Err(create_error!(AlreadyConnected)) + + } else if conn.scard::<_, u32>(format!("vc-{}", &user.id)) // check if the current vc set is empty + .await + .to_internal_error()? > 0 + { + Err(create_error!(AlreadyInVoiceChannel)) + } else { + Ok(()) + } +} + +pub fn get_allowed_sources(permissions: PermissionValue) -> Vec { + let mut allowed_sources = Vec::new(); + + if permissions.has(ChannelPermission::Speak as u64) { + allowed_sources.push("MICROPHONE".to_string()) + }; + + if permissions.has(ChannelPermission::Video as u64) { + allowed_sources.extend([ + "CAMERA".to_string(), + "SCREEN_SHARE".to_string(), + "SCREEN_SHARE_AUDIO".to_string() + ]); + }; + + allowed_sources +} + +pub async fn create_voice_state(channel_id: &str, server_id: Option<&str>, user_id: &str) -> Result { + let unique_key = format!( + "{}-{}", + &user_id, + server_id.unwrap_or(channel_id) + ); + + let voice_state = UserVoiceState { + id: user_id.to_string(), + can_receive: true, + can_publish: false, + screensharing: false, + camera: false, + }; + + Pipeline::new() + .sadd(format!("vc-members-{channel_id}"), user_id) + .sadd(format!("vc-{user_id}"), channel_id) + .set(format!("can_publish-{unique_key}"), voice_state.can_publish) + .set(format!("can_receive-{unique_key}"), voice_state.can_receive) + .set(format!("screensharing-{unique_key}"), voice_state.screensharing) + .set(format!("camera-{unique_key}"), voice_state.camera) + .query_async(&mut get_connection() + .await + .to_internal_error()? + .into_inner()) + .await + .to_internal_error()?; + + Ok(voice_state) +} + +pub async fn delete_voice_state(channel_id: &str, server_id: Option<&str>, user_id: &str) -> Result<()> { + let unique_key = format!( + "{}-{}", + &user_id, + server_id.unwrap_or(channel_id) + ); + + Pipeline::new() + .srem(format!("vc-members-{channel_id}"), user_id) + .srem(format!("vc-{user_id}"), channel_id) + .del(&[ + format!("can_publish-{unique_key}"), + format!("can_receive-{unique_key}"), + format!("screensharing-{unique_key}"), + format!("camera-{unique_key}"), + ]) + .query_async(&mut get_connection() + .await + .to_internal_error()? + .into_inner()) + .await + .to_internal_error()?; + + Ok(()) +} + +pub async fn update_voice_state_tracks(channel_id: &str, server_id: Option<&str>, user_id: &str, added: bool, track: i32) -> Result { + let partial = match track { + /* TrackSource::Unknown */ 0 => PartialUserVoiceState::default(), + /* TrackSource::Camera */ 1 => { + PartialUserVoiceState { + camera: Some(added), + ..Default::default() + } + } + /* TrackSource::Microphone */ 2 => { + PartialUserVoiceState { + can_publish: Some(added), + ..Default::default() + } + } + /* TrackSource::ScreenShare | TrackSource::ScreenShareAudio */ 3 | 4 => { + PartialUserVoiceState { + screensharing: Some(added), + ..Default::default() + } + } + _ => unreachable!(), + }; + + update_voice_state(channel_id, server_id, user_id, &partial).await?; + + Ok(partial) +} + +pub async fn update_voice_state(channel_id: &str, server_id: Option<&str>, user_id: &str, partial: &PartialUserVoiceState) -> Result<()> { + let unique_key = format!( + "{}-{}", + &user_id, + server_id.unwrap_or(channel_id) + ); + + let mut pipeline = Pipeline::new(); + + if let Some(camera) = &partial.camera { + pipeline.set(format!("camera-{unique_key}"), camera); + }; + + if let Some(can_publish) = &partial.can_publish { + pipeline.set(format!("can_publish-{unique_key}"), can_publish); + } + + if let Some(can_receive) = &partial.can_receive { + pipeline.set(format!("can_receive-{unique_key}"), can_receive); + } + + if let Some(screensharing) = &partial.screensharing { + pipeline.set(format!("screensharing-{unique_key}"), screensharing); + } + + pipeline.query_async(&mut get_connection() + .await + .to_internal_error()? + .into_inner() + ) + .await + .to_internal_error()?; + + Ok(()) +} + +pub async fn get_voice_channel_members(channel_id: &str) -> Result> { + get_connection() + .await + .to_internal_error()? + .smembers::<_, Vec>(format!("vc-members-{}", channel_id)) + .await + .to_internal_error() +} + +pub async fn get_voice_state(channel_id: &str, server_id: Option<&str>, user_id: &str) -> Result> { + let unique_key = format!("{}-{user_id}", server_id.unwrap_or(channel_id)); + + let (can_publish, can_receive, screensharing, camera) = get_connection() + .await + .to_internal_error()? + .mget::<_, (Option, Option, Option, Option)>(&[ + format!("can_publish-{unique_key}"), + format!("can_receive-{unique_key}"), + format!("screensharing-{unique_key}"), + format!("camera-{unique_key}"), + ]) + .await + .to_internal_error()?; + + match (can_publish, can_receive, screensharing, camera) { + (Some(can_publish), Some(can_receive), Some(screensharing), Some(camera)) => { + Ok(Some(v0::UserVoiceState { + id: user_id.to_string(), + can_receive, + can_publish, + screensharing, + camera, + })) + }, + _ => Ok(None) + } +} + +#[derive(Debug)] +pub struct VoiceClient { + rooms: RoomClient, + api_key: String, + api_secret: String +} + +impl VoiceClient { + pub fn new(url: String, api_key: String, api_secret: String) -> Self { + Self { + rooms: RoomClient::with_api_key(&url, &api_key, &api_secret), + api_key, + api_secret + } + } + + pub async fn from_revolt_config() -> Self { + let config = config().await; + + Self::new(config.hosts.livekit, config.api.livekit.key, config.api.livekit.secret) + } + + pub fn create_token(&self, user: &User, permissions: PermissionValue, channel: &Channel) -> Result { + let allowed_sources = get_allowed_sources(permissions); + + AccessToken::with_api_key(&self.api_key, &self.api_secret) + .with_name(&format!("{}#{}", user.username, user.discriminator)) + .with_identity(&user.id) + .with_metadata(&serde_json::to_string(&user).to_internal_error()?) + .with_ttl(Duration::from_secs(10)) + .with_grants(VideoGrants { + room_join: true, + can_publish_sources: allowed_sources, + room: channel.id().to_string(), + ..Default::default() + }) + .to_jwt() + .to_internal_error() + } + + pub async fn create_room(&self, channel: &Channel) -> Result { + let voice = channel + .voice() + .ok_or_else(|| create_error!(NotAVoiceChannel))?; + + self.rooms.create_room(&channel.id(), CreateRoomOptions { + max_participants: voice.max_users.unwrap_or(u32::MAX), + empty_timeout: 5 * 60, // 5 minutes + ..Default::default() + }) + .await + .map_err(|_| create_error!(InternalError)) + } +} \ No newline at end of file diff --git a/crates/delta/Cargo.toml b/crates/delta/Cargo.toml index e8a4b6844..a69c3fcb0 100644 --- a/crates/delta/Cargo.toml +++ b/crates/delta/Cargo.toml @@ -79,6 +79,7 @@ revolt-models = { path = "../core/models", features = [ ] } revolt-result = { path = "../core/result", features = ["rocket", "okapi"] } revolt-permissions = { path = "../core/permissions", features = ["schemas"] } +revolt-voice = { path = "../core/voice" } # voice livekit-api = "0.3.2" diff --git a/crates/delta/src/main.rs b/crates/delta/src/main.rs index f910e660c..7d7b760e0 100644 --- a/crates/delta/src/main.rs +++ b/crates/delta/src/main.rs @@ -99,10 +99,8 @@ pub async fn web() -> Rocket { ) .into(); - - log::info!("{:?}", &config.api.livekit); - - let room_client = RoomClient::with_api_key(&config.api.livekit.url, &config.api.livekit.key, &config.api.livekit.secret); + // Voice handler + let voice_client = revolt_voice::VoiceClient::new(config.api.livekit.url.clone(), config.api.livekit.key.clone(), config.api.livekit.secret.clone()); // Configure Rocket let rocket = rocket::build(); @@ -117,7 +115,7 @@ pub async fn web() -> Rocket { .manage(authifier) .manage(db) .manage(cors.clone()) - .manage(room_client) + .manage(voice_client) .attach(util::ratelimiter::RatelimitFairing) .attach(cors) .configure(rocket::Config { diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index 08330b806..460b362da 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -1,12 +1,9 @@ -use std::borrow::Cow; - -use revolt_config::config; use revolt_models::v0; -use revolt_database::{util::{permissions::perms, reference::Reference}, Channel, Database, User}; +use revolt_database::{util::{permissions::perms, reference::Reference}, Database, User}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; -use revolt_result::{create_error, Result}; +use revolt_result::Result; +use revolt_voice::{VoiceClient, raise_if_in_voice}; -use livekit_api::{access_token::{AccessToken, VideoGrants}, services::room::{CreateRoomOptions, RoomClient}}; use rocket::{serde::json::Json, State}; /// # Join Call @@ -14,69 +11,20 @@ use rocket::{serde::json::Json, State}; /// Asks the voice server for a token to join the call. #[openapi(tag = "Voice")] #[post("//join_call")] -pub async fn call(db: &State, rooms: &State, user: User, target: Reference) -> Result> { +pub async fn call(db: &State, voice: &State, user: User, target: Reference) -> Result> { let channel = target.as_channel(db).await?; + raise_if_in_voice(&user, &channel.id()).await?; + let mut permissions = perms(db, &user).channel(&channel); let current_permissions = calculate_channel_permissions(&mut permissions).await; current_permissions.throw_if_lacking_channel_permission(ChannelPermission::Connect)?; - // TODO - decide if we should allow being in multiple voice channels for users - - // if user.current_voice_channel(&channel.server().unwrap_or_else(|| channel.id())) - // .await? - // .is_some() - // { - // return Err(create_error!(AlreadyInVoiceChannel)) - // } - - let config = config().await; - - if config.api.livekit.url.is_empty() { - return Err(create_error!(LiveKitUnavailable)); - }; - - let voice = match &channel { - Channel::DirectMessage { .. } | Channel::VoiceChannel { .. } => Cow::Owned(v0::VoiceInformation::default()), - Channel::TextChannel { voice: Some(voice), .. } => Cow::Borrowed(voice), - _ => return Err(create_error!(CannotJoinCall)) - }; - - let mut allowed_sources = Vec::new(); - - if current_permissions.has(ChannelPermission::Speak as u64) { - allowed_sources.push("MICROPHONE".to_string()) - }; - - if current_permissions.has(ChannelPermission::Video as u64) { - allowed_sources.extend(["CAMERA".to_string(), "SCREEN_SHARE".to_string(), "SCREEN_SHARE_AUDIO".to_string()]); - }; - - let token = AccessToken::with_api_key(&config.api.livekit.key, &config.api.livekit.secret) - .with_name(&format!("{}#{}", user.username, user.discriminator)) - .with_identity(&user.id) - .with_metadata(&serde_json::to_string(&user).map_err(|_| create_error!(InternalError))?) - .with_grants(VideoGrants { - room_join: true, - can_publish_sources: allowed_sources, - room: channel.id().to_string(), - ..Default::default() - }) - .to_jwt() - .inspect_err(|e| log::error!("{e:?}")) - .map_err(|_| create_error!(InternalError))?; - - let room = rooms.create_room(&channel.id(), CreateRoomOptions { - max_participants: voice.max_users.unwrap_or(u32::MAX), - empty_timeout: 5 * 60, // 5 minutes - ..Default::default() - }) - .await - .inspect_err(|e| log::error!("{e:?}")) - .map_err(|_| create_error!(InternalError))?; + let token = voice.create_token(&user, current_permissions, &channel)?; + let room = voice.create_room(&channel).await?; - log::info!("created room {room:?}"); + log::debug!("created room {}", room.name); Ok(Json(v0::CreateVoiceUserResponse { token })) } diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index 941a36ff3..3506e955f 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -1,17 +1,17 @@ use std::collections::HashSet; +use futures::TryFutureExt; use livekit_api::services::room::{RoomClient, UpdateParticipantOptions}; use livekit_protocol::ParticipantPermission; use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Database, File, PartialMember, User, + events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, File, PartialMember, User }; -use revolt_models::v0; +use revolt_models::v0::{self, PartialUserVoiceState}; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result, ToRevoltError}; -use rocket::{serde::json::Json, State}; +use rocket::{form::validate::Contains, serde::json::Json, State}; use validator::Validate; /// # Edit Member @@ -99,6 +99,25 @@ pub async fn edit( permissions.throw_if_lacking_channel_permission(ChannelPermission::DeafenMembers)?; } + if let Some(new_channel) = &data.voice_channel { + permissions.throw_if_lacking_channel_permission(ChannelPermission::MoveMembers)?; + + // ensure the channel we are moving them to is in the server and is a voice channel + + let channel = Reference::from_unchecked(new_channel.clone()) + .as_channel(db) + .await + .map_err(|_| create_error!(UnknownChannel))?; + + if !channel.server().is_some_and(|v| v == member.id.server) { + Err(create_error!(UnknownChannel))? + } + + if channel.voice().is_none() { + Err(create_error!(NotAVoiceChannel))? + } + } + // Resolve our ranking let our_ranking = query.get_member_rank().unwrap_or(i64::MIN); @@ -136,6 +155,7 @@ pub async fn edit( remove, can_publish, can_receive, + voice_channel } = data; let mut partial = PartialMember { @@ -171,13 +191,15 @@ pub async fn edit( ) .await?; - if can_publish.is_some() || can_receive.is_some() { + if can_publish.is_some() || can_receive.is_some() || voice_channel.is_some() { let mut conn = get_connection().await.to_internal_error()?; + let unique_key = format!("{}-{}", &member.id.user, &member.id.server); + // if we edit the member while they are in a voice channel we need to also update the perms // otherwise it wont take place until they leave and rejoin if let Some(channel) = conn - .get::<_, Option>(format!("vc-{}-{}", &member.id.user, &member.id.server)) + .get::<_, Option>(format!("vc-{}", &unique_key)) .await .to_internal_error()? { @@ -186,7 +208,7 @@ pub async fn edit( if let Some(can_publish) = can_publish { pipeline.set( - format!("can_publish-{}-{}", &channel, &member.id.user), + format!("can_publish-{}", unique_key), can_publish, ); @@ -196,13 +218,18 @@ pub async fn edit( if let Some(can_receive) = can_receive { pipeline.set( - format!("can_receive-{}-{}", &channel, &member.id.user), + format!("can_receive-{}", unique_key), can_receive, ); new_perms.can_subscribe = can_receive; }; + if let Some(new_channel) = voice_channel { + pipeline + .smove(format!("vc-members-{channel}"), format!("vc-members-{new_channel}"), &member.id.user); + }; + pipeline .query_async(&mut conn.into_inner()) .await @@ -220,6 +247,18 @@ pub async fn edit( ) .await .to_internal_error()?; + + EventV1::UserVoiceStateUpdate { + id: member.id.user.clone(), + channel_id: channel.clone(), + data: PartialUserVoiceState { + can_publish, + can_receive, + ..Default::default() + } + } + .p(channel) + .await; }; }; diff --git a/crates/voice-ingress/Cargo.toml b/crates/voice-ingress/Cargo.toml index b220968f3..70fba32cb 100644 --- a/crates/voice-ingress/Cargo.toml +++ b/crates/voice-ingress/Cargo.toml @@ -36,8 +36,8 @@ revolt-result = { path = "../core/result" } revolt-models = { path = "../core/models" } revolt-config = { path = "../core/config" } revolt-database = { path = "../core/database" } -revolt-permissions = { version = "0.7.1", path = "../core/permissions" } -revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] } +revolt-permissions = { path = "../core/permissions" } +revolt-voice = { path = "../core/voice" } # voice livekit-api = "0.3.2" diff --git a/crates/voice-ingress/src/main.rs b/crates/voice-ingress/src/main.rs index 28147efc9..4ff6d3600 100644 --- a/crates/voice-ingress/src/main.rs +++ b/crates/voice-ingress/src/main.rs @@ -1,12 +1,11 @@ use std::env; -use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use livekit_protocol::WebhookEvent; use revolt_database::{ events::client::EventV1, util::reference::Reference, Database, DatabaseInfo, }; -use revolt_models::v0::{PartialUserVoiceState, UserVoiceState}; +use revolt_voice::{create_voice_state, delete_voice_state, update_voice_state_tracks, VoiceClient}; use rocket::{build, post, routes, serde::json::Json, Config, State}; use rocket_empty::EmptyResponse; @@ -17,9 +16,11 @@ async fn main() -> Result<(), rocket::Error> { revolt_config::configure!(voice_ingress); let database = DatabaseInfo::Auto.connect().await.unwrap(); + let voice_client = VoiceClient::from_revolt_config().await; let _rocket = build() .manage(database) + .manage(voice_client) .mount("/", routes![ingress]) .configure(Config { port: 8500, @@ -34,13 +35,10 @@ async fn main() -> Result<(), rocket::Error> { } #[post("/", data = "")] -async fn ingress(db: &State, body: Json) -> Result { - let mut conn = get_connection().await.to_internal_error()?; - +async fn ingress(db: &State, voice_client: &State, body: Json) -> Result { log::debug!("received event: {body:?}"); let channel_id = body.room.as_ref().map(|r| &r.name); - let user_id = body.participant.as_ref().map(|r| &r.identity); match body.event.as_str() { @@ -52,29 +50,7 @@ async fn ingress(db: &State, body: Json) -> Result, body: Json) -> Result(format!("vc-members-{channel_id}"), user_id) - .await - .to_internal_error()?; - - let unique_key = format!( - "{}-{user_id}", - channel.server().unwrap_or_else(|| channel.id()) - ); - - conn.del::<_, Vec>(&[ - format!("vc-{unique_key}"), - format!("can_publish-{unique_key}"), - format!("can_receive-{unique_key}"), - format!("screensharing-{unique_key}"), - format!("camera-{unique_key}"), - ]) - .await - .to_internal_error()?; + delete_voice_state(channel_id, channel.server().as_deref(), user_id).await?; EventV1::VoiceChannelLeave { id: channel_id.clone(), @@ -118,66 +77,21 @@ async fn ingress(db: &State, body: Json) -> Result { - let value = body.event == "track_published"; // to avoid duplicating this entire case twice - let channel_id = channel_id.to_internal_error()?; let user_id = user_id.to_internal_error()?; let track = body.track.as_ref().to_internal_error()?; - let user = Reference::from_unchecked(user_id.clone()) - .as_user(db) - .await?; - let channel = Reference::from_unchecked(channel_id.clone()) .as_channel(db) .await?; - let unique_key = if user.bot.is_some() { - format!( - "{}-{user_id}", - channel.server().unwrap_or_else(|| channel.id()) - ) - } else { - user_id.to_string() - }; - - let partial = match track.source { - /* TrackSource::Unknown */ 0 => PartialUserVoiceState::default(), - /* TrackSource::Camera */ - 1 => { - conn.set::<_, _, String>(format!("camera-{unique_key}"), value) - .await - .to_internal_error()?; - - PartialUserVoiceState { - camera: Some(value), - ..Default::default() - } - } - /* TrackSource::Microphone */ - 2 => { - conn.set::<_, _, String>(format!("can_receive-{unique_key}"), value) - .await - .to_internal_error()?; - - PartialUserVoiceState { - can_publish: Some(value), - ..Default::default() - } - } - /* TrackSource::ScreenShare | TrackSource::ScreenShareAudio */ - 3 | 4 => { - conn.set::<_, _, String>(format!("screensharing-{unique_key}"), value) - .await - .to_internal_error()?; - - PartialUserVoiceState { - screensharing: Some(value), - ..Default::default() - } - } - _ => unreachable!(), - }; + let partial = update_voice_state_tracks( + channel_id, + channel.server().as_deref(), + user_id, + body.event == "track_published", // to avoid duplicating this entire case twice + track.source + ).await?; EventV1::UserVoiceStateUpdate { id: user_id.clone(), From 120ca449b86239ca1fcc0805b96e825cc748de40 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Tue, 23 Jul 2024 04:07:34 +0100 Subject: [PATCH 07/62] feat(livekit): more permission handling --- Cargo.lock | 1 + crates/core/database/src/events/client.rs | 2 +- .../src/models/server_members/model.rs | 16 +++--- .../src/models/server_members/ops/mongodb.rs | 2 + crates/core/database/src/util/bridge/v0.rs | 4 ++ crates/core/database/src/util/permissions.rs | 16 ++++++ crates/core/models/src/v0/server_members.rs | 10 ++-- crates/core/permissions/src/impl.rs | 8 +++ crates/core/permissions/src/models/channel.rs | 5 +- crates/core/permissions/src/test.rs | 32 ++++++++++++ crates/core/permissions/src/trait.rs | 6 +++ crates/core/result/Cargo.toml | 3 ++ crates/core/result/src/lib.rs | 6 ++- crates/core/voice/src/lib.rs | 40 +++++++++++++-- crates/delta/src/routes/root.rs | 4 +- crates/delta/src/routes/servers/ban_create.rs | 10 +++- .../delta/src/routes/servers/member_edit.rs | 49 ++++++++++++------- crates/voice-ingress/src/main.rs | 2 +- 18 files changed, 177 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69e98842e..c32da1943 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4182,6 +4182,7 @@ dependencies = [ name = "revolt-result" version = "0.7.1" dependencies = [ + "log", "revolt_okapi", "revolt_rocket_okapi", "rocket", diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index 473c5cb32..f9222bfe0 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -236,7 +236,7 @@ pub enum EventV1 { UserVoiceStateUpdate { id: String, channel_id: String, - data: PartialUserVoiceState + data: PartialUserVoiceState, } } diff --git a/crates/core/database/src/models/server_members/model.rs b/crates/core/database/src/models/server_members/model.rs index fd068aeeb..fb9c4417c 100644 --- a/crates/core/database/src/models/server_members/model.rs +++ b/crates/core/database/src/models/server_members/model.rs @@ -32,11 +32,11 @@ auto_derived_partial!( pub timeout: Option, /// Whether the member is server-wide voice muted - #[serde(skip_serializing_if = "if_false")] - pub can_publish: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub can_publish: Option, /// Whether the member is server-wide voice deafened - #[serde(skip_serializing_if = "if_false")] - pub can_receive: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub can_receive: Option, }, "PartialMember" ); @@ -57,6 +57,8 @@ auto_derived!( Avatar, Roles, Timeout, + CanReceive, + CanPublish, } /// Member removal intention @@ -76,8 +78,8 @@ impl Default for Member { avatar: None, roles: vec![], timeout: None, - can_publish: false, - can_receive: false, + can_publish: None, + can_receive: None, } } } @@ -199,6 +201,8 @@ impl Member { FieldsMember::Nickname => self.nickname = None, FieldsMember::Roles => self.roles.clear(), FieldsMember::Timeout => self.timeout = None, + FieldsMember::CanReceive => self.can_receive = None, + FieldsMember::CanPublish => self.can_publish = None } } diff --git a/crates/core/database/src/models/server_members/ops/mongodb.rs b/crates/core/database/src/models/server_members/ops/mongodb.rs index 8ab526790..1c5116906 100644 --- a/crates/core/database/src/models/server_members/ops/mongodb.rs +++ b/crates/core/database/src/models/server_members/ops/mongodb.rs @@ -173,6 +173,8 @@ impl IntoDocumentPath for FieldsMember { FieldsMember::Nickname => "nickname", FieldsMember::Roles => "roles", FieldsMember::Timeout => "timeout", + FieldsMember::CanPublish => "can_publish", + FieldsMember::CanReceive => "can_receive" }) } } diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index 042f10c8f..de7505bef 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -690,6 +690,8 @@ impl From for FieldsMember { crate::FieldsMember::Nickname => FieldsMember::Nickname, crate::FieldsMember::Roles => FieldsMember::Roles, crate::FieldsMember::Timeout => FieldsMember::Timeout, + crate::FieldsMember::CanReceive => FieldsMember::CanReceive, + crate::FieldsMember::CanPublish => FieldsMember::CanPublish, } } } @@ -701,6 +703,8 @@ impl From for crate::FieldsMember { FieldsMember::Nickname => crate::FieldsMember::Nickname, FieldsMember::Roles => crate::FieldsMember::Roles, FieldsMember::Timeout => crate::FieldsMember::Timeout, + FieldsMember::CanReceive => crate::FieldsMember::CanReceive, + FieldsMember::CanPublish => crate::FieldsMember::CanPublish, } } } diff --git a/crates/core/database/src/util/permissions.rs b/crates/core/database/src/util/permissions.rs index 3adb2ec34..08fe3a9eb 100644 --- a/crates/core/database/src/util/permissions.rs +++ b/crates/core/database/src/util/permissions.rs @@ -179,6 +179,22 @@ impl PermissionQuery for DatabasePermissionQuery<'_> { } } + async fn do_we_have_publish_overwrites(&mut self) -> bool { + if let Some(member) = &self.member { + member.can_publish.unwrap_or(true) + } else { + false + } + } + + async fn do_we_have_receive_overwrites(&mut self) -> bool { + if let Some(member) = &self.member { + member.can_receive.unwrap_or(true) + } else { + false + } + } + // * For calculating channel permission /// Get the type of the channel diff --git a/crates/core/models/src/v0/server_members.rs b/crates/core/models/src/v0/server_members.rs index 0ed31fe0a..d1cbe9651 100644 --- a/crates/core/models/src/v0/server_members.rs +++ b/crates/core/models/src/v0/server_members.rs @@ -60,11 +60,11 @@ auto_derived_partial!( pub timeout: Option, /// Whether the member is server-wide voice muted - #[serde(skip_serializing_if = "if_false")] - pub can_publish: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub can_publish: Option, /// Whether the member is server-wide voice deafened - #[serde(skip_serializing_if = "if_false")] - pub can_receive: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub can_receive: Option, }, "PartialMember" ); @@ -85,6 +85,8 @@ auto_derived!( Avatar, Roles, Timeout, + CanReceive, + CanPublish, } /// Member removal intention diff --git a/crates/core/permissions/src/impl.rs b/crates/core/permissions/src/impl.rs index 9975eeda5..f175d6bd4 100644 --- a/crates/core/permissions/src/impl.rs +++ b/crates/core/permissions/src/impl.rs @@ -61,6 +61,14 @@ pub async fn calculate_server_permissions(query: &mut P) -> permissions.apply(role_override); } + if !query.do_we_have_publish_overwrites().await { + permissions.revoke(ChannelPermission::Speak as u64); + } + + if !query.do_we_have_receive_overwrites().await { + permissions.revoke(ChannelPermission::Listen as u64); + } + if query.are_we_timed_out().await { permissions.restrict(*ALLOW_IN_TIMEOUT); } diff --git a/crates/core/permissions/src/models/channel.rs b/crates/core/permissions/src/models/channel.rs index 17762b1ed..0b2867ae8 100644 --- a/crates/core/permissions/src/models/channel.rs +++ b/crates/core/permissions/src/models/channel.rs @@ -89,6 +89,8 @@ pub enum ChannelPermission { DeafenMembers = 1 << 34, /// Move members between voice channels MoveMembers = 1 << 35, + /// Listen to other users + Listen = 1 << 36, // * Misc. permissions // % Bits 36 to 52: free area @@ -124,7 +126,8 @@ pub static DEFAULT_PERMISSION: Lazy = Lazy::new(|| { + ChannelPermission::SendEmbeds + ChannelPermission::UploadFiles + ChannelPermission::Connect - + ChannelPermission::Speak, + + ChannelPermission::Speak + + ChannelPermission::Listen ) }); diff --git a/crates/core/permissions/src/test.rs b/crates/core/permissions/src/test.rs index e4dadd485..93e1dea2c 100644 --- a/crates/core/permissions/src/test.rs +++ b/crates/core/permissions/src/test.rs @@ -64,6 +64,14 @@ async fn validate_user_permissions() { unreachable!() } + async fn do_we_have_publish_overwrites(&mut self) -> bool { + true + } + + async fn do_we_have_receive_overwrites(&mut self) -> bool { + true + } + async fn get_channel_type(&mut self) -> ChannelType { ChannelType::DirectMessage } @@ -153,6 +161,14 @@ async fn validate_group_permissions() { unreachable!() } + async fn do_we_have_publish_overwrites(&mut self) -> bool { + true + } + + async fn do_we_have_receive_overwrites(&mut self) -> bool { + true + } + async fn get_channel_type(&mut self) -> ChannelType { ChannelType::Group } @@ -254,6 +270,14 @@ async fn validate_server_permissions() { false } + async fn do_we_have_publish_overwrites(&mut self) -> bool { + true + } + + async fn do_we_have_receive_overwrites(&mut self) -> bool { + true + } + async fn get_channel_type(&mut self) -> ChannelType { ChannelType::ServerChannel } @@ -346,6 +370,14 @@ async fn validate_timed_out_member() { true } + async fn do_we_have_publish_overwrites(&mut self) -> bool { + true + } + + async fn do_we_have_receive_overwrites(&mut self) -> bool { + true + } + async fn get_channel_type(&mut self) -> ChannelType { ChannelType::ServerChannel } diff --git a/crates/core/permissions/src/trait.rs b/crates/core/permissions/src/trait.rs index d4a7c3d36..96b42caf8 100644 --- a/crates/core/permissions/src/trait.rs +++ b/crates/core/permissions/src/trait.rs @@ -39,6 +39,12 @@ pub trait PermissionQuery { /// Is our perspective user timed out on this server? async fn are_we_timed_out(&mut self) -> bool; + /// Is the member muted? + async fn do_we_have_publish_overwrites(&mut self) -> bool; + + /// Is the member deafend? + async fn do_we_have_receive_overwrites(&mut self) -> bool; + // * For calculating channel permission /// Get the type of the channel diff --git a/crates/core/result/Cargo.toml b/crates/core/result/Cargo.toml index c7f1ec042..fae07bd6f 100644 --- a/crates/core/result/Cargo.toml +++ b/crates/core/result/Cargo.toml @@ -28,3 +28,6 @@ schemars = { version = "0.8.8", optional = true } rocket = { optional = true, version = "0.5.0-rc.2", default-features = false } revolt_rocket_okapi = { version = "0.9.1", optional = true } revolt_okapi = { version = "0.9.1", optional = true } + +# utilities +log = "0.4" \ No newline at end of file diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index e3bdb4f26..713f5a18b 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -181,9 +181,11 @@ pub trait ToRevoltError { fn to_internal_error(self) -> Result; } -impl ToRevoltError for Result { +impl ToRevoltError for Result { fn to_internal_error(self) -> Result { - self.map_err(|_| { + self + .inspect_err(|e| log::error!("{e:?}")) + .map_err(|_| { let loc = Location::caller(); Error { diff --git a/crates/core/voice/src/lib.rs b/crates/core/voice/src/lib.rs index 226364d54..0e0c714b3 100644 --- a/crates/core/voice/src/lib.rs +++ b/crates/core/voice/src/lib.rs @@ -1,5 +1,5 @@ -use livekit_api::{access_token::{AccessToken, VideoGrants}, services::room::{CreateRoomOptions, RoomClient}}; -use livekit_protocol::Room; +use livekit_api::{access_token::{AccessToken, VideoGrants}, services::room::{CreateRoomOptions, RoomClient, UpdateParticipantOptions}}; +use livekit_protocol::{ParticipantInfo, ParticipantPermission, Room}; use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use revolt_database::{Channel, User}; use revolt_models::v0::{self, PartialUserVoiceState, UserVoiceState}; @@ -31,6 +31,22 @@ pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> { } } +pub async fn get_user_voice_channel_in_server(user_id: &str, server_id: &str) -> Result> { + let mut conn = get_connection() + .await + .to_internal_error()?; + + let unique_key = format!( + "{}-{}", + user_id, + server_id + ); + + conn.get::<&str, Option>(&unique_key) + .await + .to_internal_error() +} + pub fn get_allowed_sources(permissions: PermissionValue) -> Vec { let mut allowed_sources = Vec::new(); @@ -67,6 +83,7 @@ pub async fn create_voice_state(channel_id: &str, server_id: Option<&str>, user_ Pipeline::new() .sadd(format!("vc-members-{channel_id}"), user_id) .sadd(format!("vc-{user_id}"), channel_id) + .set(&unique_key, channel_id) .set(format!("can_publish-{unique_key}"), voice_state.can_publish) .set(format!("can_receive-{unique_key}"), voice_state.can_receive) .set(format!("screensharing-{unique_key}"), voice_state.screensharing) @@ -96,6 +113,7 @@ pub async fn delete_voice_state(channel_id: &str, server_id: Option<&str>, user_ format!("can_receive-{unique_key}"), format!("screensharing-{unique_key}"), format!("camera-{unique_key}"), + unique_key.clone(), ]) .query_async(&mut get_connection() .await @@ -243,6 +261,7 @@ impl VoiceClient { .with_grants(VideoGrants { room_join: true, can_publish_sources: allowed_sources, + can_subscribe: permissions.has_channel_permission(ChannelPermission::Listen), room: channel.id().to_string(), ..Default::default() }) @@ -261,6 +280,21 @@ impl VoiceClient { ..Default::default() }) .await - .map_err(|_| create_error!(InternalError)) + .to_internal_error() + } + + pub async fn update_permissions(&self, user: &User, channel_id: &str, new_permissions: ParticipantPermission) -> Result { + self.rooms + .update_participant( + channel_id, + &user.id, + UpdateParticipantOptions { + permission: Some(new_permissions), + name: "".to_string(), + metadata: "".to_string(), + }, + ) + .await + .to_internal_error() } } \ No newline at end of file diff --git a/crates/delta/src/routes/root.rs b/crates/delta/src/routes/root.rs index d8677f8b4..63a731fa0 100644 --- a/crates/delta/src/routes/root.rs +++ b/crates/delta/src/routes/root.rs @@ -105,8 +105,8 @@ pub async fn root() -> Result> { url: config.hosts.january, }, livekit: VoiceFeature { - enabled: !config.api.livekit.url.is_empty(), - url: config.api.livekit.url.to_string(), + enabled: !config.hosts.livekit.is_empty(), + url: config.hosts.livekit.to_string(), }, }, ws: config.hosts.events, diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index 8003e79ea..09666ac48 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -5,11 +5,11 @@ use revolt_database::{ use revolt_models::v0; use revolt_permissions::{ - calculate_channel_permissions, calculate_server_permissions, ChannelPermission, + calculate_server_permissions, ChannelPermission, }; use revolt_result::{create_error, Result}; +use revolt_voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}; use rocket::{serde::json::Json, State}; -use serde::{Deserialize, Serialize}; use validator::Validate; /// # Ban User @@ -19,6 +19,7 @@ use validator::Validate; #[put("//bans/", data = "")] pub async fn ban( db: &State, + voice: &State, user: User, server: Reference, target: Reference, @@ -57,6 +58,11 @@ pub async fn ban( member .remove(db, &server, RemovalIntention::Ban, false) .await?; + + // If the member is in a voice channel while banned kick them from the voice channel + if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { + delete_voice_state(&channel_id, Some(&server.id), &user.id).await? + } } ServerBan::create(db, &server, &target.id, data.reason) diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index 3506e955f..218eddbc7 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -7,10 +7,11 @@ use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use revolt_database::{ events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, File, PartialMember, User }; -use revolt_models::v0::{self, PartialUserVoiceState}; +use revolt_models::v0::{self, FieldsMember, PartialUserVoiceState}; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result, ToRevoltError}; +use revolt_voice::VoiceClient; use rocket::{form::validate::Contains, serde::json::Json, State}; use validator::Validate; @@ -20,7 +21,7 @@ use validator::Validate; #[openapi(tag = "Server Members")] #[patch("//members/", data = "")] pub async fn edit( - room_client: &State, + voice_client: &State, db: &State, user: User, server: Reference, @@ -37,6 +38,7 @@ pub async fn edit( // Fetch server, target member and current permissions let mut server = server.as_server(db).await?; let mut member = target.as_member(db, &server.id).await?; + let target_user = target.as_user(db).await?; let mut query = DatabasePermissionQuery::new(db, &user) .server(&server) .member(&member); @@ -153,8 +155,8 @@ pub async fn edit( roles, timeout, remove, - can_publish, - can_receive, + mut can_publish, + mut can_receive, voice_channel } = data; @@ -181,6 +183,10 @@ pub async fn edit( partial.avatar = Some(File::use_avatar(db, &avatar, &user.id).await?); } + let remove_contains_voice = remove + .as_ref() + .map(|r| r.contains(FieldsMember::CanPublish) || r.contains(FieldsMember::CanReceive)).unwrap_or_default(); + member .update( db, @@ -191,7 +197,11 @@ pub async fn edit( ) .await?; - if can_publish.is_some() || can_receive.is_some() || voice_channel.is_some() { + if can_publish.is_some() || + can_receive.is_some() || + voice_channel.is_some() || + remove_contains_voice + { let mut conn = get_connection().await.to_internal_error()?; let unique_key = format!("{}-{}", &member.id.user, &member.id.server); @@ -206,6 +216,22 @@ pub async fn edit( let mut pipeline = Pipeline::new(); let mut new_perms = ParticipantPermission::default(); + if remove_contains_voice { + let mut query = DatabasePermissionQuery::new(db, &target_user) + .server(&server) + .member(&member); + + let permissions = calculate_server_permissions(&mut query).await; + + if !permissions.has_channel_permission(ChannelPermission::Speak) { + can_publish = Some(false) + } + + if !permissions.has_channel_permission(ChannelPermission::Listen) { + can_receive = Some(false) + } + } + if let Some(can_publish) = can_publish { pipeline.set( format!("can_publish-{}", unique_key), @@ -235,18 +261,7 @@ pub async fn edit( .await .to_internal_error()?; - room_client - .update_participant( - &channel, - &member.id.user, - UpdateParticipantOptions { - permission: Some(new_perms), - name: "".to_string(), - metadata: "".to_string(), - }, - ) - .await - .to_internal_error()?; + voice_client.update_permissions(&user, &channel, new_perms).await?; EventV1::UserVoiceStateUpdate { id: member.id.user.clone(), diff --git a/crates/voice-ingress/src/main.rs b/crates/voice-ingress/src/main.rs index 4ff6d3600..f4c9f8770 100644 --- a/crates/voice-ingress/src/main.rs +++ b/crates/voice-ingress/src/main.rs @@ -96,7 +96,7 @@ async fn ingress(db: &State, voice_client: &State, body: EventV1::UserVoiceStateUpdate { id: user_id.clone(), channel_id: channel_id.clone(), - data: partial, + data: partial } .p(channel_id.clone()) .await; From 2f7294705e228f8454b6ac6bc168cb0ae9ce13bb Mon Sep 17 00:00:00 2001 From: Zomatree Date: Sun, 13 Oct 2024 20:23:00 +0100 Subject: [PATCH 08/62] feat(livekit): push unfinished code --- .../delta/src/routes/servers/roles_delete.rs | 46 +++++++++++++++++-- crates/delta/src/routes/servers/roles_edit.rs | 42 +++++++++++++++-- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/crates/delta/src/routes/servers/roles_delete.rs b/crates/delta/src/routes/servers/roles_delete.rs index 9063dae24..adb302568 100644 --- a/crates/delta/src/routes/servers/roles_delete.rs +++ b/crates/delta/src/routes/servers/roles_delete.rs @@ -1,9 +1,10 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Database, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, Channel, Database, User }; -use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_models::v0::PartialUserVoiceState; +use revolt_permissions::{calculate_channel_permissions, calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; +use revolt_voice::{get_allowed_sources, get_voice_channel_members, get_voice_state}; use rocket::State; use rocket_empty::EmptyResponse; @@ -31,6 +32,45 @@ pub async fn delete( return Err(create_error!(NotElevated)); } + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; + + if matches!(channel, Channel::VoiceChannel { .. }) { + for member_id in get_voice_channel_members(channel_id).await? { + let member = Reference::from_unchecked(member_id).as_member(db, &server.id).await?; + + if member.roles.contains(&role_id) { + let user = Reference::from_unchecked(member.id.user.clone()).as_user(db).await?; + let voice_state = get_voice_state(channel_id, Some(&server.id), &user.id).await?.unwrap(); + + let mut query = DatabasePermissionQuery::new(db, &user) + .member(&member) + .channel(&channel) + .server(&server); + + let permissions = calculate_channel_permissions(&mut query).await; + + let sources = get_allowed_sources(permissions); + + let mut update_event = PartialUserVoiceState { + id: Some(user.id.clone()), + ..Default::default() + }; + + if !sources.contains(&"CAMERA".to_string()) { + update_event.camera = + update_event + } + + if voice_state.camera && !sources.contains(&"MICROPHONE".to_string()) { + update_event. = Some(false); + } + + } + } + } + }; + role.delete(db, &server.id, &role_id) .await .map(|_| EmptyResponse) diff --git a/crates/delta/src/routes/servers/roles_edit.rs b/crates/delta/src/routes/servers/roles_edit.rs index c7f5124ce..a63206e65 100644 --- a/crates/delta/src/routes/servers/roles_edit.rs +++ b/crates/delta/src/routes/servers/roles_edit.rs @@ -1,10 +1,10 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Database, PartialRole, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, Channel, Database, PartialRole, User }; -use revolt_models::v0; -use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_models::v0::{self, PartialUserVoiceState}; +use revolt_permissions::{calculate_channel_permissions, calculate_server_permissions, ChannelPermission, PermissionQuery}; use revolt_result::{create_error, Result}; +use revolt_voice::{get_allowed_sources, get_voice_channel_members, get_voice_state, update_voice_state_tracks, VoiceClient}; use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; use validator::Validate; @@ -16,6 +16,7 @@ use validator::Validate; #[patch("//roles/", data = "")] pub async fn edit( db: &State, + voice: &State, user: User, target: Reference, role_id: String, @@ -76,6 +77,39 @@ pub async fn edit( ) .await?; + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; + + if matches!(channel, Channel::VoiceChannel { .. }) { + for member_id in get_voice_channel_members(channel_id).await? { + let member = Reference::from_unchecked(member_id).as_member(db, &server.id).await?; + + if member.roles.contains(&role_id) { + let user = Reference::from_unchecked(member.id.user.clone()).as_user(db).await?; + let voice_state = get_voice_state(channel_id, Some(&server.id), &user.id).await?.unwrap(); + + let mut query = DatabasePermissionQuery::new(db, &user) + .member(&member) + .channel(&channel) + .server(&server); + + let permissions = calculate_channel_permissions(&mut query).await; + + let sources = get_allowed_sources(permissions); + + let mut update_event = PartialUserVoiceState { + id: Some(user.id.clone()), + ..Default::default() + }; + + if voice_state.camera && !sources.contains(&"CAMERA".to_string()) { + update_event.camera = Some(false); + } + } + } + } + }; + Ok(Json(role.into())) } else { Err(create_error!(NotFound)) From c55b5bf75f08e2aa4dd13f6211962bf2c3384c21 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Mon, 27 Jan 2025 20:46:29 +0000 Subject: [PATCH 09/62] fix: include voice states in servercreate event feat: call started system message in dms --- Cargo.lock | 24 +-- crates/bonfire/Cargo.toml | 1 - crates/bonfire/src/config.rs | 1 + crates/bonfire/src/events/impl.rs | 33 +--- crates/bonfire/src/main.rs | 2 +- crates/core/database/Cargo.toml | 5 + crates/core/database/src/events/client.rs | 1 + crates/core/database/src/lib.rs | 3 + .../database/src/models/messages/model.rs | 5 + .../src/models/server_members/model.rs | 10 + crates/core/database/src/util/bridge/v0.rs | 1 + .../src/lib.rs => database/src/voice/mod.rs} | 173 ++++++------------ .../core/database/src/voice/voice_client.rs | 108 +++++++++++ crates/core/models/src/v0/messages.rs | 3 + crates/core/result/src/lib.rs | 2 + crates/core/voice/Cargo.toml | 33 ---- crates/daemons/voice-ingress/Cargo.toml | 1 - crates/daemons/voice-ingress/src/main.rs | 2 +- crates/delta/Cargo.toml | 1 - crates/delta/src/main.rs | 4 +- .../delta/src/routes/channels/voice_join.rs | 30 ++- crates/delta/src/routes/servers/ban_create.rs | 2 +- .../delta/src/routes/servers/member_edit.rs | 2 +- .../delta/src/routes/servers/roles_delete.rs | 5 +- crates/delta/src/routes/servers/roles_edit.rs | 5 +- 25 files changed, 237 insertions(+), 220 deletions(-) rename crates/core/{voice/src/lib.rs => database/src/voice/mod.rs} (53%) create mode 100644 crates/core/database/src/voice/voice_client.rs delete mode 100644 crates/core/voice/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 51db43a61..9db589c30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6268,7 +6268,6 @@ dependencies = [ "revolt-permissions", "revolt-presence", "revolt-result", - "revolt-voice", "rmp-serde", "sentry", "serde", @@ -6313,6 +6312,9 @@ dependencies = [ "isahc", "iso8601-timestamp 0.2.17", "linkify 0.8.1", + "livekit-api", + "livekit-protocol", + "livekit-runtime", "log", "lru 0.11.1", "mongodb", @@ -6375,7 +6377,6 @@ dependencies = [ "revolt-permissions", "revolt-presence", "revolt-result", - "revolt-voice", "revolt_rocket_okapi", "rocket", "rocket_authifier", @@ -6538,24 +6539,6 @@ dependencies = [ "utoipa", ] -[[package]] -name = "revolt-voice" -version = "0.1.0" -dependencies = [ - "async-std", - "futures", - "livekit-api", - "livekit-protocol", - "redis-kiss", - "revolt-config", - "revolt-database", - "revolt-models", - "revolt-permissions", - "revolt-result", - "serde", - "serde_json", -] - [[package]] name = "revolt-voice-ingress" version = "0.7.1" @@ -6573,7 +6556,6 @@ dependencies = [ "revolt-models", "revolt-permissions", "revolt-result", - "revolt-voice", "rmp-serde", "rocket", "rocket_empty", diff --git a/crates/bonfire/Cargo.toml b/crates/bonfire/Cargo.toml index 64c6507de..86627e3cb 100644 --- a/crates/bonfire/Cargo.toml +++ b/crates/bonfire/Cargo.toml @@ -43,7 +43,6 @@ revolt-config = { path = "../core/config" } revolt-database = { path = "../core/database" } revolt-permissions = { version = "0.8.1", path = "../core/permissions" } revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] } -revolt-voice = { path = "../core/voice" } # redis fred = { version = "8.0.1", features = ["subscriber-client"] } diff --git a/crates/bonfire/src/config.rs b/crates/bonfire/src/config.rs index 5642d3baa..04cfd5ca6 100644 --- a/crates/bonfire/src/config.rs +++ b/crates/bonfire/src/config.rs @@ -93,6 +93,7 @@ impl ProtocolConfiguration { ReadyPayloadFields::Channels, ReadyPayloadFields::Members, ReadyPayloadFields::Emoji, + ReadyPayloadFields::VoiceStates, ] } } diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index 714d9a8d7..5c974fb65 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -3,13 +3,13 @@ use std::collections::HashSet; use revolt_database::{ events::client::{EventV1, ReadyPayloadFields}, util::permissions::DatabasePermissionQuery, + voice::{delete_voice_state, get_voice_channel_members, get_voice_state, get_channel_voice_state}, Channel, Database, Member, MemberCompositeKey, Presence, RelationshipStatus, }; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_presence::filter_online; use revolt_result::Result; -use revolt_voice::{delete_voice_state, get_voice_channel_members, get_voice_state}; use super::state::{Cache, State}; @@ -236,7 +236,7 @@ impl State { let mut voice_states = Vec::new(); for channel in &channels { - if let Ok(Some(voice_state)) = self.fetch_voice_state(channel).await { + if let Ok(Some(voice_state)) = get_channel_voice_state(channel).await { voice_states.push(voice_state) } } @@ -464,6 +464,7 @@ impl State { server, channels, emojis: _, + voice_states: _, } => { self.insert_subscription(id.clone()).await; @@ -637,32 +638,4 @@ impl State { true } - - async fn fetch_voice_state( - &self, - channel: &Channel, - ) -> Result> { - let members = get_voice_channel_members(&channel.id()).await?; - - if !members.is_empty() { - let mut participants = Vec::with_capacity(members.len()); - - for user_id in members { - if let Some(voice_state) = get_voice_state(&channel.id(), channel.server().as_deref(), &user_id).await? { - participants.push(voice_state); - } else { - log::info!("Voice state not found but member in voice channel members, removing."); - - delete_voice_state(&channel.id(), channel.server().as_deref(), &user_id).await?; - } - } - - Ok(Some(v0::ChannelVoiceState { - id: channel.id().to_string(), - participants, - })) - } else { - Ok(None) - } - } } diff --git a/crates/bonfire/src/main.rs b/crates/bonfire/src/main.rs index 2cfeac921..3f68e4729 100644 --- a/crates/bonfire/src/main.rs +++ b/crates/bonfire/src/main.rs @@ -3,7 +3,7 @@ use std::{env, sync::Arc}; use async_std::net::TcpListener; use revolt_presence::clear_region; use once_cell::sync::OnceCell; -use revolt_voice::VoiceClient; +use revolt_database::voice::VoiceClient; #[macro_use] extern crate log; diff --git a/crates/core/database/Cargo.toml b/crates/core/database/Cargo.toml index ba3de99ac..329217ffa 100644 --- a/crates/core/database/Cargo.toml +++ b/crates/core/database/Cargo.toml @@ -100,3 +100,8 @@ authifier = { version = "1.0.9", features = ["rocket_impl"] } # RabbitMQ amqprs = { version = "1.7.0" } + +# Voice +livekit-api = "0.4.1" +livekit-protocol = "0.3.6" +livekit-runtime = { version = "0.3.1", features = ["tokio"] } \ No newline at end of file diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index 9262d9996..6262f9da6 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -120,6 +120,7 @@ pub enum EventV1 { server: Server, channels: Vec, emojis: Vec, + voice_states: Vec }, /// Update existing server diff --git a/crates/core/database/src/lib.rs b/crates/core/database/src/lib.rs index 9e26336d1..8889f196c 100644 --- a/crates/core/database/src/lib.rs +++ b/crates/core/database/src/lib.rs @@ -108,6 +108,9 @@ pub mod tasks; mod amqp; pub use amqp::amqp::AMQP; +pub mod voice; + + /// Utility function to check if a boolean value is false pub fn if_false(t: &bool) -> bool { !t diff --git a/crates/core/database/src/models/messages/model.rs b/crates/core/database/src/models/messages/model.rs index 725d46d1d..d4991ed42 100644 --- a/crates/core/database/src/models/messages/model.rs +++ b/crates/core/database/src/models/messages/model.rs @@ -106,6 +106,8 @@ auto_derived!( MessagePinned { id: String, by: String }, #[serde(rename = "message_unpinned")] MessageUnpinned { id: String, by: String }, + #[serde(rename = "call_started")] + CallStarted { by: String }, } /// Name and / or avatar override information @@ -658,6 +660,9 @@ impl Message { v0::SystemMessage::MessageUnpinned { by, .. } => { users.push(by.clone()); } + v0::SystemMessage::CallStarted { by } => { + users.push(by.clone()) + } } } users diff --git a/crates/core/database/src/models/server_members/model.rs b/crates/core/database/src/models/server_members/model.rs index 0728c30a8..da33644f3 100644 --- a/crates/core/database/src/models/server_members/model.rs +++ b/crates/core/database/src/models/server_members/model.rs @@ -1,6 +1,7 @@ use iso8601_timestamp::Timestamp; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; +use crate::voice::get_channel_voice_state; use crate::{ events::client::EventV1, if_false, util::permissions::DatabasePermissionQuery, Channel, @@ -132,6 +133,14 @@ impl Member { let emojis = db.fetch_emoji_by_parent_id(&server.id).await?; + let mut voice_states = Vec::new(); + + for channel in &channels { + if let Ok(Some(voice_state)) = get_channel_voice_state(channel).await { + voice_states.push(voice_state) + } + } + EventV1::ServerMemberJoin { id: server.id.clone(), user: user.id.clone(), @@ -148,6 +157,7 @@ impl Member { .map(|channel| channel.into()) .collect(), emojis: emojis.into_iter().map(|emoji| emoji.into()).collect(), + voice_states } .private(user.id.clone()) .await; diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index c04af1554..1ad07a927 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -545,6 +545,7 @@ impl From for SystemMessage { crate::SystemMessage::UserRemove { id, by } => Self::UserRemove { id, by }, crate::SystemMessage::MessagePinned { id, by } => Self::MessagePinned { id, by }, crate::SystemMessage::MessageUnpinned { id, by } => Self::MessageUnpinned { id, by }, + crate::SystemMessage::CallStarted { by } => Self::CallStarted { by } } } } diff --git a/crates/core/voice/src/lib.rs b/crates/core/database/src/voice/mod.rs similarity index 53% rename from crates/core/voice/src/lib.rs rename to crates/core/database/src/voice/mod.rs index c5bfc0f8b..2e59ca335 100644 --- a/crates/core/voice/src/lib.rs +++ b/crates/core/database/src/voice/mod.rs @@ -1,28 +1,25 @@ -use livekit_api::{ - access_token::{AccessToken, VideoGrants}, - services::room::{CreateRoomOptions, RoomClient, UpdateParticipantOptions}, -}; -use livekit_protocol::{ParticipantInfo, ParticipantPermission, Room}; + use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; -use revolt_config::config; -use revolt_database::{Channel, User}; +use crate::models::{Channel, User}; use revolt_models::v0::{self, PartialUserVoiceState, UserVoiceState}; use revolt_permissions::{ChannelPermission, PermissionValue}; use revolt_result::{create_error, Result, ToRevoltError}; -use std::{collections::HashMap, time::Duration}; + +mod voice_client; +pub use voice_client::VoiceClient; pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> { let mut conn = get_connection().await.to_internal_error()?; if user.bot.is_some() // bots can be in as many voice channels as it wants so we just check if its already connected to the one its trying to connect to - && conn.sismember(format!("vc-{}", &user.id), target) + && conn.sismember(format!("vc:{}", &user.id), target) .await .to_internal_error()? { Err(create_error!(AlreadyConnected)) } else if conn - .scard::<_, u32>(format!("vc-{}", &user.id)) // check if the current vc set is empty + .scard::<_, u32>(format!("vc:{}", &user.id)) // check if the current vc set is empty .await .to_internal_error()? > 0 @@ -39,7 +36,7 @@ pub async fn get_user_voice_channel_in_server( ) -> Result> { let mut conn = get_connection().await.to_internal_error()?; - let unique_key = format!("{}-{}", user_id, server_id); + let unique_key = format!("{}:{}", user_id, server_id); conn.get::<&str, Option>(&unique_key) .await @@ -65,7 +62,7 @@ pub async fn create_voice_state( server_id: Option<&str>, user_id: &str, ) -> Result { - let unique_key = format!("{}-{}", &user_id, server_id.unwrap_or(channel_id)); + let unique_key = format!("{}:{}", &user_id, server_id.unwrap_or(channel_id)); let voice_state = UserVoiceState { id: user_id.to_string(), @@ -76,22 +73,22 @@ pub async fn create_voice_state( }; Pipeline::new() - .sadd(format!("vc-members-{channel_id}"), user_id) - .sadd(format!("vc-{user_id}"), channel_id) + .sadd(format!("vc_members:{channel_id}"), user_id) + .sadd(format!("vc:{user_id}"), channel_id) .set(&unique_key, channel_id) .set( - format!("is_publishing-{unique_key}"), + format!("is_publishing:{unique_key}"), voice_state.is_publishing, ) .set( - format!("is_receiving-{unique_key}"), + format!("is_receiving:{unique_key}"), voice_state.is_receiving, ) .set( - format!("screensharing-{unique_key}"), + format!("screensharing:{unique_key}"), voice_state.screensharing, ) - .set(format!("camera-{unique_key}"), voice_state.camera) + .set(format!("camera:{unique_key}"), voice_state.camera) .query_async(&mut get_connection().await.to_internal_error()?.into_inner()) .await .to_internal_error()?; @@ -104,16 +101,16 @@ pub async fn delete_voice_state( server_id: Option<&str>, user_id: &str, ) -> Result<()> { - let unique_key = format!("{}-{}", &user_id, server_id.unwrap_or(channel_id)); + let unique_key = format!("{}:{}", &user_id, server_id.unwrap_or(channel_id)); Pipeline::new() - .srem(format!("vc-members-{channel_id}"), user_id) - .srem(format!("vc-{user_id}"), channel_id) + .srem(format!("vc_members:{channel_id}"), user_id) + .srem(format!("vc:{user_id}"), channel_id) .del(&[ - format!("is_publishing-{unique_key}"), - format!("is_receiving-{unique_key}"), - format!("screensharing-{unique_key}"), - format!("camera-{unique_key}"), + format!("is_publishing:{unique_key}"), + format!("is_receiving:{unique_key}"), + format!("screensharing:{unique_key}"), + format!("camera:{unique_key}"), unique_key.clone(), ]) .query_async(&mut get_connection().await.to_internal_error()?.into_inner()) @@ -161,24 +158,24 @@ pub async fn update_voice_state( user_id: &str, partial: &PartialUserVoiceState, ) -> Result<()> { - let unique_key = format!("{}-{}", &user_id, server_id.unwrap_or(channel_id)); + let unique_key = format!("{}:{}", &user_id, server_id.unwrap_or(channel_id)); let mut pipeline = Pipeline::new(); if let Some(camera) = &partial.camera { - pipeline.set(format!("camera-{unique_key}"), camera); + pipeline.set(format!("camera:{unique_key}"), camera); }; if let Some(is_publishing) = &partial.is_publishing { - pipeline.set(format!("is_publishing-{unique_key}"), is_publishing); + pipeline.set(format!("is_publishing:{unique_key}"), is_publishing); } if let Some(is_receiving) = &partial.is_receiving { - pipeline.set(format!("is_receiving-{unique_key}"), is_receiving); + pipeline.set(format!("is_receiving:{unique_key}"), is_receiving); } if let Some(screensharing) = &partial.screensharing { - pipeline.set(format!("screensharing-{unique_key}"), screensharing); + pipeline.set(format!("screensharing:{unique_key}"), screensharing); } pipeline @@ -193,7 +190,7 @@ pub async fn get_voice_channel_members(channel_id: &str) -> Result> get_connection() .await .to_internal_error()? - .smembers::<_, Vec>(format!("vc-members-{}", channel_id)) + .smembers::<_, Vec>(format!("vc_members:{}", channel_id)) .await .to_internal_error() } @@ -203,16 +200,16 @@ pub async fn get_voice_state( server_id: Option<&str>, user_id: &str, ) -> Result> { - let unique_key = format!("{}-{user_id}", server_id.unwrap_or(channel_id)); + let unique_key = format!("{}:{user_id}", server_id.unwrap_or(channel_id)); let (is_publishing, is_receiving, screensharing, camera) = get_connection() .await .to_internal_error()? .mget::<_, (Option, Option, Option, Option)>(&[ - format!("is_publishing-{unique_key}"), - format!("is_receiving-{unique_key}"), - format!("screensharing-{unique_key}"), - format!("camera-{unique_key}"), + format!("is_publishing:{unique_key}"), + format!("is_receiving:{unique_key}"), + format!("screensharing:{unique_key}"), + format!("camera:{unique_key}"), ]) .await .to_internal_error()?; @@ -231,92 +228,32 @@ pub async fn get_voice_state( } } -#[derive(Debug)] -pub struct VoiceClient { - rooms: RoomClient, - api_key: String, - api_secret: String, -} +pub async fn get_channel_voice_state(channel: &Channel) -> Result> { + let members = get_voice_channel_members(channel.id()).await?; -impl VoiceClient { - pub fn new(url: String, api_key: String, api_secret: String) -> Self { - Self { - rooms: RoomClient::with_api_key(&url, &api_key, &api_secret), - api_key, - api_secret, - } - } + let server = match channel { + Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => Some(server.as_str()), + _ => None + }; - pub async fn from_revolt_config() -> Self { - let config = config().await; + if !members.is_empty() { + let mut participants = Vec::with_capacity(members.len()); - Self::new( - config.hosts.livekit, - config.api.livekit.key, - config.api.livekit.secret, - ) - } + for user_id in members { + if let Some(voice_state) = get_voice_state(channel.id(), server, &user_id).await? { + participants.push(voice_state); + } else { + log::info!("Voice state not found but member in voice channel members, removing."); - pub fn create_token( - &self, - user: &User, - permissions: PermissionValue, - channel: &Channel, - ) -> Result { - let allowed_sources = get_allowed_sources(permissions); - - AccessToken::with_api_key(&self.api_key, &self.api_secret) - .with_name(&format!("{}#{}", user.username, user.discriminator)) - .with_identity(&user.id) - .with_metadata(&serde_json::to_string(&user).to_internal_error()?) - .with_ttl(Duration::from_secs(10)) - .with_grants(VideoGrants { - room_join: true, - can_publish_sources: allowed_sources.into_iter().map(ToString::to_string).collect(), - can_subscribe: permissions.has_channel_permission(ChannelPermission::Listen), - room: channel.id().to_string(), - ..Default::default() - }) - .to_jwt() - .to_internal_error() - } - - pub async fn create_room(&self, channel: &Channel) -> Result { - let voice = channel - .voice() - .ok_or_else(|| create_error!(NotAVoiceChannel))?; - - self.rooms - .create_room( - channel.id(), - CreateRoomOptions { - max_participants: voice.max_users.unwrap_or(u32::MAX), - empty_timeout: 5 * 60, // 5 minutes - ..Default::default() - }, - ) - .await - .to_internal_error() - } + delete_voice_state(channel.id(), server, &user_id).await?; + } + } - pub async fn update_permissions( - &self, - user: &User, - channel_id: &str, - new_permissions: ParticipantPermission, - ) -> Result { - self.rooms - .update_participant( - channel_id, - &user.id, - UpdateParticipantOptions { - permission: Some(new_permissions), - attributes: HashMap::new(), - name: "".to_string(), - metadata: "".to_string(), - }, - ) - .await - .to_internal_error() + Ok(Some(v0::ChannelVoiceState { + id: channel.id().to_string(), + participants, + })) + } else { + Ok(None) } } diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs new file mode 100644 index 000000000..0aa78ec20 --- /dev/null +++ b/crates/core/database/src/voice/voice_client.rs @@ -0,0 +1,108 @@ +use livekit_api::{ + access_token::{AccessToken, VideoGrants}, + services::room::{CreateRoomOptions, RoomClient, UpdateParticipantOptions}, +}; +use livekit_protocol::{ParticipantInfo, ParticipantPermission, Room}; +use revolt_config::config; +use crate::models::{Channel, User}; +use revolt_models::v0; +use revolt_permissions::{ChannelPermission, PermissionValue}; +use revolt_result::{create_error, Result, ToRevoltError}; +use std::{borrow::Cow, collections::HashMap, time::Duration}; + +use super::get_allowed_sources; + +#[derive(Debug)] +pub struct VoiceClient { + rooms: RoomClient, + api_key: String, + api_secret: String, +} + +impl VoiceClient { + pub fn new(url: String, api_key: String, api_secret: String) -> Self { + Self { + rooms: RoomClient::with_api_key(&url, &api_key, &api_secret), + api_key, + api_secret, + } + } + + pub async fn from_revolt_config() -> Self { + let config = config().await; + + Self::new( + config.hosts.livekit, + config.api.livekit.key, + config.api.livekit.secret, + ) + } + + pub fn create_token( + &self, + user: &User, + permissions: PermissionValue, + channel: &Channel, + ) -> Result { + let allowed_sources = get_allowed_sources(permissions); + + AccessToken::with_api_key(&self.api_key, &self.api_secret) + .with_name(&format!("{}#{}", user.username, user.discriminator)) + .with_identity(&user.id) + .with_metadata(&serde_json::to_string(&user).to_internal_error()?) + .with_ttl(Duration::from_secs(10)) + .with_grants(VideoGrants { + room_join: true, + can_publish: true, + can_publish_sources: vec![], // allowed_sources.into_iter().map(ToString::to_string).collect(), + can_subscribe: permissions.has_channel_permission(ChannelPermission::Listen), + room: channel.id().to_string(), + ..Default::default() + }) + .to_jwt() + .to_internal_error() + } + + pub async fn create_room(&self, channel: &Channel) -> Result { + let voice = match channel { + Channel::DirectMessage { .. } | Channel::VoiceChannel { .. } => Some(Cow::Owned(v0::VoiceInformation::default())), + Channel::TextChannel { voice: Some(voice), .. } => Some(Cow::Borrowed(voice)), + _ => None + } + + .ok_or_else(|| create_error!(NotAVoiceChannel))?; + + self.rooms + .create_room( + channel.id(), + CreateRoomOptions { + max_participants: voice.max_users.unwrap_or(u32::MAX), + empty_timeout: 5 * 60, // 5 minutes + ..Default::default() + }, + ) + .await + .to_internal_error() + } + + pub async fn update_permissions( + &self, + user: &User, + channel_id: &str, + new_permissions: ParticipantPermission, + ) -> Result { + self.rooms + .update_participant( + channel_id, + &user.id, + UpdateParticipantOptions { + permission: Some(new_permissions), + attributes: HashMap::new(), + name: "".to_string(), + metadata: "".to_string(), + }, + ) + .await + .to_internal_error() + } +} diff --git a/crates/core/models/src/v0/messages.rs b/crates/core/models/src/v0/messages.rs index b8c53aeed..21541984b 100644 --- a/crates/core/models/src/v0/messages.rs +++ b/crates/core/models/src/v0/messages.rs @@ -134,6 +134,8 @@ auto_derived!( MessagePinned { id: String, by: String }, #[serde(rename = "message_unpinned")] MessageUnpinned { id: String, by: String }, + #[serde(rename = "call_started")] + CallStarted { by: String }, } /// Name and / or avatar override information @@ -438,6 +440,7 @@ impl From for String { } SystemMessage::MessagePinned { .. } => "Message pinned.".to_string(), SystemMessage::MessageUnpinned { .. } => "Message unpinned.".to_string(), + SystemMessage::CallStarted { .. } => "Call started.".to_string(), } } } diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index 36dda5ac0..8c04b7098 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -218,6 +218,7 @@ pub trait ToRevoltError { } impl ToRevoltError for Result { + #[track_caller] fn to_internal_error(self) -> Result { self .inspect_err(|e| log::error!("{e:?}")) @@ -233,6 +234,7 @@ impl ToRevoltError for Result { } impl ToRevoltError for Option { + #[track_caller] fn to_internal_error(self) -> Result { self.ok_or_else(|| { let loc = Location::caller(); diff --git a/crates/core/voice/Cargo.toml b/crates/core/voice/Cargo.toml deleted file mode 100644 index b43e4bf47..000000000 --- a/crates/core/voice/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "revolt-voice" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -# voice -livekit-api = "0.4.1" -livekit-protocol = "0.3.6" - -# core -revolt-result = { path = "../result" } -revolt-models = { path = "../models" } -revolt-config = { path = "../config" } -revolt-database = { path = "../database" } -revolt-permissions = { path = "../permissions" } - -# async -futures = "0.3.21" -async-std = { version = "1.8.0", features = [ - "tokio1", - "tokio02", - "attributes", -] } - -# util -redis-kiss = "0.1.4" - -# serde -serde_json = "1.0.79" -serde = "1.0.136" diff --git a/crates/daemons/voice-ingress/Cargo.toml b/crates/daemons/voice-ingress/Cargo.toml index a2aea7aa5..93eee95bd 100644 --- a/crates/daemons/voice-ingress/Cargo.toml +++ b/crates/daemons/voice-ingress/Cargo.toml @@ -37,7 +37,6 @@ revolt-models = { path = "../../core/models" } revolt-config = { path = "../../core/config" } revolt-database = { path = "../../core/database" } revolt-permissions = { path = "../../core/permissions" } -revolt-voice = { path = "../../core/voice" } # voice livekit-api = "0.4.1" diff --git a/crates/daemons/voice-ingress/src/main.rs b/crates/daemons/voice-ingress/src/main.rs index f4c9f8770..91abe3215 100644 --- a/crates/daemons/voice-ingress/src/main.rs +++ b/crates/daemons/voice-ingress/src/main.rs @@ -4,8 +4,8 @@ use std::env; use livekit_protocol::WebhookEvent; use revolt_database::{ events::client::EventV1, util::reference::Reference, Database, DatabaseInfo, + voice::{create_voice_state, delete_voice_state, update_voice_state_tracks, VoiceClient} }; -use revolt_voice::{create_voice_state, delete_voice_state, update_voice_state_tracks, VoiceClient}; use rocket::{build, post, routes, serde::json::Json, Config, State}; use rocket_empty::EmptyResponse; diff --git a/crates/delta/Cargo.toml b/crates/delta/Cargo.toml index 0039f8ff9..5de0a2c03 100644 --- a/crates/delta/Cargo.toml +++ b/crates/delta/Cargo.toml @@ -80,7 +80,6 @@ revolt-models = { path = "../core/models", features = [ revolt-presence = { path = "../core/presence" } revolt-result = { path = "../core/result", features = ["rocket", "okapi"] } revolt-permissions = { path = "../core/permissions", features = ["schemas"] } -revolt-voice = { path = "../core/voice" } # voice livekit-api = "0.4.1" diff --git a/crates/delta/src/main.rs b/crates/delta/src/main.rs index 245d77860..f9feb40b3 100644 --- a/crates/delta/src/main.rs +++ b/crates/delta/src/main.rs @@ -24,7 +24,7 @@ use amqprs::{ use async_std::channel::unbounded; use authifier::AuthifierEvent; use rocket::data::ToByteUnit; -use livekit_api::services::room::RoomClient; +use revolt_database::voice::VoiceClient; pub async fn web() -> Rocket { // Get settings @@ -84,7 +84,7 @@ pub async fn web() -> Rocket { .into(); // Voice handler - let voice_client = revolt_voice::VoiceClient::new(config.api.livekit.url.clone(), config.api.livekit.key.clone(), config.api.livekit.secret.clone()); + let voice_client = VoiceClient::new(config.api.livekit.url.clone(), config.api.livekit.key.clone(), config.api.livekit.secret.clone()); // Configure Rabbit let connection = Connection::open(&OpenConnectionArguments::new( &config.rabbit.host, diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index 460b362da..c2f9f3096 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -1,8 +1,7 @@ use revolt_models::v0; -use revolt_database::{util::{permissions::perms, reference::Reference}, Database, User}; +use revolt_database::{util::{permissions::perms, reference::Reference}, voice::{raise_if_in_voice, VoiceClient}, Channel, Database, SystemMessage, User, AMQP}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_result::Result; -use revolt_voice::{VoiceClient, raise_if_in_voice}; use rocket::{serde::json::Json, State}; @@ -11,10 +10,10 @@ use rocket::{serde::json::Json, State}; /// Asks the voice server for a token to join the call. #[openapi(tag = "Voice")] #[post("//join_call")] -pub async fn call(db: &State, voice: &State, user: User, target: Reference) -> Result> { +pub async fn call(db: &State, amqp: &State, voice: &State, user: User, target: Reference) -> Result> { let channel = target.as_channel(db).await?; - raise_if_in_voice(&user, &channel.id()).await?; + raise_if_in_voice(&user, channel.id()).await?; let mut permissions = perms(db, &user).channel(&channel); @@ -24,7 +23,28 @@ pub async fn call(db: &State, voice: &State, user: User, let token = voice.create_token(&user, current_permissions, &channel)?; let room = voice.create_room(&channel).await?; - log::debug!("created room {}", room.name); + log::debug!("Created room {}", room.name); + + match &channel { + Channel::DirectMessage { .. } | Channel::Group { .. } => { + SystemMessage::CallStarted { + by: user.id.clone() + } + .into_message(channel.id().to_string()) + .send( + db, + Some(amqp), + v0::MessageAuthor::System { + username: &user.username, + avatar: user.avatar.as_ref().map(|file| file.id.as_ref()), + }, + None, + None, + &channel, false + ).await?; + } + _ => {} + }; Ok(Json(v0::CreateVoiceUserResponse { token })) } diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index 09666ac48..309b53f69 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -1,5 +1,6 @@ use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, + voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}, Database, RemovalIntention, ServerBan, User, }; use revolt_models::v0; @@ -8,7 +9,6 @@ use revolt_permissions::{ calculate_server_permissions, ChannelPermission, }; use revolt_result::{create_error, Result}; -use revolt_voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}; use rocket::{serde::json::Json, State}; use validator::Validate; diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index 5fb71a14d..e20fa8460 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -7,13 +7,13 @@ use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use revolt_database::{ events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, + voice::VoiceClient, Database, File, PartialMember, User, }; use revolt_models::v0::{self, FieldsMember, PartialUserVoiceState}; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result, ToRevoltError}; -use revolt_voice::VoiceClient; use rocket::{form::validate::Contains, serde::json::Json, State}; use validator::Validate; diff --git a/crates/delta/src/routes/servers/roles_delete.rs b/crates/delta/src/routes/servers/roles_delete.rs index 3f1e5b509..881ef6cb5 100644 --- a/crates/delta/src/routes/servers/roles_delete.rs +++ b/crates/delta/src/routes/servers/roles_delete.rs @@ -1,11 +1,12 @@ use livekit_protocol::ParticipantPermission; use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, Channel, Database, User + util::{permissions::DatabasePermissionQuery, reference::Reference}, + voice::{get_allowed_sources, get_voice_channel_members, get_voice_state, update_voice_state, VoiceClient}, + Channel, Database, User }; use revolt_models::v0::PartialUserVoiceState; use revolt_permissions::{calculate_channel_permissions, calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; -use revolt_voice::{get_allowed_sources, get_voice_channel_members, get_voice_state, update_voice_state, VoiceClient}; use rocket::State; use rocket_empty::EmptyResponse; diff --git a/crates/delta/src/routes/servers/roles_edit.rs b/crates/delta/src/routes/servers/roles_edit.rs index 46d22314a..7b211d0f2 100644 --- a/crates/delta/src/routes/servers/roles_edit.rs +++ b/crates/delta/src/routes/servers/roles_edit.rs @@ -1,11 +1,12 @@ use livekit_protocol::ParticipantPermission; use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, Channel, Database, PartialRole, User + util::{permissions::DatabasePermissionQuery, reference::Reference}, + voice::{get_allowed_sources, get_voice_channel_members, get_voice_state, update_voice_state, update_voice_state_tracks, VoiceClient}, + Channel, Database, PartialRole, User }; use revolt_models::v0::{self, PartialUserVoiceState}; use revolt_permissions::{calculate_channel_permissions, calculate_server_permissions, ChannelPermission, PermissionQuery}; use revolt_result::{create_error, Result}; -use revolt_voice::{get_allowed_sources, get_voice_channel_members, get_voice_state, update_voice_state, update_voice_state_tracks, VoiceClient}; use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; use validator::Validate; From c8ae34ba73fa59b2376ebd2cc8c884e2d3c4c0bd Mon Sep 17 00:00:00 2001 From: Zomatree Date: Tue, 28 Jan 2025 15:55:21 +0000 Subject: [PATCH 10/62] feat: implement missing permission syncs --- crates/core/database/src/events/client.rs | 5 + crates/core/database/src/voice/mod.rs | 125 ++++++++++++++++-- .../core/database/src/voice/voice_client.rs | 6 + crates/core/result/src/lib.rs | 21 ++- .../src/routes/channels/channel_delete.rs | 25 ++-- .../routes/channels/group_remove_member.rs | 56 ++++---- .../src/routes/channels/permissions_set.rs | 16 ++- .../channels/permissions_set_default.rs | 11 +- crates/delta/src/routes/servers/ban_create.rs | 3 +- .../delta/src/routes/servers/member_edit.rs | 79 ++--------- .../delta/src/routes/servers/member_remove.rs | 7 + .../src/routes/servers/permissions_set.rs | 10 +- .../routes/servers/permissions_set_default.rs | 10 +- .../delta/src/routes/servers/roles_delete.rs | 48 +------ crates/delta/src/routes/servers/roles_edit.rs | 53 +------- .../delta/src/routes/servers/server_delete.rs | 19 ++- 16 files changed, 266 insertions(+), 228 deletions(-) diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index 6262f9da6..99781ea43 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -254,6 +254,11 @@ pub enum EventV1 { id: String, user: String, }, + VoiceChannelMove { + user: String, + from: String, + to: String, + }, UserVoiceStateUpdate { id: String, channel_id: String, diff --git a/crates/core/database/src/voice/mod.rs b/crates/core/database/src/voice/mod.rs index 2e59ca335..996ee5054 100644 --- a/crates/core/database/src/voice/mod.rs +++ b/crates/core/database/src/voice/mod.rs @@ -1,15 +1,20 @@ -use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; -use crate::models::{Channel, User}; +use livekit_protocol::ParticipantPermission; +use redis_kiss::{get_connection as _get_connection, Conn, redis::Pipeline, AsyncCommands}; +use crate::{events::client::EventV1, models::{Channel, User}, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, Role, Server}; use revolt_models::v0::{self, PartialUserVoiceState, UserVoiceState}; -use revolt_permissions::{ChannelPermission, PermissionValue}; +use revolt_permissions::{calculate_channel_permissions, ChannelPermission, PermissionValue}; use revolt_result::{create_error, Result, ToRevoltError}; mod voice_client; pub use voice_client::VoiceClient; +async fn get_connection() -> Result { + _get_connection().await.to_internal_error() +} + pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> { - let mut conn = get_connection().await.to_internal_error()?; + let mut conn = get_connection().await?; if user.bot.is_some() // bots can be in as many voice channels as it wants so we just check if its already connected to the one its trying to connect to @@ -30,11 +35,27 @@ pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> { } } +pub async fn get_user_voice_channels(user_id: &str) -> Result> { + get_connection() + .await? + .smembers(format!("vc:{user_id}")) + .await + .to_internal_error() +} + +pub async fn is_in_voice_channel(user_id: &str, channel_id: &str) -> Result { + get_connection() + .await? + .sismember(format!("vc:{}", user_id), channel_id) + .await + .to_internal_error() +} + pub async fn get_user_voice_channel_in_server( user_id: &str, server_id: &str, ) -> Result> { - let mut conn = get_connection().await.to_internal_error()?; + let mut conn = get_connection().await?; let unique_key = format!("{}:{}", user_id, server_id); @@ -89,7 +110,7 @@ pub async fn create_voice_state( voice_state.screensharing, ) .set(format!("camera:{unique_key}"), voice_state.camera) - .query_async(&mut get_connection().await.to_internal_error()?.into_inner()) + .query_async(&mut get_connection().await?.into_inner()) .await .to_internal_error()?; @@ -113,7 +134,7 @@ pub async fn delete_voice_state( format!("camera:{unique_key}"), unique_key.clone(), ]) - .query_async(&mut get_connection().await.to_internal_error()?.into_inner()) + .query_async(&mut get_connection().await?.into_inner()) .await .to_internal_error()?; @@ -179,7 +200,7 @@ pub async fn update_voice_state( } pipeline - .query_async(&mut get_connection().await.to_internal_error()?.into_inner()) + .query_async(&mut get_connection().await?.into_inner()) .await .to_internal_error()?; @@ -188,8 +209,7 @@ pub async fn update_voice_state( pub async fn get_voice_channel_members(channel_id: &str) -> Result> { get_connection() - .await - .to_internal_error()? + .await? .smembers::<_, Vec>(format!("vc_members:{}", channel_id)) .await .to_internal_error() @@ -203,8 +223,7 @@ pub async fn get_voice_state( let unique_key = format!("{}:{user_id}", server_id.unwrap_or(channel_id)); let (is_publishing, is_receiving, screensharing, camera) = get_connection() - .await - .to_internal_error()? + .await? .mget::<_, (Option, Option, Option, Option)>(&[ format!("is_publishing:{unique_key}"), format!("is_receiving:{unique_key}"), @@ -257,3 +276,85 @@ pub async fn get_channel_voice_state(channel: &Channel) -> Result Result<()> { + get_connection() + .await? + .smove( + format!("vc-members-{from}"), + format!("vc-members-{to}"), + user, + ) + .await + .to_internal_error()?; + + Ok(()) +} + +pub async fn sync_voice_permissions(db: &Database, voice_client: &VoiceClient, channel: &Channel, server: Option<&Server>, role_id: Option<&str>) -> Result<()> { + + for user_id in get_voice_channel_members(channel.id()).await? { + let user = Reference::from_unchecked(user_id.clone()).as_user(db).await?; + + sync_user_voice_permissions(db, voice_client, &user, channel, server, role_id).await?; + }; + + Ok(()) +} + +pub async fn sync_user_voice_permissions(db: &Database, voice_client: &VoiceClient, user: &User, channel: &Channel, server: Option<&Server>, role_id: Option<&str>) -> Result<()> { + let channel_id = channel.id(); + let server_id: Option<&str> = server.as_ref().map(|s| s.id.as_str()); + + let member = match server_id { + Some(server_id) => Some(Reference::from_unchecked(user.id.clone()).as_member(db, server_id).await?), + None => None + }; + + if role_id.is_none_or(|role_id| member.as_ref().is_none_or(|member| member.roles.iter().any(|r| r == role_id))) { + let voice_state = get_voice_state(channel_id, server_id, &user.id).await?.unwrap(); + + let mut query = DatabasePermissionQuery::new(db, user) + .channel(channel); + + if let (Some(server), Some(member)) = (server, member.as_ref()) { + query = query.member(member).server(server) + } + + let permissions = calculate_channel_permissions(&mut query).await; + + let mut update_event = PartialUserVoiceState { + id: Some(user.id.clone()), + ..Default::default() + }; + + let before = update_event.clone(); + + let can_video = permissions.has_channel_permission(ChannelPermission::Video); + let can_speak = permissions.has_channel_permission(ChannelPermission::Speak); + let can_listen = permissions.has_channel_permission(ChannelPermission::Listen); + + update_event.camera = voice_state.camera.then_some(can_video); + update_event.screensharing = voice_state.screensharing.then_some(can_video); + update_event.is_publishing = voice_state.is_publishing.then_some(can_speak); + + update_voice_state(channel_id, server_id, &user.id, &update_event).await?; + + voice_client.update_permissions(user, channel_id, ParticipantPermission { + can_subscribe: can_listen, + can_publish: can_speak, + can_publish_data: can_speak, + ..Default::default() + }).await?; + + if update_event != before { + EventV1::UserVoiceStateUpdate { + id: user.id.clone(), + channel_id: channel_id.to_string(), + data: update_event + }.p(channel_id.to_string()).await; + }; + }; + + Ok(()) +} \ No newline at end of file diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs index 0aa78ec20..4f540870b 100644 --- a/crates/core/database/src/voice/voice_client.rs +++ b/crates/core/database/src/voice/voice_client.rs @@ -105,4 +105,10 @@ impl VoiceClient { .await .to_internal_error() } + + pub async fn remove_user(&self, user: &User, channel_id: &str) -> Result<()> { + self.rooms.remove_participant(channel_id, &user.id) + .await + .to_internal_error() + } } diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index 8c04b7098..e9d4f24b2 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -220,25 +220,24 @@ pub trait ToRevoltError { impl ToRevoltError for Result { #[track_caller] fn to_internal_error(self) -> Result { - self - .inspect_err(|e| log::error!("{e:?}")) - .map_err(|_| { - let loc = Location::caller(); + let loc = Location::caller(); - Error { - error_type: ErrorType::InternalError, - location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) - } - }) + self + .map_err(|_| { + Error { + error_type: ErrorType::InternalError, + location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) + } + }) } } impl ToRevoltError for Option { #[track_caller] fn to_internal_error(self) -> Result { - self.ok_or_else(|| { - let loc = Location::caller(); + let loc = Location::caller(); + self.ok_or_else(|| { Error { error_type: ErrorType::InternalError, location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) diff --git a/crates/delta/src/routes/channels/channel_delete.rs b/crates/delta/src/routes/channels/channel_delete.rs index 86dea166c..8c8dd5db8 100644 --- a/crates/delta/src/routes/channels/channel_delete.rs +++ b/crates/delta/src/routes/channels/channel_delete.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Channel, Database, PartialChannel, User, AMQP, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{delete_voice_state, get_voice_channel_members, VoiceClient}, Channel, Database, PartialChannel, User, AMQP }; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; @@ -15,6 +14,7 @@ use rocket_empty::EmptyResponse; #[delete("/?")] pub async fn delete( db: &State, + voice_client: &State, amqp: &State, user: User, target: Reference, @@ -27,7 +27,7 @@ pub async fn delete( permissions.throw_if_lacking_channel_permission(ChannelPermission::ViewChannel)?; match &channel { - Channel::SavedMessages { .. } => Err(create_error!(NoEffect)), + Channel::SavedMessages { .. } => Err(create_error!(NoEffect))?, Channel::DirectMessage { .. } => channel .update( db, @@ -37,8 +37,7 @@ pub async fn delete( }, vec![], ) - .await - .map(|_| EmptyResponse), + .await?, Channel::Group { .. } => channel .remove_user_from_group( db, @@ -47,13 +46,21 @@ pub async fn delete( None, options.leave_silently.unwrap_or_default(), ) - .await - .map(|_| EmptyResponse), + .await?, Channel::TextChannel { .. } | Channel::VoiceChannel { .. } => { permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?; - channel.delete(db).await.map(|_| EmptyResponse) + channel.delete(db).await? } - } + }; + + for user_id in get_voice_channel_members(channel.id()).await? { + let user = Reference::from_unchecked(user_id).as_user(db).await?; + + voice_client.remove_user(&user, channel.id()).await?; + delete_voice_state(channel.id(), channel.server().as_deref(), &user.id).await?; + }; + + Ok(EmptyResponse) } #[cfg(test)] diff --git a/crates/delta/src/routes/channels/group_remove_member.rs b/crates/delta/src/routes/channels/group_remove_member.rs index 90cccfefa..6bc0feb25 100644 --- a/crates/delta/src/routes/channels/group_remove_member.rs +++ b/crates/delta/src/routes/channels/group_remove_member.rs @@ -1,4 +1,4 @@ -use revolt_database::{util::reference::Reference, Channel, Database, User, AMQP}; +use revolt_database::{util::reference::Reference, voice::{delete_voice_state, is_in_voice_channel, raise_if_in_voice, VoiceClient}, Channel, Database, User, AMQP}; use revolt_permissions::ChannelPermission; use revolt_result::{create_error, Result}; @@ -12,6 +12,7 @@ use rocket_empty::EmptyResponse; #[delete("//recipients/")] pub async fn remove_member( db: &State, + voice_client: &State, amqp: &State, user: User, target: Reference, @@ -23,32 +24,35 @@ pub async fn remove_member( let channel = target.as_channel(db).await?; - match &channel { - Channel::Group { - owner, recipients, .. - } => { - if &user.id != owner { - return Err(create_error!(MissingPermission { - permission: ChannelPermission::ManageChannel.to_string() - })); - } - - let member = member.as_user(db).await?; - if user.id == member.id { - return Err(create_error!(CannotRemoveYourself)); - } - - if !recipients.iter().any(|x| *x == member.id) { - return Err(create_error!(NotInGroup)); - } - - channel - .remove_user_from_group(db, amqp, &member, Some(&user.id), false) - .await - .map(|_| EmptyResponse) + if let Channel::Group { owner, recipients, .. } = &channel { + if &user.id != owner { + return Err(create_error!(MissingPermission { + permission: ChannelPermission::ManageChannel.to_string() + })); } - _ => Err(create_error!(InvalidOperation)), - } + + let member = member.as_user(db).await?; + if user.id == member.id { + return Err(create_error!(CannotRemoveYourself)); + } + + if !recipients.iter().any(|x| *x == member.id) { + return Err(create_error!(NotInGroup)); + } + + channel + .remove_user_from_group(db, amqp, &member, Some(&user.id), false) + .await?; + } else { + return Err(create_error!(InvalidOperation)) + }; + + if is_in_voice_channel(&user.id, channel.id()).await? { + voice_client.remove_user(&user, channel.id()).await?; + delete_voice_state(channel.id(), None, &user.id).await?; + }; + + Ok(EmptyResponse) } #[cfg(test)] diff --git a/crates/delta/src/routes/channels/permissions_set.rs b/crates/delta/src/routes/channels/permissions_set.rs index b4b3c3dce..e35645406 100644 --- a/crates/delta/src/routes/channels/permissions_set.rs +++ b/crates/delta/src/routes/channels/permissions_set.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Channel, Database, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Channel, Database, User }; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission, Override}; @@ -17,14 +16,15 @@ use serde::Deserialize; #[put("//permissions/", data = "", rank = 2)] pub async fn set_role_permissions( db: &State, + voice_client: &State, user: User, target: Reference, role_id: String, data: Json, ) -> Result> { - let mut channel = target.as_channel(db).await?; + let channel = target.as_channel(db).await?; let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel); - let permissions = calculate_channel_permissions(&mut query).await; + let permissions: revolt_permissions::PermissionValue = calculate_channel_permissions(&mut query).await; permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?; @@ -39,11 +39,15 @@ pub async fn set_role_permissions( .throw_permission_override(current_value, &data.permissions) .await?; - channel + let mut new_channel = channel.clone(); + + new_channel .set_role_permission(db, &role_id, data.permissions.clone().into()) .await?; - Ok(Json(channel.into())) + sync_voice_permissions(db, voice_client, &new_channel, Some(server), Some(&role_id)).await?; + + Ok(Json(new_channel.into())) } else { Err(create_error!(NotFound)) } diff --git a/crates/delta/src/routes/channels/permissions_set_default.rs b/crates/delta/src/routes/channels/permissions_set_default.rs index 5957426ab..ed82de816 100644 --- a/crates/delta/src/routes/channels/permissions_set_default.rs +++ b/crates/delta/src/routes/channels/permissions_set_default.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Channel, Database, PartialChannel, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Channel, Database, PartialChannel, User }; use revolt_models::v0::{self, DataDefaultChannelPermissions}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; @@ -16,6 +15,7 @@ use rocket::{serde::json::Json, State}; #[put("//permissions/default", data = "", rank = 1)] pub async fn set_default_permissions( db: &State, + voice_client: &State, user: User, target: Reference, data: Json, @@ -75,5 +75,12 @@ pub async fn set_default_permissions( _ => return Err(create_error!(InvalidOperation)), } + let server = match channel.server() { + Some(server_id) => Some(Reference::from_unchecked(server_id).as_server(db).await?), + None => None + }; + + sync_voice_permissions(db, voice_client, &channel, server.as_ref(), None).await?; + Ok(Json(channel.into())) } diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index 309b53f69..cd1c8cf36 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -19,7 +19,7 @@ use validator::Validate; #[put("//bans/", data = "")] pub async fn ban( db: &State, - voice: &State, + voice_client: &State, user: User, server: Reference, target: Reference, @@ -61,6 +61,7 @@ pub async fn ban( // If the member is in a voice channel while banned kick them from the voice channel if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { + voice_client.remove_user(&user, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await? } } diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index e20fa8460..d86155be2 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -7,7 +7,7 @@ use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use revolt_database::{ events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::VoiceClient, + voice::{get_user_voice_channel_in_server, move_user, sync_user_voice_permissions, VoiceClient}, Database, File, PartialMember, User, }; use revolt_models::v0::{self, FieldsMember, PartialUserVoiceState}; @@ -23,8 +23,8 @@ use validator::Validate; #[openapi(tag = "Server Members")] #[patch("//members/", data = "")] pub async fn edit( - voice_client: &State, db: &State, + voice_client: &State, user: User, server: Reference, target: Reference, @@ -190,6 +190,18 @@ pub async fn edit( .map(|r| r.contains(FieldsMember::CanPublish) || r.contains(FieldsMember::CanReceive)) .unwrap_or_default(); + if can_publish.is_some() + || can_receive.is_some() + || voice_channel.is_some() + || remove_contains_voice + { + if let Some(channel) = get_user_voice_channel_in_server(&target_user.id, &server.id).await? { + let channel = Reference::from_unchecked(channel).as_channel(db).await?; + + sync_user_voice_permissions(db, voice_client, &user, &channel, Some(&server), None).await?; + }; + }; + member .update( db, @@ -200,68 +212,5 @@ pub async fn edit( ) .await?; - if can_publish.is_some() - || can_receive.is_some() - || voice_channel.is_some() - || remove_contains_voice - { - let mut conn = get_connection().await.to_internal_error()?; - - let unique_key = format!("{}-{}", &member.id.user, &member.id.server); - - // if we edit the member while they are in a voice channel we need to also update the perms - // otherwise it wont take place until they leave and rejoin - if let Some(channel) = conn - .get::<_, Option>(format!("vc-{}", &unique_key)) - .await - .to_internal_error()? - { - let mut pipeline = Pipeline::new(); - let mut new_perms = ParticipantPermission::default(); - - if remove_contains_voice { - let mut query = DatabasePermissionQuery::new(db, &target_user) - .server(&server) - .member(&member); - - let permissions = calculate_server_permissions(&mut query).await; - - if !permissions.has_channel_permission(ChannelPermission::Speak) { - can_publish = Some(false) - } - - if !permissions.has_channel_permission(ChannelPermission::Listen) { - can_publish = Some(false) - } - } - - if let Some(can_publish) = can_publish { - new_perms.can_publish = can_publish; - new_perms.can_publish_data = can_publish; - }; - - if let Some(can_receive) = can_receive { - new_perms.can_subscribe = can_receive; - }; - - if let Some(new_channel) = voice_channel { - pipeline.smove( - format!("vc-members-{channel}"), - format!("vc-members-{new_channel}"), - &member.id.user, - ); - }; - - pipeline - .query_async(&mut conn.into_inner()) - .await - .to_internal_error()?; - - voice_client - .update_permissions(&user, &channel, new_perms) - .await?; - }; - }; - Ok(Json(member.into())) } diff --git a/crates/delta/src/routes/servers/member_remove.rs b/crates/delta/src/routes/servers/member_remove.rs index a7e2f50af..a06e4be47 100644 --- a/crates/delta/src/routes/servers/member_remove.rs +++ b/crates/delta/src/routes/servers/member_remove.rs @@ -1,5 +1,6 @@ use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, + voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}, Database, RemovalIntention, User, }; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; @@ -14,6 +15,7 @@ use rocket_empty::EmptyResponse; #[delete("//members/")] pub async fn kick( db: &State, + voice_client: &State, user: User, target: Reference, member: Reference, @@ -40,6 +42,11 @@ pub async fn kick( return Err(create_error!(NotElevated)); } + if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { + voice_client.remove_user(&user, &channel_id).await?; + delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; + } + member .remove(db, &server, RemovalIntention::Kick, false) .await diff --git a/crates/delta/src/routes/servers/permissions_set.rs b/crates/delta/src/routes/servers/permissions_set.rs index 0c293aa89..3d80d3873 100644 --- a/crates/delta/src/routes/servers/permissions_set.rs +++ b/crates/delta/src/routes/servers/permissions_set.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Database, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Database, User }; use revolt_models::v0; use revolt_permissions::{calculate_server_permissions, ChannelPermission, Override}; @@ -14,6 +13,7 @@ use rocket::{serde::json::Json, State}; #[put("//permissions/", data = "", rank = 2)] pub async fn set_role_permission( db: &State, + voice_client: &State, user: User, target: Reference, role_id: String, @@ -40,6 +40,12 @@ pub async fn set_role_permission( .throw_permission_override(current_value, &data.permissions) .await?; + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; + + sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; + }; + server .set_role_permission(db, &role_id, data.permissions.into()) .await?; diff --git a/crates/delta/src/routes/servers/permissions_set_default.rs b/crates/delta/src/routes/servers/permissions_set_default.rs index 8189e9569..42c364d83 100644 --- a/crates/delta/src/routes/servers/permissions_set_default.rs +++ b/crates/delta/src/routes/servers/permissions_set_default.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Database, PartialServer, Server, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Database, PartialServer, Server, User }; use revolt_models::v0; use revolt_permissions::{ @@ -16,6 +15,7 @@ use rocket::{serde::json::Json, State}; #[put("//permissions/default", data = "", rank = 1)] pub async fn set_default_permissions( db: &State, + voice_client: &State, user: User, target: Reference, data: Json, @@ -39,6 +39,12 @@ pub async fn set_default_permissions( ) .await?; + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; + + sync_voice_permissions(db, voice_client, &channel, Some(&server), None).await?; + }; + server .update( db, diff --git a/crates/delta/src/routes/servers/roles_delete.rs b/crates/delta/src/routes/servers/roles_delete.rs index 881ef6cb5..38b408597 100644 --- a/crates/delta/src/routes/servers/roles_delete.rs +++ b/crates/delta/src/routes/servers/roles_delete.rs @@ -1,11 +1,9 @@ -use livekit_protocol::ParticipantPermission; use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::{get_allowed_sources, get_voice_channel_members, get_voice_state, update_voice_state, VoiceClient}, - Channel, Database, User + voice::{sync_voice_permissions, VoiceClient}, + Database, User }; -use revolt_models::v0::PartialUserVoiceState; -use revolt_permissions::{calculate_channel_permissions, calculate_server_permissions, ChannelPermission}; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; use rocket::State; use rocket_empty::EmptyResponse; @@ -38,45 +36,7 @@ pub async fn delete( for channel_id in &server.channels { let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; - if matches!(channel, Channel::VoiceChannel { .. }) { - for member_id in get_voice_channel_members(channel_id).await? { - let member = Reference::from_unchecked(member_id).as_member(db, &server.id).await?; - - if member.roles.contains(&role_id) { - let user = Reference::from_unchecked(member.id.user.clone()).as_user(db).await?; - let voice_state = get_voice_state(channel_id, Some(&server.id), &user.id).await?.unwrap(); - - let mut query = DatabasePermissionQuery::new(db, &user) - .member(&member) - .channel(&channel) - .server(&server); - - let permissions = calculate_channel_permissions(&mut query).await; - - let mut update_event = PartialUserVoiceState { - id: Some(user.id.clone()), - ..Default::default() - }; - - let can_video = permissions.has_channel_permission(ChannelPermission::Video); - let can_speak = permissions.has_channel_permission(ChannelPermission::Speak); - let can_listen = permissions.has_channel_permission(ChannelPermission::Listen); - - update_event.camera = voice_state.camera.then_some(can_video); - update_event.screensharing = voice_state.screensharing.then_some(can_video); - update_event.is_publishing = voice_state.is_publishing.then_some(can_speak); - - update_voice_state(channel_id, Some(&server.id), &user.id, &update_event).await?; - - voice_client.update_permissions(&user, channel_id, ParticipantPermission { - can_subscribe: can_listen, - can_publish: can_speak, - can_publish_data: can_speak, - ..Default::default() - }).await?; - } - } - } + sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; }; role.delete(db, &server.id, &role_id) diff --git a/crates/delta/src/routes/servers/roles_edit.rs b/crates/delta/src/routes/servers/roles_edit.rs index 7b211d0f2..9d3f85a20 100644 --- a/crates/delta/src/routes/servers/roles_edit.rs +++ b/crates/delta/src/routes/servers/roles_edit.rs @@ -1,14 +1,12 @@ -use livekit_protocol::ParticipantPermission; use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::{get_allowed_sources, get_voice_channel_members, get_voice_state, update_voice_state, update_voice_state_tracks, VoiceClient}, - Channel, Database, PartialRole, User + voice::{sync_voice_permissions, VoiceClient}, + Database, PartialRole, User }; -use revolt_models::v0::{self, PartialUserVoiceState}; -use revolt_permissions::{calculate_channel_permissions, calculate_server_permissions, ChannelPermission, PermissionQuery}; +use revolt_models::v0; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; use rocket::{serde::json::Json, State}; -use serde::{Deserialize, Serialize}; use validator::Validate; /// # Edit Role @@ -18,12 +16,11 @@ use validator::Validate; #[patch("//roles/", data = "")] pub async fn edit( db: &State, - voice: &State, + voice_client: &State, user: User, target: Reference, role_id: String, data: Json, - voice_client: &State ) -> Result> { let data = data.into_inner(); data.validate().map_err(|error| { @@ -83,45 +80,7 @@ pub async fn edit( for channel_id in &server.channels { let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; - if matches!(channel, Channel::VoiceChannel { .. }) { - for member_id in get_voice_channel_members(channel_id).await? { - let member = Reference::from_unchecked(member_id).as_member(db, &server.id).await?; - - if member.roles.contains(&role_id) { - let user = Reference::from_unchecked(member.id.user.clone()).as_user(db).await?; - let voice_state = get_voice_state(channel_id, Some(&server.id), &user.id).await?.unwrap(); - - let mut query = DatabasePermissionQuery::new(db, &user) - .member(&member) - .channel(&channel) - .server(&server); - - let permissions = calculate_channel_permissions(&mut query).await; - - let mut update_event = PartialUserVoiceState { - id: Some(user.id.clone()), - ..Default::default() - }; - - let can_video = permissions.has_channel_permission(ChannelPermission::Video); - let can_speak = permissions.has_channel_permission(ChannelPermission::Speak); - let can_listen = permissions.has_channel_permission(ChannelPermission::Listen); - - update_event.camera = voice_state.camera.then_some(can_video); - update_event.screensharing = voice_state.screensharing.then_some(can_video); - update_event.is_publishing = voice_state.is_publishing.then_some(can_speak); - - update_voice_state(channel_id, Some(&server.id), &user.id, &update_event).await?; - - voice_client.update_permissions(&user, channel_id, ParticipantPermission { - can_subscribe: can_listen, - can_publish: can_speak, - can_publish_data: can_speak, - ..Default::default() - }).await?; - } - } - } + sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; }; Ok(Json(role.into())) diff --git a/crates/delta/src/routes/servers/server_delete.rs b/crates/delta/src/routes/servers/server_delete.rs index 28a3bf42a..9c9096594 100644 --- a/crates/delta/src/routes/servers/server_delete.rs +++ b/crates/delta/src/routes/servers/server_delete.rs @@ -1,4 +1,8 @@ -use revolt_database::{util::reference::Reference, Database, RemovalIntention, User}; +use revolt_database::{ + util::reference::Reference, + voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}, + Database, RemovalIntention, User, +}; use revolt_models::v0; use revolt_result::Result; use rocket::State; @@ -12,6 +16,7 @@ use rocket_empty::EmptyResponse; #[delete("/?")] pub async fn delete( db: &State, + voice_client: &State, user: User, target: Reference, options: v0::OptionsServerDelete, @@ -20,8 +25,20 @@ pub async fn delete( let member = db.fetch_member(&target.id, &user.id).await?; if server.owner == user.id { + for channel_id in &server.channels { + voice_client.remove_user(&user, channel_id).await?; + delete_voice_state(channel_id, Some(&server.id), &user.id).await?; + } + server.delete(db).await } else { + if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { + if server.channels.iter().any(|c| c == &channel_id) { + voice_client.remove_user(&user, &channel_id).await?; + delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; + } + }; + member .remove( db, From 41a6de67cdcb3632c843266a827ef66b3318c997 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Tue, 28 Jan 2025 15:59:58 +0000 Subject: [PATCH 11/62] fix: remove locked rocket version --- Cargo.lock | 45 ++++++++++++++++++++------------------------- Cargo.toml | 2 +- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9db589c30..80cd01bdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -972,7 +972,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "itoa", "matchit", @@ -3197,9 +3197,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -3242,9 +3242,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes 1.9.0", "futures-channel", @@ -3285,7 +3285,7 @@ checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", "http 1.2.0", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "rustls 0.22.4", "rustls-pki-types", @@ -3303,7 +3303,7 @@ checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http 1.2.0", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "rustls 0.23.21", "rustls-pki-types", @@ -3333,7 +3333,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes 1.9.0", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "native-tls", "tokio 1.43.0", @@ -3352,7 +3352,7 @@ dependencies = [ "futures-util", "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.2", + "hyper 1.6.0", "pin-project-lite 0.2.16", "socket2 0.5.8", "tokio 1.43.0", @@ -6152,7 +6152,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-rustls 0.27.5", "hyper-tls 0.6.0", "hyper-util", @@ -6575,7 +6575,7 @@ dependencies = [ "erased-serde", "http 1.2.0", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-rustls 0.26.0", "hyper-util", "parking_lot", @@ -7053,9 +7053,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" @@ -7104,9 +7104,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "schannel" @@ -8380,7 +8380,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.24", + "winnow 0.6.25", ] [[package]] @@ -8708,9 +8708,9 @@ checksum = "260bc6647b3893a9a90668360803a15f96b85a5257b1c3a0c3daf6ae2496de42" [[package]] name = "unicode-ident" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-normalization" @@ -9522,9 +9522,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.24" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" dependencies = [ "memchr", ] @@ -9739,8 +9739,3 @@ dependencies = [ name = "redis" version = "0.22.3" source = "git+https://github.com/revoltchat/redis-rs?rev=1a41faf356fd21aebba71cea7eb7eb2653e5f0ef#1a41faf356fd21aebba71cea7eb7eb2653e5f0ef" - -[[patch.unused]] -name = "rocket" -version = "0.5.0-rc.2" -source = "git+https://github.com/rwf2/Rocket/?rev=4dcd928#4dcd92837f3a4626a28d4b25da8e6e560878d004" diff --git a/Cargo.toml b/Cargo.toml index 2c3013fdf..e8c4eab68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ redis22 = { package = "redis", version = "0.22.3", git = "https://github.com/rev redis23 = { package = "redis", version = "0.23.1", git = "https://github.com/revoltchat/redis-rs", rev = "f8ca28ab85da59d2ccde526b4d2fb390eff5a5f9" } # authifier = { package = "authifier", version = "1.0.8", path = "../authifier/crates/authifier" } # rocket_authifier = { package = "rocket_authifier", version = "1.0.8", path = "../authifier/crates/rocket_authifier" } -rocket = { git = "https://github.com/rwf2/Rocket/", rev = "4dcd928" } +# rocket = { git = "https://github.com/rwf2/Rocket/", rev = "4dcd928" } [profile.release] lto = true From 742eb3bb7d9b3666877cd0ff4449d1cd477ce376 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Tue, 28 Jan 2025 16:06:58 +0000 Subject: [PATCH 12/62] fix: remove local testing values from config --- Revolt.toml | 6 ------ crates/core/config/Revolt.toml | 6 +++--- crates/core/database/src/drivers/mod.rs | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Revolt.toml b/Revolt.toml index f9ea06291..97da148d6 100644 --- a/Revolt.toml +++ b/Revolt.toml @@ -53,9 +53,3 @@ access_key_id = "minioautumn" secret_access_key = "minioautumn" # Bucket to upload to by default default_bucket = "revolt-uploads" - -[rabbit] -host = "rabbit.backend.orb.local" -port = 5672 -username = "rabbituser" -password = "rabbitpass" diff --git a/crates/core/config/Revolt.toml b/crates/core/config/Revolt.toml index 6b9a4919c..d298a2d7f 100644 --- a/crates/core/config/Revolt.toml +++ b/crates/core/config/Revolt.toml @@ -65,11 +65,11 @@ max_concurrent_connections = 50 [api.livekit] # Livekit server url -url = "ws://livekit:7880" +url = "ws://local.revolt.chat/livekit" # Livekit security key name -key = "dev" +key = "" # Livekit security secret value -secret = "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxc" +secret = "" [pushd] diff --git a/crates/core/database/src/drivers/mod.rs b/crates/core/database/src/drivers/mod.rs index 605a56126..f24263aef 100644 --- a/crates/core/database/src/drivers/mod.rs +++ b/crates/core/database/src/drivers/mod.rs @@ -43,7 +43,7 @@ impl DatabaseInfo { #[async_recursion] pub async fn connect(self) -> Result { let config = config().await; - println!("{config:?}"); + Ok(match self { DatabaseInfo::Auto => { if std::env::var("TEST_DB").is_ok() { From 2593a4272bf78b2e20c49843e8dd1ff3b35d19d4 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Sun, 2 Feb 2025 13:46:45 +0000 Subject: [PATCH 13/62] chore: switch to ids for parameter --- crates/core/database/src/voice/voice_client.rs | 4 ++-- crates/daemons/voice-ingress/Dockerfile | 12 ++++++------ crates/daemons/voice-ingress/src/main.rs | 13 ++++++++++++- crates/delta/src/routes/channels/channel_delete.rs | 4 +--- .../src/routes/channels/group_remove_member.rs | 2 +- crates/delta/src/routes/servers/ban_create.rs | 2 +- crates/delta/src/routes/servers/member_remove.rs | 2 +- crates/delta/src/routes/servers/server_delete.rs | 4 ++-- 8 files changed, 26 insertions(+), 17 deletions(-) diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs index 4f540870b..261f68a73 100644 --- a/crates/core/database/src/voice/voice_client.rs +++ b/crates/core/database/src/voice/voice_client.rs @@ -106,8 +106,8 @@ impl VoiceClient { .to_internal_error() } - pub async fn remove_user(&self, user: &User, channel_id: &str) -> Result<()> { - self.rooms.remove_participant(channel_id, &user.id) + pub async fn remove_user(&self, user_id: &str, channel_id: &str) -> Result<()> { + self.rooms.remove_participant(channel_id, user_id) .await .to_internal_error() } diff --git a/crates/daemons/voice-ingress/Dockerfile b/crates/daemons/voice-ingress/Dockerfile index 773069863..4749d5cc9 100644 --- a/crates/daemons/voice-ingress/Dockerfile +++ b/crates/daemons/voice-ingress/Dockerfile @@ -1,11 +1,11 @@ # Build Stage FROM ghcr.io/revoltchat/base:latest AS builder +FROM debian:12 AS debian # Bundle Stage -FROM debian:bullseye-slim -RUN apt-get update && \ - apt-get install -y ca-certificates && \ - apt-get clean +FROM gcr.io/distroless/cc-debian12:nonroot COPY --from=builder /home/rust/src/target/release/revolt-voice-ingress ./ -EXPOSE 9000 -CMD ["./revolt-voice-ingress"] +COPY --from=debian /usr/bin/uname /usr/bin/uname + +USER nonroot +CMD ["./revolt-voice-ingress"] \ No newline at end of file diff --git a/crates/daemons/voice-ingress/src/main.rs b/crates/daemons/voice-ingress/src/main.rs index 91abe3215..55fdf7551 100644 --- a/crates/daemons/voice-ingress/src/main.rs +++ b/crates/daemons/voice-ingress/src/main.rs @@ -1,7 +1,7 @@ use std::env; -use livekit_protocol::WebhookEvent; +use livekit_protocol::{TrackType, WebhookEvent}; use revolt_database::{ events::client::EventV1, util::reference::Reference, Database, DatabaseInfo, voice::{create_voice_state, delete_voice_state, update_voice_state_tracks, VoiceClient} @@ -85,6 +85,17 @@ async fn ingress(db: &State, voice_client: &State, body: .as_channel(db) .await?; + // remove the user if they try publish a video larger than 1080x720 or they publish data + if body.event == "track_published" + && ( + (track.r#type == TrackType::Video as i32 && (track.width > 1080 || track.height > 720)) + | (track.r#type == TrackType::Data as i32) + ) + { + voice_client.remove_user(user_id, channel_id).await?; + delete_voice_state(channel_id, channel.server().as_deref(), user_id).await?; + } + let partial = update_voice_state_tracks( channel_id, channel.server().as_deref(), diff --git a/crates/delta/src/routes/channels/channel_delete.rs b/crates/delta/src/routes/channels/channel_delete.rs index 8c8dd5db8..5cefe7552 100644 --- a/crates/delta/src/routes/channels/channel_delete.rs +++ b/crates/delta/src/routes/channels/channel_delete.rs @@ -54,9 +54,7 @@ pub async fn delete( }; for user_id in get_voice_channel_members(channel.id()).await? { - let user = Reference::from_unchecked(user_id).as_user(db).await?; - - voice_client.remove_user(&user, channel.id()).await?; + voice_client.remove_user(&user_id, channel.id()).await?; delete_voice_state(channel.id(), channel.server().as_deref(), &user.id).await?; }; diff --git a/crates/delta/src/routes/channels/group_remove_member.rs b/crates/delta/src/routes/channels/group_remove_member.rs index 6bc0feb25..9836a7c39 100644 --- a/crates/delta/src/routes/channels/group_remove_member.rs +++ b/crates/delta/src/routes/channels/group_remove_member.rs @@ -48,7 +48,7 @@ pub async fn remove_member( }; if is_in_voice_channel(&user.id, channel.id()).await? { - voice_client.remove_user(&user, channel.id()).await?; + voice_client.remove_user(&user.id, channel.id()).await?; delete_voice_state(channel.id(), None, &user.id).await?; }; diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index cd1c8cf36..96a8d4c92 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -61,7 +61,7 @@ pub async fn ban( // If the member is in a voice channel while banned kick them from the voice channel if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { - voice_client.remove_user(&user, &channel_id).await?; + voice_client.remove_user(&user.id, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await? } } diff --git a/crates/delta/src/routes/servers/member_remove.rs b/crates/delta/src/routes/servers/member_remove.rs index a06e4be47..3276add74 100644 --- a/crates/delta/src/routes/servers/member_remove.rs +++ b/crates/delta/src/routes/servers/member_remove.rs @@ -43,7 +43,7 @@ pub async fn kick( } if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { - voice_client.remove_user(&user, &channel_id).await?; + voice_client.remove_user(&user.id, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; } diff --git a/crates/delta/src/routes/servers/server_delete.rs b/crates/delta/src/routes/servers/server_delete.rs index 9c9096594..1cae301c8 100644 --- a/crates/delta/src/routes/servers/server_delete.rs +++ b/crates/delta/src/routes/servers/server_delete.rs @@ -26,7 +26,7 @@ pub async fn delete( if server.owner == user.id { for channel_id in &server.channels { - voice_client.remove_user(&user, channel_id).await?; + voice_client.remove_user(&user.id, channel_id).await?; delete_voice_state(channel_id, Some(&server.id), &user.id).await?; } @@ -34,7 +34,7 @@ pub async fn delete( } else { if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { if server.channels.iter().any(|c| c == &channel_id) { - voice_client.remove_user(&user, &channel_id).await?; + voice_client.remove_user(&user.id, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; } }; From 060b4c43f4407bf8f0210e3f7fea8db47ad11934 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Fri, 7 Feb 2025 21:35:51 +0000 Subject: [PATCH 14/62] feat: make Channel::server return a reference --- crates/core/database/src/models/channels/model.rs | 4 ++-- crates/core/database/src/voice/mod.rs | 2 +- crates/core/database/src/voice/voice_client.rs | 2 +- crates/daemons/voice-ingress/src/main.rs | 8 ++++---- crates/delta/src/routes/channels/channel_delete.rs | 2 +- .../delta/src/routes/channels/permissions_set_default.rs | 2 +- crates/delta/src/routes/servers/member_edit.rs | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/core/database/src/models/channels/model.rs b/crates/core/database/src/models/channels/model.rs index 94dd272a2..8af881ff4 100644 --- a/crates/core/database/src/models/channels/model.rs +++ b/crates/core/database/src/models/channels/model.rs @@ -435,9 +435,9 @@ impl Channel { } /// Clone this channel's server id - pub fn server(&self) -> Option { + pub fn server(&self) -> Option<&str> { match self { - Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => Some(server.clone()), + Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => Some(server), _ => None } } diff --git a/crates/core/database/src/voice/mod.rs b/crates/core/database/src/voice/mod.rs index 996ee5054..18380660a 100644 --- a/crates/core/database/src/voice/mod.rs +++ b/crates/core/database/src/voice/mod.rs @@ -1,7 +1,7 @@ use livekit_protocol::ParticipantPermission; use redis_kiss::{get_connection as _get_connection, Conn, redis::Pipeline, AsyncCommands}; -use crate::{events::client::EventV1, models::{Channel, User}, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, Role, Server}; +use crate::{events::client::EventV1, models::{Channel, User}, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, Server}; use revolt_models::v0::{self, PartialUserVoiceState, UserVoiceState}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission, PermissionValue}; use revolt_result::{create_error, Result, ToRevoltError}; diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs index 261f68a73..a6b2ba033 100644 --- a/crates/core/database/src/voice/voice_client.rs +++ b/crates/core/database/src/voice/voice_client.rs @@ -54,7 +54,7 @@ impl VoiceClient { .with_grants(VideoGrants { room_join: true, can_publish: true, - can_publish_sources: vec![], // allowed_sources.into_iter().map(ToString::to_string).collect(), + can_publish_sources: allowed_sources.into_iter().map(ToString::to_string).collect(), can_subscribe: permissions.has_channel_permission(ChannelPermission::Listen), room: channel.id().to_string(), ..Default::default() diff --git a/crates/daemons/voice-ingress/src/main.rs b/crates/daemons/voice-ingress/src/main.rs index 55fdf7551..04ec6e092 100644 --- a/crates/daemons/voice-ingress/src/main.rs +++ b/crates/daemons/voice-ingress/src/main.rs @@ -50,7 +50,7 @@ async fn ingress(db: &State, voice_client: &State, body: .as_channel(db) .await?; - let voice_state = create_voice_state(channel_id, channel.server().as_deref(), user_id).await?; + let voice_state = create_voice_state(channel_id, channel.server(), user_id).await?; EventV1::VoiceChannelJoin { id: channel_id.clone(), @@ -67,7 +67,7 @@ async fn ingress(db: &State, voice_client: &State, body: .as_channel(db) .await?; - delete_voice_state(channel_id, channel.server().as_deref(), user_id).await?; + delete_voice_state(channel_id, channel.server(), user_id).await?; EventV1::VoiceChannelLeave { id: channel_id.clone(), @@ -93,12 +93,12 @@ async fn ingress(db: &State, voice_client: &State, body: ) { voice_client.remove_user(user_id, channel_id).await?; - delete_voice_state(channel_id, channel.server().as_deref(), user_id).await?; + delete_voice_state(channel_id, channel.server(), user_id).await?; } let partial = update_voice_state_tracks( channel_id, - channel.server().as_deref(), + channel.server(), user_id, body.event == "track_published", // to avoid duplicating this entire case twice track.source diff --git a/crates/delta/src/routes/channels/channel_delete.rs b/crates/delta/src/routes/channels/channel_delete.rs index 5cefe7552..f3dab86db 100644 --- a/crates/delta/src/routes/channels/channel_delete.rs +++ b/crates/delta/src/routes/channels/channel_delete.rs @@ -55,7 +55,7 @@ pub async fn delete( for user_id in get_voice_channel_members(channel.id()).await? { voice_client.remove_user(&user_id, channel.id()).await?; - delete_voice_state(channel.id(), channel.server().as_deref(), &user.id).await?; + delete_voice_state(channel.id(), channel.server(), &user.id).await?; }; Ok(EmptyResponse) diff --git a/crates/delta/src/routes/channels/permissions_set_default.rs b/crates/delta/src/routes/channels/permissions_set_default.rs index ed82de816..91f53b2fb 100644 --- a/crates/delta/src/routes/channels/permissions_set_default.rs +++ b/crates/delta/src/routes/channels/permissions_set_default.rs @@ -76,7 +76,7 @@ pub async fn set_default_permissions( } let server = match channel.server() { - Some(server_id) => Some(Reference::from_unchecked(server_id).as_server(db).await?), + Some(server_id) => Some(Reference::from_unchecked(server_id.to_string()).as_server(db).await?), None => None }; diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index d86155be2..16ebbd963 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -157,8 +157,8 @@ pub async fn edit( roles, timeout, remove, - mut can_publish, - mut can_receive, + can_publish, + can_receive, voice_channel, } = data; From 367ac887f4a01b5c597557ae9ba876fec13b6ecc Mon Sep 17 00:00:00 2001 From: Zomatree Date: Mon, 17 Feb 2025 01:39:00 +0000 Subject: [PATCH 15/62] feat: support multiple voice nodes --- Revolt.toml | 19 ++++-- crates/core/config/Revolt.toml | 10 +-- crates/core/config/src/lib.rs | 9 ++- crates/core/database/src/voice/mod.rs | 38 ++++++++--- .../core/database/src/voice/voice_client.rs | 65 +++++++++++++------ crates/core/models/src/v0/channels.rs | 9 ++- crates/core/permissions/src/models/channel.rs | 1 + crates/core/result/src/axum.rs | 1 + crates/core/result/src/lib.rs | 1 + crates/core/result/src/rocket.rs | 1 + crates/daemons/voice-ingress/src/main.rs | 63 +++++++++++++----- crates/delta/src/main.rs | 2 +- .../src/routes/channels/channel_delete.rs | 12 ++-- .../routes/channels/group_remove_member.rs | 6 +- .../delta/src/routes/channels/voice_join.rs | 14 ++-- crates/delta/src/routes/root.rs | 7 +- crates/delta/src/routes/servers/ban_create.rs | 5 +- .../delta/src/routes/servers/member_edit.rs | 8 ++- .../delta/src/routes/servers/member_remove.rs | 6 +- .../delta/src/routes/servers/server_delete.rs | 15 +++-- 20 files changed, 209 insertions(+), 83 deletions(-) diff --git a/Revolt.toml b/Revolt.toml index 97da148d6..e5058c51b 100644 --- a/Revolt.toml +++ b/Revolt.toml @@ -20,10 +20,14 @@ api = "http://local.revolt.chat:14702" events = "ws://local.revolt.chat:14703" autumn = "http://local.revolt.chat:14704" january = "http://local.revolt.chat:14705" -livekit = "ws://local.revolt.chat:14706" voso_legacy = "" voso_legacy_ws = "" +# Public urls for livekit nodes +# each entry here should have a corresponding entry under `api.livekit.nodes` +[hosts.livekit] +worldwide = "ws://local.revolt.chat:14706" + [api] [api.smtp] @@ -38,9 +42,16 @@ port = 14025 use_tls = false [api.livekit] -url = "ws://local.revolt.chat:14706" -key = "" -secret = "" + +# Config for livekit nodes +# Make sure to change the secret when deploying +# The key and secret should match the values livekit is using +[api.livekit.nodes.worldwide] +url = "http://livekit" +lat = 0.0 +lon = 0.0 +key = "worldwide_key" +secret = "ZjCofRlfm6GGtjlifmNpCDkcQbEIIVC0" [files.s3] # S3 protocol endpoint diff --git a/crates/core/config/Revolt.toml b/crates/core/config/Revolt.toml index d298a2d7f..c2c2c2b3a 100644 --- a/crates/core/config/Revolt.toml +++ b/crates/core/config/Revolt.toml @@ -17,10 +17,11 @@ api = "http://local.revolt.chat/api" events = "ws://local.revolt.chat/ws" autumn = "http://local.revolt.chat/autumn" january = "http://local.revolt.chat/january" -livekit = "ws://local.revolt.chat/livekit" voso_legacy = "" voso_legacy_ws = "" +[hosts.livekit] + [rabbit] host = "rabbit" port = 5672 @@ -64,13 +65,8 @@ hcaptcha_sitekey = "" max_concurrent_connections = 50 [api.livekit] -# Livekit server url -url = "ws://local.revolt.chat/livekit" -# Livekit security key name -key = "" -# Livekit security secret value -secret = "" +[api.livekit.nodes] [pushd] # this changes the names of the queues to not overlap diff --git a/crates/core/config/src/lib.rs b/crates/core/config/src/lib.rs index 168788dcc..5e6404348 100644 --- a/crates/core/config/src/lib.rs +++ b/crates/core/config/src/lib.rs @@ -105,7 +105,7 @@ pub struct Hosts { pub events: String, pub autumn: String, pub january: String, - pub livekit: String + pub livekit: HashMap } #[derive(Deserialize, Debug, Clone)] @@ -176,7 +176,14 @@ pub struct ApiWorkers { #[derive(Deserialize, Debug, Clone)] pub struct ApiLiveKit { + pub nodes: HashMap +} + +#[derive(Deserialize, Debug, Clone)] +pub struct LiveKitNode { pub url: String, + pub lat: f64, + pub lon: f64, pub key: String, pub secret: String } diff --git a/crates/core/database/src/voice/mod.rs b/crates/core/database/src/voice/mod.rs index 18380660a..10ee8b168 100644 --- a/crates/core/database/src/voice/mod.rs +++ b/crates/core/database/src/voice/mod.rs @@ -35,6 +35,24 @@ pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> { } } +pub async fn set_channel_node(channel: &str, node: &str) -> Result<()> { + get_connection() + .await? + .set(format!("node:{channel}"), node) + .await + .to_internal_error()?; + + Ok(()) +} + +pub async fn get_channel_node(channel: &str) -> Result { + get_connection() + .await? + .get(format!("node:{channel}")) + .await + .to_internal_error() +} + pub async fn get_user_voice_channels(user_id: &str) -> Result> { get_connection() .await? @@ -207,10 +225,10 @@ pub async fn update_voice_state( Ok(()) } -pub async fn get_voice_channel_members(channel_id: &str) -> Result> { +pub async fn get_voice_channel_members(channel_id: &str) -> Result>> { get_connection() .await? - .smembers::<_, Vec>(format!("vc_members:{}", channel_id)) + .smembers(format!("vc_members:{}", channel_id)) .await .to_internal_error() } @@ -255,8 +273,9 @@ pub async fn get_channel_voice_state(channel: &Channel) -> Result None }; - if !members.is_empty() { + if let Some(members) = members { let mut participants = Vec::with_capacity(members.len()); + let node = get_channel_node(channel.id()).await?; for user_id in members { if let Some(voice_state) = get_voice_state(channel.id(), server, &user_id).await? { @@ -271,6 +290,7 @@ pub async fn get_channel_voice_state(channel: &Channel) -> Result Result<()> { } pub async fn sync_voice_permissions(db: &Database, voice_client: &VoiceClient, channel: &Channel, server: Option<&Server>, role_id: Option<&str>) -> Result<()> { + let node = get_channel_node(channel.id()).await?; - for user_id in get_voice_channel_members(channel.id()).await? { + for user_id in get_voice_channel_members(channel.id()).await?.iter().flatten() { let user = Reference::from_unchecked(user_id.clone()).as_user(db).await?; - sync_user_voice_permissions(db, voice_client, &user, channel, server, role_id).await?; + sync_user_voice_permissions(db, voice_client, &node, &user, channel, server, role_id).await?; }; Ok(()) } -pub async fn sync_user_voice_permissions(db: &Database, voice_client: &VoiceClient, user: &User, channel: &Channel, server: Option<&Server>, role_id: Option<&str>) -> Result<()> { +pub async fn sync_user_voice_permissions(db: &Database, voice_client: &VoiceClient, node: &str, user: &User, channel: &Channel, server: Option<&Server>, role_id: Option<&str>) -> Result<()> { let channel_id = channel.id(); let server_id: Option<&str> = server.as_ref().map(|s| s.id.as_str()); @@ -315,7 +336,8 @@ pub async fn sync_user_voice_permissions(db: &Database, voice_client: &VoiceClie let voice_state = get_voice_state(channel_id, server_id, &user.id).await?.unwrap(); let mut query = DatabasePermissionQuery::new(db, user) - .channel(channel); + .channel(channel) + .user(user); if let (Some(server), Some(member)) = (server, member.as_ref()) { query = query.member(member).server(server) @@ -340,7 +362,7 @@ pub async fn sync_user_voice_permissions(db: &Database, voice_client: &VoiceClie update_voice_state(channel_id, server_id, &user.id, &update_event).await?; - voice_client.update_permissions(user, channel_id, ParticipantPermission { + voice_client.update_permissions(node, user, channel_id, ParticipantPermission { can_subscribe: can_listen, can_publish: can_speak, can_publish_data: can_speak, diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs index a6b2ba033..ea6f52e80 100644 --- a/crates/core/database/src/voice/voice_client.rs +++ b/crates/core/database/src/voice/voice_client.rs @@ -1,9 +1,9 @@ use livekit_api::{ access_token::{AccessToken, VideoGrants}, - services::room::{CreateRoomOptions, RoomClient, UpdateParticipantOptions}, + services::room::{CreateRoomOptions, RoomClient as InnerRoomClient, UpdateParticipantOptions}, }; use livekit_protocol::{ParticipantInfo, ParticipantPermission, Room}; -use revolt_config::config; +use revolt_config::{config, LiveKitNode}; use crate::models::{Channel, User}; use revolt_models::v0; use revolt_permissions::{ChannelPermission, PermissionValue}; @@ -12,41 +12,59 @@ use std::{borrow::Cow, collections::HashMap, time::Duration}; use super::get_allowed_sources; + +#[derive(Debug)] +pub struct RoomClient { + pub client: InnerRoomClient, + pub node: LiveKitNode +} + #[derive(Debug)] pub struct VoiceClient { - rooms: RoomClient, - api_key: String, - api_secret: String, + pub rooms: HashMap } impl VoiceClient { - pub fn new(url: String, api_key: String, api_secret: String) -> Self { + pub fn new(nodes: HashMap) -> Self { Self { - rooms: RoomClient::with_api_key(&url, &api_key, &api_secret), - api_key, - api_secret, + rooms: nodes + .into_iter() + .map(|(name, node)| + ( + name, + RoomClient { + client: InnerRoomClient::with_api_key(&node.url, &node.key, &node.secret), + node + } + ) + ) + .collect() } } pub async fn from_revolt_config() -> Self { let config = config().await; - Self::new( - config.hosts.livekit, - config.api.livekit.key, - config.api.livekit.secret, - ) + Self::new(config.api.livekit.nodes.clone()) + } + + pub fn get_node(&self, name: &str) -> Result<&RoomClient> { + self.rooms.get(name) + .ok_or_else(|| create_error!(UnknownNode)) } pub fn create_token( &self, + node: &str, user: &User, permissions: PermissionValue, channel: &Channel, ) -> Result { + let room = self.get_node(node)?; + let allowed_sources = get_allowed_sources(permissions); - AccessToken::with_api_key(&self.api_key, &self.api_secret) + AccessToken::with_api_key(&room.node.key, &room.node.secret) .with_name(&format!("{}#{}", user.username, user.discriminator)) .with_identity(&user.id) .with_metadata(&serde_json::to_string(&user).to_internal_error()?) @@ -63,7 +81,9 @@ impl VoiceClient { .to_internal_error() } - pub async fn create_room(&self, channel: &Channel) -> Result { + pub async fn create_room(&self, node: &str, channel: &Channel) -> Result { + let room = self.get_node(node)?; + let voice = match channel { Channel::DirectMessage { .. } | Channel::VoiceChannel { .. } => Some(Cow::Owned(v0::VoiceInformation::default())), Channel::TextChannel { voice: Some(voice), .. } => Some(Cow::Borrowed(voice)), @@ -72,7 +92,7 @@ impl VoiceClient { .ok_or_else(|| create_error!(NotAVoiceChannel))?; - self.rooms + room.client .create_room( channel.id(), CreateRoomOptions { @@ -87,11 +107,14 @@ impl VoiceClient { pub async fn update_permissions( &self, + node: &str, user: &User, channel_id: &str, new_permissions: ParticipantPermission, ) -> Result { - self.rooms + let room = self.get_node(node)?; + + room.client .update_participant( channel_id, &user.id, @@ -106,8 +129,10 @@ impl VoiceClient { .to_internal_error() } - pub async fn remove_user(&self, user_id: &str, channel_id: &str) -> Result<()> { - self.rooms.remove_participant(channel_id, user_id) + pub async fn remove_user(&self, node: &str, user_id: &str, channel_id: &str) -> Result<()> { + let room = self.get_node(node)?; + + room.client.remove_participant(channel_id, user_id) .await .to_internal_error() } diff --git a/crates/core/models/src/v0/channels.rs b/crates/core/models/src/v0/channels.rs index 7947f1fa8..e4dafbe08 100644 --- a/crates/core/models/src/v0/channels.rs +++ b/crates/core/models/src/v0/channels.rs @@ -315,7 +315,14 @@ auto_derived!( pub struct ChannelVoiceState { pub id: String, /// The states of the users who are connected to the channel - pub participants: Vec + pub participants: Vec, + /// The node's node which the channel is currently using + pub node: String + } + + /// Join a voice channel + pub struct DataJoinCall { + pub node: String } ); diff --git a/crates/core/permissions/src/models/channel.rs b/crates/core/permissions/src/models/channel.rs index 0b2867ae8..252fb35a4 100644 --- a/crates/core/permissions/src/models/channel.rs +++ b/crates/core/permissions/src/models/channel.rs @@ -128,6 +128,7 @@ pub static DEFAULT_PERMISSION: Lazy = Lazy::new(|| { + ChannelPermission::Connect + ChannelPermission::Speak + ChannelPermission::Listen + + ChannelPermission::Video ) }); diff --git a/crates/core/result/src/axum.rs b/crates/core/result/src/axum.rs index 78c35eb76..813ba76ac 100644 --- a/crates/core/result/src/axum.rs +++ b/crates/core/result/src/axum.rs @@ -78,6 +78,7 @@ impl IntoResponse for Error { ErrorType::AlreadyInVoiceChannel => StatusCode::BAD_REQUEST, ErrorType::NotAVoiceChannel => StatusCode::BAD_REQUEST, ErrorType::AlreadyConnected => StatusCode::BAD_REQUEST, + ErrorType::UnknownNode => StatusCode::BAD_REQUEST, ErrorType::ProxyError => StatusCode::BAD_REQUEST, ErrorType::FileTooSmall => StatusCode::UNPROCESSABLE_ENTITY, ErrorType::FileTooLarge { .. } => StatusCode::UNPROCESSABLE_ENTITY, diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index e9d4f24b2..9be2de7e5 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -161,6 +161,7 @@ pub enum ErrorType { AlreadyInVoiceChannel, NotAVoiceChannel, AlreadyConnected, + UnknownNode, // ? Micro-service errors ProxyError, FileTooSmall, diff --git a/crates/core/result/src/rocket.rs b/crates/core/result/src/rocket.rs index 046b06843..78a7f8444 100644 --- a/crates/core/result/src/rocket.rs +++ b/crates/core/result/src/rocket.rs @@ -82,6 +82,7 @@ impl<'r> Responder<'r, 'static> for Error { ErrorType::AlreadyInVoiceChannel => Status::BadRequest, ErrorType::NotAVoiceChannel => Status::BadRequest, ErrorType::AlreadyConnected => Status::BadRequest, + ErrorType::UnknownNode => Status::BadRequest, ErrorType::ProxyError => Status::BadRequest, ErrorType::FileTooSmall => Status::UnprocessableEntity, ErrorType::FileTooLarge { .. } => Status::UnprocessableEntity, diff --git a/crates/daemons/voice-ingress/src/main.rs b/crates/daemons/voice-ingress/src/main.rs index 04ec6e092..6bf98c228 100644 --- a/crates/daemons/voice-ingress/src/main.rs +++ b/crates/daemons/voice-ingress/src/main.rs @@ -1,15 +1,16 @@ use std::env; -use livekit_protocol::{TrackType, WebhookEvent}; +use livekit_protocol::TrackType; +use livekit_api::{access_token::TokenVerifier, webhooks::WebhookReceiver}; use revolt_database::{ events::client::EventV1, util::reference::Reference, Database, DatabaseInfo, voice::{create_voice_state, delete_voice_state, update_voice_state_tracks, VoiceClient} }; -use rocket::{build, post, routes, serde::json::Json, Config, State}; +use rocket::{build, http::Status, post, request::{FromRequest, Outcome}, routes, Config, Request, State}; use rocket_empty::EmptyResponse; - -use revolt_result::{Result, ToRevoltError}; +use std::net::Ipv4Addr; +use revolt_result::{create_error, Error, Result, ToRevoltError}; #[rocket::main] async fn main() -> Result<(), rocket::Error> { @@ -17,13 +18,13 @@ async fn main() -> Result<(), rocket::Error> { let database = DatabaseInfo::Auto.connect().await.unwrap(); let voice_client = VoiceClient::from_revolt_config().await; - let _rocket = build() .manage(database) .manage(voice_client) .mount("/", routes![ingress]) .configure(Config { port: 8500, + address: Ipv4Addr::new(0, 0, 0, 0).into(), ..Default::default() }) .ignite() @@ -34,14 +35,44 @@ async fn main() -> Result<(), rocket::Error> { Ok(()) } -#[post("/", data = "")] -async fn ingress(db: &State, voice_client: &State, body: Json) -> Result { +struct AuthHeader<'a>(&'a str); + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for AuthHeader<'r> { + type Error = Error; + + async fn from_request(request: &'r Request<'_>) -> Outcome { + match request.headers().get("Authorization").next() { + Some(token) => Outcome::Success(Self(token)), + None => Outcome::Error((Status::Unauthorized, create_error!(NotAuthenticated))) + } + } +} + +impl std::ops::Deref for AuthHeader<'_> { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +#[post("/", data = "")] +async fn ingress(db: &State, node: &str, voice_client: &State, auth_header: AuthHeader<'_>, body: &str) -> Result { log::debug!("received event: {body:?}"); - let channel_id = body.room.as_ref().map(|r| &r.name); - let user_id = body.participant.as_ref().map(|r| &r.identity); + let config = revolt_config::config().await; + + let node_info = config.api.livekit.nodes.get(node) + .ok_or_else(|| create_error!(NotAuthenticated))?; + + let webhook_receiver = WebhookReceiver::new(TokenVerifier::with_api_key(&node_info.key, &node_info.secret)); + let event = webhook_receiver.receive(body, &auth_header).to_internal_error()?; + + let channel_id = event.room.as_ref().map(|r| &r.name); + let user_id = event.participant.as_ref().map(|r| &r.identity); - match body.event.as_str() { + match event.event.as_str() { "participant_joined" => { let channel_id = channel_id.to_internal_error()?; let user_id = user_id.to_internal_error()?; @@ -79,20 +110,22 @@ async fn ingress(db: &State, voice_client: &State, body: "track_published" | "track_unpublished" => { let channel_id = channel_id.to_internal_error()?; let user_id = user_id.to_internal_error()?; - let track = body.track.as_ref().to_internal_error()?; + let track = event.track.as_ref().to_internal_error()?; let channel = Reference::from_unchecked(channel_id.clone()) .as_channel(db) .await?; // remove the user if they try publish a video larger than 1080x720 or they publish data - if body.event == "track_published" + // TODO: move to config + if event.event == "track_published" && ( - (track.r#type == TrackType::Video as i32 && (track.width > 1080 || track.height > 720)) + // handle any size which goes over the limit of "1080x720" to stop people from making too tall or too wide and bypassing the limit + (track.r#type == TrackType::Video as i32 && (track.width * track.height) >= (1080 * 720)) | (track.r#type == TrackType::Data as i32) ) { - voice_client.remove_user(user_id, channel_id).await?; + voice_client.remove_user(node, user_id, channel_id).await?; delete_voice_state(channel_id, channel.server(), user_id).await?; } @@ -100,7 +133,7 @@ async fn ingress(db: &State, voice_client: &State, body: channel_id, channel.server(), user_id, - body.event == "track_published", // to avoid duplicating this entire case twice + event.event == "track_published", // to avoid duplicating this entire case twice track.source ).await?; diff --git a/crates/delta/src/main.rs b/crates/delta/src/main.rs index f9feb40b3..aaa769325 100644 --- a/crates/delta/src/main.rs +++ b/crates/delta/src/main.rs @@ -84,7 +84,7 @@ pub async fn web() -> Rocket { .into(); // Voice handler - let voice_client = VoiceClient::new(config.api.livekit.url.clone(), config.api.livekit.key.clone(), config.api.livekit.secret.clone()); + let voice_client = VoiceClient::new(config.api.livekit.nodes.clone()); // Configure Rabbit let connection = Connection::open(&OpenConnectionArguments::new( &config.rabbit.host, diff --git a/crates/delta/src/routes/channels/channel_delete.rs b/crates/delta/src/routes/channels/channel_delete.rs index f3dab86db..4264e47b1 100644 --- a/crates/delta/src/routes/channels/channel_delete.rs +++ b/crates/delta/src/routes/channels/channel_delete.rs @@ -1,5 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{delete_voice_state, get_voice_channel_members, VoiceClient}, Channel, Database, PartialChannel, User, AMQP + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{delete_voice_state, get_channel_node, get_voice_channel_members, VoiceClient}, Channel, Database, PartialChannel, User, AMQP }; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; @@ -53,9 +53,13 @@ pub async fn delete( } }; - for user_id in get_voice_channel_members(channel.id()).await? { - voice_client.remove_user(&user_id, channel.id()).await?; - delete_voice_state(channel.id(), channel.server(), &user.id).await?; + if let Some(users) = get_voice_channel_members(channel.id()).await? { + let node = get_channel_node(channel.id()).await?; + + for user in users { + voice_client.remove_user(&node, &user, channel.id()).await?; + delete_voice_state(channel.id(), channel.server(), &user).await?; + } }; Ok(EmptyResponse) diff --git a/crates/delta/src/routes/channels/group_remove_member.rs b/crates/delta/src/routes/channels/group_remove_member.rs index 9836a7c39..0f6291bb1 100644 --- a/crates/delta/src/routes/channels/group_remove_member.rs +++ b/crates/delta/src/routes/channels/group_remove_member.rs @@ -1,4 +1,4 @@ -use revolt_database::{util::reference::Reference, voice::{delete_voice_state, is_in_voice_channel, raise_if_in_voice, VoiceClient}, Channel, Database, User, AMQP}; +use revolt_database::{util::reference::Reference, voice::{delete_voice_state, get_channel_node, is_in_voice_channel, raise_if_in_voice, VoiceClient}, Channel, Database, User, AMQP}; use revolt_permissions::ChannelPermission; use revolt_result::{create_error, Result}; @@ -48,7 +48,9 @@ pub async fn remove_member( }; if is_in_voice_channel(&user.id, channel.id()).await? { - voice_client.remove_user(&user.id, channel.id()).await?; + let node = get_channel_node(channel.id()).await?; + + voice_client.remove_user(&node, &user.id, channel.id()).await?; delete_voice_state(channel.id(), None, &user.id).await?; }; diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index c2f9f3096..313590885 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -1,7 +1,7 @@ use revolt_models::v0; use revolt_database::{util::{permissions::perms, reference::Reference}, voice::{raise_if_in_voice, VoiceClient}, Channel, Database, SystemMessage, User, AMQP}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; -use revolt_result::Result; +use revolt_result::{create_error, Result}; use rocket::{serde::json::Json, State}; @@ -9,8 +9,12 @@ use rocket::{serde::json::Json, State}; /// /// Asks the voice server for a token to join the call. #[openapi(tag = "Voice")] -#[post("//join_call")] -pub async fn call(db: &State, amqp: &State, voice: &State, user: User, target: Reference) -> Result> { +#[post("//join_call", data="")] +pub async fn call(db: &State, amqp: &State, voice: &State, user: User, target: Reference, data: Json) -> Result> { + if !voice.rooms.contains_key(&data.node) { + return Err(create_error!(UnknownNode)) + } + let channel = target.as_channel(db).await?; raise_if_in_voice(&user, channel.id()).await?; @@ -20,8 +24,8 @@ pub async fn call(db: &State, amqp: &State, voice: &State Result> { url: config.hosts.january, }, livekit: VoiceFeature { - enabled: !config.hosts.livekit.is_empty(), - url: config.hosts.livekit.to_string(), + enabled: !config.hosts.livekit.is_empty() }, }, ws: config.hosts.events, diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index 96a8d4c92..491347bb3 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -1,6 +1,6 @@ use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}, + voice::{delete_voice_state, get_channel_node, get_user_voice_channel_in_server, VoiceClient}, Database, RemovalIntention, ServerBan, User, }; use revolt_models::v0; @@ -61,7 +61,8 @@ pub async fn ban( // If the member is in a voice channel while banned kick them from the voice channel if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { - voice_client.remove_user(&user.id, &channel_id).await?; + let node = get_channel_node(&channel_id).await?; + voice_client.remove_user(&node, &user.id, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await? } } diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index 16ebbd963..e08bbfd52 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -7,7 +7,7 @@ use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use revolt_database::{ events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::{get_user_voice_channel_in_server, move_user, sync_user_voice_permissions, VoiceClient}, + voice::{get_channel_node, get_user_voice_channel_in_server, move_user, sync_user_voice_permissions, VoiceClient}, Database, File, PartialMember, User, }; use revolt_models::v0::{self, FieldsMember, PartialUserVoiceState}; @@ -185,6 +185,8 @@ pub async fn edit( partial.avatar = Some(File::use_user_avatar(db, &avatar, &user.id, &user.id).await?); } + // TODO: impelement moving users + let remove_contains_voice = remove .as_ref() .map(|r| r.contains(FieldsMember::CanPublish) || r.contains(FieldsMember::CanReceive)) @@ -196,9 +198,11 @@ pub async fn edit( || remove_contains_voice { if let Some(channel) = get_user_voice_channel_in_server(&target_user.id, &server.id).await? { + let node = get_channel_node(&channel).await?; let channel = Reference::from_unchecked(channel).as_channel(db).await?; - sync_user_voice_permissions(db, voice_client, &user, &channel, Some(&server), None).await?; + + sync_user_voice_permissions(db, voice_client, &node, &user, &channel, Some(&server), None).await?; }; }; diff --git a/crates/delta/src/routes/servers/member_remove.rs b/crates/delta/src/routes/servers/member_remove.rs index 3276add74..9315b1b4a 100644 --- a/crates/delta/src/routes/servers/member_remove.rs +++ b/crates/delta/src/routes/servers/member_remove.rs @@ -1,6 +1,6 @@ use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}, + voice::{delete_voice_state, get_channel_node, get_user_voice_channel_in_server, VoiceClient}, Database, RemovalIntention, User, }; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; @@ -43,7 +43,9 @@ pub async fn kick( } if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { - voice_client.remove_user(&user.id, &channel_id).await?; + let node = get_channel_node(&channel_id).await?; + + voice_client.remove_user(&node, &user.id, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; } diff --git a/crates/delta/src/routes/servers/server_delete.rs b/crates/delta/src/routes/servers/server_delete.rs index 1cae301c8..abf6724f7 100644 --- a/crates/delta/src/routes/servers/server_delete.rs +++ b/crates/delta/src/routes/servers/server_delete.rs @@ -1,6 +1,6 @@ use revolt_database::{ util::reference::Reference, - voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}, + voice::{delete_voice_state, get_channel_node, get_user_voice_channel_in_server, get_voice_channel_members, VoiceClient}, Database, RemovalIntention, User, }; use revolt_models::v0; @@ -26,15 +26,22 @@ pub async fn delete( if server.owner == user.id { for channel_id in &server.channels { - voice_client.remove_user(&user.id, channel_id).await?; - delete_voice_state(channel_id, Some(&server.id), &user.id).await?; + if let Some(users) = get_voice_channel_members(channel_id).await? { + let node = get_channel_node(channel_id).await?; + + for user in users { + voice_client.remove_user(&node, &user, channel_id).await?; + delete_voice_state(channel_id, Some(&server.id), &user).await?; + } + } } server.delete(db).await } else { if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { if server.channels.iter().any(|c| c == &channel_id) { - voice_client.remove_user(&user.id, &channel_id).await?; + let node = get_channel_node(&channel_id).await?; + voice_client.remove_user(&node, &user.id, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; } }; From 951128032bfdf7f8d74f4098cb41a511d2bc8b19 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Mon, 17 Feb 2025 01:48:34 +0000 Subject: [PATCH 16/62] fix: expose list of nodes --- crates/delta/src/routes/root.rs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/crates/delta/src/routes/root.rs b/crates/delta/src/routes/root.rs index d928a8aad..6b42497c9 100644 --- a/crates/delta/src/routes/root.rs +++ b/crates/delta/src/routes/root.rs @@ -21,11 +21,22 @@ pub struct Feature { pub url: String, } +/// # Information about a livekit node +#[derive(Serialize, JsonSchema, Debug)] +pub struct VoiceNode { + pub name: String, + pub lat: f64, + pub lon: f64, + pub public_url: String +} + /// # Voice Server Configuration #[derive(Serialize, JsonSchema, Debug)] pub struct VoiceFeature { /// Whether voice is enabled - pub enabled: bool + pub enabled: bool, + /// All livekit nodes + pub nodes: Vec, } /// # Feature Configuration @@ -90,20 +101,29 @@ pub async fn root() -> Result> { features: RevoltFeatures { captcha: CaptchaFeature { enabled: !config.api.security.captcha.hcaptcha_key.is_empty(), - key: config.api.security.captcha.hcaptcha_sitekey, + key: config.api.security.captcha.hcaptcha_sitekey.clone(), }, email: !config.api.smtp.host.is_empty(), invite_only: config.api.registration.invite_only, autumn: Feature { enabled: !config.hosts.autumn.is_empty(), - url: config.hosts.autumn, + url: config.hosts.autumn.clone(), }, january: Feature { enabled: !config.hosts.january.is_empty(), - url: config.hosts.january, + url: config.hosts.january.clone(), }, livekit: VoiceFeature { - enabled: !config.hosts.livekit.is_empty() + enabled: !config.hosts.livekit.is_empty(), + nodes: config.api.livekit.nodes.iter().map(|(name, value)| { + VoiceNode { + name: name.clone(), + lat: value.lat, + lon: value.lon, + public_url: config.hosts.livekit.get(name).expect("Missing corresponding host for voice node").clone() + } + }) + .collect() }, }, ws: config.hosts.events, From 9c2cc8736c4ae3171434e8031ee52c5bd0a3927e Mon Sep 17 00:00:00 2001 From: Zomatree Date: Mon, 17 Feb 2025 02:11:32 +0000 Subject: [PATCH 17/62] fix: respond with the url when joining a voice channel --- crates/core/models/src/v0/channels.rs | 2 ++ crates/delta/src/routes/channels/voice_join.rs | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/core/models/src/v0/channels.rs b/crates/core/models/src/v0/channels.rs index e4dafbe08..efd65aeb8 100644 --- a/crates/core/models/src/v0/channels.rs +++ b/crates/core/models/src/v0/channels.rs @@ -309,6 +309,8 @@ auto_derived!( pub struct CreateVoiceUserResponse { /// Token for authenticating with the voice server pub token: String, + /// Url of the livekit server to connect to + pub url: String } /// Voice state for a channel diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index 313590885..eebbdc719 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -1,3 +1,4 @@ +use revolt_config::config; use revolt_models::v0; use revolt_database::{util::{permissions::perms, reference::Reference}, voice::{raise_if_in_voice, VoiceClient}, Channel, Database, SystemMessage, User, AMQP}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; @@ -11,9 +12,10 @@ use rocket::{serde::json::Json, State}; #[openapi(tag = "Voice")] #[post("//join_call", data="")] pub async fn call(db: &State, amqp: &State, voice: &State, user: User, target: Reference, data: Json) -> Result> { - if !voice.rooms.contains_key(&data.node) { - return Err(create_error!(UnknownNode)) - } + let config = config().await; + + let node_host = config.hosts.livekit.get(&data.node) + .ok_or_else(|| create_error!(UnknownNode))?; let channel = target.as_channel(db).await?; @@ -50,5 +52,5 @@ pub async fn call(db: &State, amqp: &State, voice: &State {} }; - Ok(Json(v0::CreateVoiceUserResponse { token })) + Ok(Json(v0::CreateVoiceUserResponse { token, url: node_host.clone() })) } From 8b5e085232f53348b2fb36641bb13c30da2aa5f5 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Sat, 1 Mar 2025 22:09:40 +0000 Subject: [PATCH 18/62] feat: being moved between voice channels --- crates/core/database/src/events/client.rs | 5 ++ crates/core/database/src/voice/mod.rs | 24 +++++- .../core/database/src/voice/voice_client.rs | 2 +- crates/core/result/src/axum.rs | 2 +- crates/core/result/src/lib.rs | 2 +- crates/core/result/src/rocket.rs | 2 +- crates/daemons/voice-ingress/src/main.rs | 26 +++++-- .../src/routes/channels/channel_delete.rs | 2 +- .../routes/channels/group_remove_member.rs | 4 +- crates/delta/src/routes/servers/ban_create.rs | 2 +- .../delta/src/routes/servers/member_edit.rs | 78 ++++++++++++------- .../delta/src/routes/servers/member_remove.rs | 2 +- .../delta/src/routes/servers/server_delete.rs | 4 +- 13 files changed, 106 insertions(+), 49 deletions(-) diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index 99781ea43..22ef110fa 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -258,11 +258,16 @@ pub enum EventV1 { user: String, from: String, to: String, + state: UserVoiceState }, UserVoiceStateUpdate { id: String, channel_id: String, data: PartialUserVoiceState, + }, + UserMoveVoiceChannel { + node: String, + token: String } } diff --git a/crates/core/database/src/voice/mod.rs b/crates/core/database/src/voice/mod.rs index 10ee8b168..b1e797e46 100644 --- a/crates/core/database/src/voice/mod.rs +++ b/crates/core/database/src/voice/mod.rs @@ -29,7 +29,7 @@ pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> { .to_internal_error()? > 0 { - Err(create_error!(AlreadyInVoiceChannel)) + Err(create_error!(NotConnected)) } else { Ok(()) } @@ -45,7 +45,7 @@ pub async fn set_channel_node(channel: &str, node: &str) -> Result<()> { Ok(()) } -pub async fn get_channel_node(channel: &str) -> Result { +pub async fn get_channel_node(channel: &str) -> Result> { get_connection() .await? .get(format!("node:{channel}")) @@ -61,6 +61,22 @@ pub async fn get_user_voice_channels(user_id: &str) -> Result> { .to_internal_error() } +pub async fn set_user_moved_from_voice(old_channel: &str, new_channel: &str, user_id: &str) -> Result<()> { + get_connection() + .await? + .set_ex(format!("moved_to:{user_id}:{new_channel}"), old_channel, 10) + .await + .to_internal_error() +} + +pub async fn get_user_moved_from_voice(channel_id: &str, user_id: &str) -> Result> { + get_connection() + .await? + .get(format!("moved_to:{user_id}:{channel_id}")) + .await + .to_internal_error() +} + pub async fn is_in_voice_channel(user_id: &str, channel_id: &str) -> Result { get_connection() .await? @@ -275,7 +291,7 @@ pub async fn get_channel_voice_state(channel: &Channel) -> Result Result<()> { } pub async fn sync_voice_permissions(db: &Database, voice_client: &VoiceClient, channel: &Channel, server: Option<&Server>, role_id: Option<&str>) -> Result<()> { - let node = get_channel_node(channel.id()).await?; + let node = get_channel_node(channel.id()).await?.unwrap(); for user_id in get_voice_channel_members(channel.id()).await?.iter().flatten() { let user = Reference::from_unchecked(user_id.clone()).as_user(db).await?; diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs index ea6f52e80..52733c8bc 100644 --- a/crates/core/database/src/voice/voice_client.rs +++ b/crates/core/database/src/voice/voice_client.rs @@ -97,7 +97,7 @@ impl VoiceClient { channel.id(), CreateRoomOptions { max_participants: voice.max_users.unwrap_or(u32::MAX), - empty_timeout: 5 * 60, // 5 minutes + empty_timeout: 5 * 60, // 5 minutes, ..Default::default() }, ) diff --git a/crates/core/result/src/axum.rs b/crates/core/result/src/axum.rs index 813ba76ac..c04d9aa5c 100644 --- a/crates/core/result/src/axum.rs +++ b/crates/core/result/src/axum.rs @@ -75,7 +75,7 @@ impl IntoResponse for Error { ErrorType::NoEffect => StatusCode::OK, ErrorType::FailedValidation { .. } => StatusCode::BAD_REQUEST, ErrorType::LiveKitUnavailable => StatusCode::BAD_REQUEST, - ErrorType::AlreadyInVoiceChannel => StatusCode::BAD_REQUEST, + ErrorType::NotConnected => StatusCode::BAD_REQUEST, ErrorType::NotAVoiceChannel => StatusCode::BAD_REQUEST, ErrorType::AlreadyConnected => StatusCode::BAD_REQUEST, ErrorType::UnknownNode => StatusCode::BAD_REQUEST, diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index 9be2de7e5..84eaaaa18 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -158,9 +158,9 @@ pub enum ErrorType { // ? Voice errors LiveKitUnavailable, - AlreadyInVoiceChannel, NotAVoiceChannel, AlreadyConnected, + NotConnected, UnknownNode, // ? Micro-service errors ProxyError, diff --git a/crates/core/result/src/rocket.rs b/crates/core/result/src/rocket.rs index 78a7f8444..da01649b9 100644 --- a/crates/core/result/src/rocket.rs +++ b/crates/core/result/src/rocket.rs @@ -79,9 +79,9 @@ impl<'r> Responder<'r, 'static> for Error { ErrorType::NoEffect => Status::Ok, ErrorType::FailedValidation { .. } => Status::BadRequest, ErrorType::LiveKitUnavailable => Status::BadRequest, - ErrorType::AlreadyInVoiceChannel => Status::BadRequest, ErrorType::NotAVoiceChannel => Status::BadRequest, ErrorType::AlreadyConnected => Status::BadRequest, + ErrorType::NotConnected => Status::BadRequest, ErrorType::UnknownNode => Status::BadRequest, ErrorType::ProxyError => Status::BadRequest, ErrorType::FileTooSmall => Status::UnprocessableEntity, diff --git a/crates/daemons/voice-ingress/src/main.rs b/crates/daemons/voice-ingress/src/main.rs index 6bf98c228..9602fa1ca 100644 --- a/crates/daemons/voice-ingress/src/main.rs +++ b/crates/daemons/voice-ingress/src/main.rs @@ -4,8 +4,7 @@ use std::env; use livekit_protocol::TrackType; use livekit_api::{access_token::TokenVerifier, webhooks::WebhookReceiver}; use revolt_database::{ - events::client::EventV1, util::reference::Reference, Database, DatabaseInfo, - voice::{create_voice_state, delete_voice_state, update_voice_state_tracks, VoiceClient} + events::client::EventV1, util::reference::Reference, voice::{create_voice_state, delete_voice_state, get_user_moved_from_voice, update_voice_state_tracks, VoiceClient}, Database, DatabaseInfo }; use rocket::{build, http::Status, post, request::{FromRequest, Outcome}, routes, Config, Request, State}; use rocket_empty::EmptyResponse; @@ -83,12 +82,23 @@ async fn ingress(db: &State, node: &str, voice_client: &State { let channel_id = channel_id.to_internal_error()?; diff --git a/crates/delta/src/routes/channels/channel_delete.rs b/crates/delta/src/routes/channels/channel_delete.rs index 4264e47b1..823e02331 100644 --- a/crates/delta/src/routes/channels/channel_delete.rs +++ b/crates/delta/src/routes/channels/channel_delete.rs @@ -54,7 +54,7 @@ pub async fn delete( }; if let Some(users) = get_voice_channel_members(channel.id()).await? { - let node = get_channel_node(channel.id()).await?; + let node = get_channel_node(channel.id()).await?.unwrap(); for user in users { voice_client.remove_user(&node, &user, channel.id()).await?; diff --git a/crates/delta/src/routes/channels/group_remove_member.rs b/crates/delta/src/routes/channels/group_remove_member.rs index 0f6291bb1..fc63acc16 100644 --- a/crates/delta/src/routes/channels/group_remove_member.rs +++ b/crates/delta/src/routes/channels/group_remove_member.rs @@ -48,8 +48,8 @@ pub async fn remove_member( }; if is_in_voice_channel(&user.id, channel.id()).await? { - let node = get_channel_node(channel.id()).await?; - + let node = get_channel_node(channel.id()).await?.unwrap(); + voice_client.remove_user(&node, &user.id, channel.id()).await?; delete_voice_state(channel.id(), None, &user.id).await?; }; diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index 491347bb3..26881a306 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -61,7 +61,7 @@ pub async fn ban( // If the member is in a voice channel while banned kick them from the voice channel if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { - let node = get_channel_node(&channel_id).await?; + let node = get_channel_node(&channel_id).await?.unwrap(); voice_client.remove_user(&node, &user.id, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await? } diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index e08bbfd52..b23db2208 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -6,13 +6,13 @@ use livekit_protocol::ParticipantPermission; use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use revolt_database::{ events::client::EventV1, - util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::{get_channel_node, get_user_voice_channel_in_server, move_user, sync_user_voice_permissions, VoiceClient}, + util::{permissions::{perms, DatabasePermissionQuery}, reference::Reference}, + voice::{get_channel_node, get_user_voice_channel_in_server, move_user, set_channel_node, set_user_moved_from_voice, sync_user_voice_permissions, VoiceClient}, Database, File, PartialMember, User, }; use revolt_models::v0::{self, FieldsMember, PartialUserVoiceState}; -use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_permissions::{calculate_channel_permissions, calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result, ToRevoltError}; use rocket::{form::validate::Contains, serde::json::Json, State}; use validator::Validate; @@ -103,7 +103,7 @@ pub async fn edit( permissions.throw_if_lacking_channel_permission(ChannelPermission::DeafenMembers)?; } - if let Some(new_channel) = &data.voice_channel { + let new_voice_channel = if let Some(new_channel) = &data.voice_channel { permissions.throw_if_lacking_channel_permission(ChannelPermission::MoveMembers)?; // ensure the channel we are moving them to is in the server and is a voice channel @@ -113,14 +113,18 @@ pub async fn edit( .await .map_err(|_| create_error!(UnknownChannel))?; - if !channel.server().is_some_and(|v| v == member.id.server) { + if channel.server().is_none_or(|v| v != member.id.server) { Err(create_error!(UnknownChannel))? } - if channel.voice().is_none() { - Err(create_error!(NotAVoiceChannel))? - } - } + if get_user_voice_channel_in_server(&target_user.id, &server.id).await?.is_none() { + Err(create_error!(NotConnected))? + }; + + Some(channel) + } else { + None + }; // Resolve our ranking let our_ranking = query.get_member_rank().unwrap_or(i64::MIN); @@ -159,7 +163,7 @@ pub async fn edit( remove, can_publish, can_receive, - voice_channel, + voice_channel: _, } = data; let mut partial = PartialMember { @@ -185,27 +189,11 @@ pub async fn edit( partial.avatar = Some(File::use_user_avatar(db, &avatar, &user.id, &user.id).await?); } - // TODO: impelement moving users - let remove_contains_voice = remove .as_ref() .map(|r| r.contains(FieldsMember::CanPublish) || r.contains(FieldsMember::CanReceive)) .unwrap_or_default(); - if can_publish.is_some() - || can_receive.is_some() - || voice_channel.is_some() - || remove_contains_voice - { - if let Some(channel) = get_user_voice_channel_in_server(&target_user.id, &server.id).await? { - let node = get_channel_node(&channel).await?; - let channel = Reference::from_unchecked(channel).as_channel(db).await?; - - - sync_user_voice_permissions(db, voice_client, &node, &user, &channel, Some(&server), None).await?; - }; - }; - member .update( db, @@ -216,5 +204,43 @@ pub async fn edit( ) .await?; + if let Some(new_voice_channel) = new_voice_channel { + if let Some(channel) = get_user_voice_channel_in_server(&target_user.id, &server.id).await? { + let old_node = get_channel_node(&channel).await?.unwrap(); + + let new_node = match get_channel_node(new_voice_channel.id()).await? { + Some(node) => node, + None => { + set_channel_node(new_voice_channel.id(), &old_node).await?; + old_node.clone() + } + }; + + set_user_moved_from_voice(&channel, new_voice_channel.id(), &target_user.id).await?; + + let mut query = perms(db, &target_user).channel(&new_voice_channel); + let permissions = calculate_channel_permissions(&mut query).await; + + voice_client.create_room(&new_node, &new_voice_channel).await?; + let token = voice_client.create_token(&new_node, &target_user, permissions, &new_voice_channel)?; + + voice_client.remove_user(&old_node, &target_user.id, &channel).await?; + + EventV1::UserMoveVoiceChannel { + node: new_node, + token + } + .p_user(target_user.id.clone(), db) + .await; + }; + } else if can_publish.is_some() || can_receive.is_some() || remove_contains_voice { + if let Some(channel) = get_user_voice_channel_in_server(&target_user.id, &server.id).await? { + let node = get_channel_node(&channel).await?.unwrap(); + let channel = Reference::from_unchecked(channel).as_channel(db).await?; + + sync_user_voice_permissions(db, voice_client, &node, &user, &channel, Some(&server), None).await?; + }; + }; + Ok(Json(member.into())) } diff --git a/crates/delta/src/routes/servers/member_remove.rs b/crates/delta/src/routes/servers/member_remove.rs index 9315b1b4a..1f8ea9eef 100644 --- a/crates/delta/src/routes/servers/member_remove.rs +++ b/crates/delta/src/routes/servers/member_remove.rs @@ -43,7 +43,7 @@ pub async fn kick( } if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { - let node = get_channel_node(&channel_id).await?; + let node = get_channel_node(&channel_id).await?.unwrap(); voice_client.remove_user(&node, &user.id, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; diff --git a/crates/delta/src/routes/servers/server_delete.rs b/crates/delta/src/routes/servers/server_delete.rs index abf6724f7..667a171fa 100644 --- a/crates/delta/src/routes/servers/server_delete.rs +++ b/crates/delta/src/routes/servers/server_delete.rs @@ -27,7 +27,7 @@ pub async fn delete( if server.owner == user.id { for channel_id in &server.channels { if let Some(users) = get_voice_channel_members(channel_id).await? { - let node = get_channel_node(channel_id).await?; + let node = get_channel_node(channel_id).await?.unwrap(); for user in users { voice_client.remove_user(&node, &user, channel_id).await?; @@ -40,7 +40,7 @@ pub async fn delete( } else { if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { if server.channels.iter().any(|c| c == &channel_id) { - let node = get_channel_node(&channel_id).await?; + let node = get_channel_node(&channel_id).await?.unwrap(); voice_client.remove_user(&node, &user.id, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; } From 091aa4705bbcd9a4a59f394c142c851e054452ba Mon Sep 17 00:00:00 2001 From: Zomatree Date: Tue, 25 Mar 2025 20:37:06 +0000 Subject: [PATCH 19/62] fix: use existing node if someone is already in the voice channel --- .../core/database/src/voice/voice_client.rs | 7 ++++-- crates/core/models/src/v0/channels.rs | 2 +- .../delta/src/routes/channels/voice_join.rs | 22 ++++++++++++++----- .../delta/src/routes/servers/member_edit.rs | 4 ++++ 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs index 52733c8bc..0b2ecdbec 100644 --- a/crates/core/database/src/voice/voice_client.rs +++ b/crates/core/database/src/voice/voice_client.rs @@ -42,6 +42,10 @@ impl VoiceClient { } } + pub fn is_enabled(&self) -> bool { + !self.rooms.is_empty() + } + pub async fn from_revolt_config() -> Self { let config = config().await; @@ -89,8 +93,7 @@ impl VoiceClient { Channel::TextChannel { voice: Some(voice), .. } => Some(Cow::Borrowed(voice)), _ => None } - - .ok_or_else(|| create_error!(NotAVoiceChannel))?; + .ok_or_else(|| create_error!(NotAVoiceChannel))?; room.client .create_room( diff --git a/crates/core/models/src/v0/channels.rs b/crates/core/models/src/v0/channels.rs index efd65aeb8..ecd895c42 100644 --- a/crates/core/models/src/v0/channels.rs +++ b/crates/core/models/src/v0/channels.rs @@ -324,7 +324,7 @@ auto_derived!( /// Join a voice channel pub struct DataJoinCall { - pub node: String + pub node: Option } ); diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index eebbdc719..7c3c86cd2 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -1,6 +1,6 @@ use revolt_config::config; use revolt_models::v0; -use revolt_database::{util::{permissions::perms, reference::Reference}, voice::{raise_if_in_voice, VoiceClient}, Channel, Database, SystemMessage, User, AMQP}; +use revolt_database::{util::{permissions::perms, reference::Reference}, voice::{get_channel_node, raise_if_in_voice, VoiceClient}, Channel, Database, SystemMessage, User, AMQP}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; @@ -12,10 +12,11 @@ use rocket::{serde::json::Json, State}; #[openapi(tag = "Voice")] #[post("//join_call", data="")] pub async fn call(db: &State, amqp: &State, voice: &State, user: User, target: Reference, data: Json) -> Result> { - let config = config().await; + if !voice.is_enabled() { + return Err(create_error!(LiveKitUnavailable)) + } - let node_host = config.hosts.livekit.get(&data.node) - .ok_or_else(|| create_error!(UnknownNode))?; + let config = config().await; let channel = target.as_channel(db).await?; @@ -26,8 +27,17 @@ pub async fn call(db: &State, amqp: &State, voice: &State Date: Tue, 29 Apr 2025 15:05:18 +0100 Subject: [PATCH 20/62] refactor: Remove VoiceChannel --- crates/bonfire/src/events/impl.rs | 2 + .../admin_migrations/ops/mongodb/scripts.rs | 21 +- .../src/models/channel_invites/model.rs | 2 +- .../database/src/models/channels/model.rs | 45 ++--- .../src/models/channels/ops/mongodb.rs | 2 +- .../src/models/channels/ops/reference.rs | 3 - .../database/src/models/messages/model.rs | 1 + crates/core/database/src/util/bridge/v0.rs | 4 + .../database/src/util/bulk_permissions.rs | 15 +- crates/core/database/src/util/permissions.rs | 16 +- crates/core/database/src/voice/mod.rs | 5 +- .../core/database/src/voice/voice_client.rs | 2 +- crates/core/models/src/v0/channels.rs | 8 +- .../pushd/src/consumers/outbound/apn.rs | 1 + .../pushd/src/consumers/outbound/fcm.rs | 1 + .../src/routes/channels/channel_delete.rs | 1 + .../delta/src/routes/channels/channel_edit.rs | 186 +++++++++++------- .../src/routes/channels/message_query.rs | 1 + .../src/routes/channels/message_search.rs | 1 + .../delta/src/routes/channels/message_send.rs | 1 + .../channels/permissions_set_default.rs | 4 - .../delta/src/routes/invites/invite_fetch.rs | 7 - 22 files changed, 176 insertions(+), 153 deletions(-) diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index 5c974fb65..5be05c722 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -18,6 +18,7 @@ use super::state::{Cache, State}; impl Cache { /// Check whether the current user can view a channel pub async fn can_view_channel(&self, db: &Database, channel: &Channel) -> bool { + #[allow(deprecated)] match &channel { Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => { let member = self.members.get(server); @@ -284,6 +285,7 @@ impl State { let id = &id.to_string(); for (channel_id, channel) in &self.cache.channels { + #[allow(deprecated)] match channel { Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => { if server == id { diff --git a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs index c83d8d8c5..f66adf143 100644 --- a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs +++ b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs @@ -21,7 +21,7 @@ struct MigrationInfo { revision: i32, } -pub const LATEST_REVISION: i32 = 31; +pub const LATEST_REVISION: i32 = 33; pub async fn migrate_database(db: &MongoDb) { let migrations = db.col::("migrations"); @@ -1185,6 +1185,7 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { .await; for webhook in webhooks { + #[allow(deprecated)] match db.fetch_channel(&webhook.channel_id).await { Ok(channel) => { let creator_id = match channel { @@ -1246,6 +1247,24 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { .expect("Failed to update members"); } + if revision <= 33 { + info!("Running migration [revision 33 / 29-04-2025]: Convert all `VoiceChannel`'s into `TextChannel` "); + + db.col::("channels") + .update_many( + doc! { "channel_type": "VoiceChannel" }, + doc! { + "$set": { + "channel_type": "TextChannel", + "voice": {} + } + }, + None + ) + .await + .expect("Failed to update voice channels"); + }; + // Reminder to update LATEST_REVISION when adding new migrations. LATEST_REVISION.max(revision) } diff --git a/crates/core/database/src/models/channel_invites/model.rs b/crates/core/database/src/models/channel_invites/model.rs index e3af921ab..a5d877fee 100644 --- a/crates/core/database/src/models/channel_invites/model.rs +++ b/crates/core/database/src/models/channel_invites/model.rs @@ -69,7 +69,7 @@ impl Invite { creator: creator.id.clone(), channel: id.clone(), }), - Channel::TextChannel { id, server, .. } | Channel::VoiceChannel { id, server, .. } => { + Channel::TextChannel { id, server, .. } => { Ok(Invite::Server { code, creator: creator.id.clone(), diff --git a/crates/core/database/src/models/channels/model.rs b/crates/core/database/src/models/channels/model.rs index 8af881ff4..6aa55be83 100644 --- a/crates/core/database/src/models/channels/model.rs +++ b/crates/core/database/src/models/channels/model.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use std::{borrow::Cow, collections::HashMap}; use revolt_config::config; @@ -108,7 +109,7 @@ auto_derived!( #[serde(skip_serializing_if = "Option::is_none")] voice: Option }, - /// Voice channel belonging to a server + #[deprecated = "Use TextChannel { voice } instead"] VoiceChannel { /// Unique Id #[serde(rename = "_id")] @@ -165,6 +166,8 @@ auto_derived!( pub default_permissions: Option, #[serde(skip_serializing_if = "Option::is_none")] pub last_message_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub voice: Option } /// Optional fields on channel object @@ -225,15 +228,17 @@ impl Channel { nsfw: data.nsfw.unwrap_or(false), voice: data.voice }, - v0::LegacyServerChannelType::Voice => Channel::VoiceChannel { + v0::LegacyServerChannelType::Voice => 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), + voice: Some(data.voice.unwrap_or_default()) }, }; @@ -464,12 +469,6 @@ impl Channel { server, role_permissions, .. - } - | Channel::VoiceChannel { - id, - server, - role_permissions, - .. } => { db.set_channel_role_permission(id, role_id, permissions) .await?; @@ -516,7 +515,7 @@ impl Channel { clear: remove.into_iter().map(|v| v.into()).collect(), } .p(match self { - Self::TextChannel { server, .. } | Self::VoiceChannel { server, .. } => server.clone(), + Self::TextChannel { server, .. } => server.clone(), _ => id, }) .await; @@ -529,16 +528,14 @@ impl Channel { match field { FieldsChannel::Description => match self { Self::Group { description, .. } - | Self::TextChannel { description, .. } - | Self::VoiceChannel { description, .. } => { + | Self::TextChannel { description, .. } => { description.take(); } _ => {} }, FieldsChannel::Icon => match self { Self::Group { icon, .. } - | Self::TextChannel { icon, .. } - | Self::VoiceChannel { icon, .. } => { + | Self::TextChannel { icon, .. } => { icon.take(); } _ => {} @@ -547,10 +544,6 @@ impl Channel { Self::TextChannel { default_permissions, .. - } - | Self::VoiceChannel { - default_permissions, - .. } => { default_permissions.take(); } @@ -567,6 +560,7 @@ impl Channel { } /// Apply partial channel to channel + #[allow(deprecated)] pub fn apply_options(&mut self, partial: PartialChannel) { match self { Self::SavedMessages { .. } => {} @@ -615,15 +609,7 @@ impl Channel { nsfw, default_permissions, role_permissions, - .. - } - | Self::VoiceChannel { - name, - description, - icon, - nsfw, - default_permissions, - role_permissions, + voice, .. } => { if let Some(v) = partial.name { @@ -649,7 +635,12 @@ impl Channel { if let Some(v) = partial.default_permissions { default_permissions.replace(v); } - } + + if let Some(v) = partial.voice { + voice.replace(v); + } + }, + Self::VoiceChannel { .. } => {} } } diff --git a/crates/core/database/src/models/channels/ops/mongodb.rs b/crates/core/database/src/models/channels/ops/mongodb.rs index 51c6b66e5..c75fab536 100644 --- a/crates/core/database/src/models/channels/ops/mongodb.rs +++ b/crates/core/database/src/models/channels/ops/mongodb.rs @@ -190,7 +190,7 @@ impl AbstractChannels for MongoDb { async fn delete_channel(&self, channel: &Channel) -> Result<()> { let id = channel.id().to_string(); let server_id = match channel { - Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => { + Channel::TextChannel { server, .. } => { Some(server) } _ => None, diff --git a/crates/core/database/src/models/channels/ops/reference.rs b/crates/core/database/src/models/channels/ops/reference.rs index 7d5c9a59f..518808bcc 100644 --- a/crates/core/database/src/models/channels/ops/reference.rs +++ b/crates/core/database/src/models/channels/ops/reference.rs @@ -94,9 +94,6 @@ impl AbstractChannels for ReferenceDb { match &mut channel { Channel::TextChannel { role_permissions, .. - } - | Channel::VoiceChannel { - role_permissions, .. } => { if role_permissions.get(role_id).is_some() { role_permissions.remove(role_id); diff --git a/crates/core/database/src/models/messages/model.rs b/crates/core/database/src/models/messages/model.rs index d4991ed42..f2ff0f55b 100644 --- a/crates/core/database/src/models/messages/model.rs +++ b/crates/core/database/src/models/messages/model.rs @@ -342,6 +342,7 @@ impl Message { // Validate the mentions go to users in the channel/server if !mentions.is_empty() { + #[allow(deprecated)] match channel { Channel::DirectMessage { ref recipients, .. } | Channel::Group { ref recipients, .. } => { diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index 1ad07a927..3673411ec 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -143,6 +143,7 @@ impl From for FieldsWebhook { } impl From for Channel { + #[allow(deprecated)] fn from(value: crate::Channel) -> Self { match value { crate::Channel::SavedMessages { id, user } => Channel::SavedMessages { id, user }, @@ -225,6 +226,7 @@ impl From for Channel { } impl From for crate::Channel { + #[allow(deprecated)] fn from(value: Channel) -> crate::Channel { match value { Channel::SavedMessages { id, user } => crate::Channel::SavedMessages { id, user }, @@ -319,6 +321,7 @@ impl From for PartialChannel { role_permissions: value.role_permissions, default_permissions: value.default_permissions, last_message_id: value.last_message_id, + voice: value.voice } } } @@ -336,6 +339,7 @@ impl From for crate::PartialChannel { role_permissions: value.role_permissions, default_permissions: value.default_permissions, last_message_id: value.last_message_id, + voice: value.voice } } } diff --git a/crates/core/database/src/util/bulk_permissions.rs b/crates/core/database/src/util/bulk_permissions.rs index 400359772..e45269ca5 100644 --- a/crates/core/database/src/util/bulk_permissions.rs +++ b/crates/core/database/src/util/bulk_permissions.rs @@ -121,10 +121,6 @@ impl<'z> BulkDatabasePermissionQuery<'z> { Channel::TextChannel { default_permissions, .. - } - | Channel::VoiceChannel { - default_permissions, - .. } => default_permissions.unwrap_or_default().into(), _ => Default::default(), } @@ -133,7 +129,7 @@ impl<'z> BulkDatabasePermissionQuery<'z> { } } - #[allow(dead_code)] + #[allow(dead_code, deprecated)] fn get_channel_type(&mut self) -> ChannelType { if let Some(channel) = &self.channel { match channel { @@ -156,9 +152,6 @@ impl<'z> BulkDatabasePermissionQuery<'z> { match channel { Channel::TextChannel { role_permissions, .. - } - | Channel::VoiceChannel { - role_permissions, .. } => role_permissions, _ => panic!("Not supported for non-server channels"), } @@ -185,12 +178,6 @@ async fn calculate_members_permissions<'a>( role_permissions, default_permissions, .. - } - | Channel::VoiceChannel { - id, - role_permissions, - default_permissions, - .. } => (id, role_permissions, default_permissions), _ => panic!("Calculation of member permissions must be done on a server channel"), }; diff --git a/crates/core/database/src/util/permissions.rs b/crates/core/database/src/util/permissions.rs index 436b236f6..e3254e66d 100644 --- a/crates/core/database/src/util/permissions.rs +++ b/crates/core/database/src/util/permissions.rs @@ -204,6 +204,7 @@ impl PermissionQuery for DatabasePermissionQuery<'_> { // * For calculating channel permission /// Get the type of the channel + #[allow(deprecated)] async fn get_channel_type(&mut self) -> ChannelType { if let Some(channel) = &self.channel { match channel { @@ -241,14 +242,6 @@ impl PermissionQuery for DatabasePermissionQuery<'_> { | Cow::Owned(Channel::TextChannel { default_permissions, .. - }) - | Cow::Borrowed(Channel::VoiceChannel { - default_permissions, - .. - }) - | Cow::Owned(Channel::VoiceChannel { - default_permissions, - .. }) => default_permissions.unwrap_or_default().into(), _ => Default::default(), } @@ -266,12 +259,6 @@ impl PermissionQuery for DatabasePermissionQuery<'_> { }) | Cow::Owned(Channel::TextChannel { role_permissions, .. - }) - | Cow::Borrowed(Channel::VoiceChannel { - role_permissions, .. - }) - | Cow::Owned(Channel::VoiceChannel { - role_permissions, .. }) => { if let Some(server) = &self.server { let member_roles = self @@ -361,6 +348,7 @@ impl PermissionQuery for DatabasePermissionQuery<'_> { /// (this will only ever be called for server channels, use unimplemented!() for other code paths) async fn set_server_from_channel(&mut self) { if let Some(channel) = &self.channel { + #[allow(deprecated)] match channel { Cow::Borrowed(Channel::TextChannel { server, .. }) | Cow::Owned(Channel::TextChannel { server, .. }) diff --git a/crates/core/database/src/voice/mod.rs b/crates/core/database/src/voice/mod.rs index b1e797e46..05eabdb78 100644 --- a/crates/core/database/src/voice/mod.rs +++ b/crates/core/database/src/voice/mod.rs @@ -284,10 +284,7 @@ pub async fn get_voice_state( pub async fn get_channel_voice_state(channel: &Channel) -> Result> { let members = get_voice_channel_members(channel.id()).await?; - let server = match channel { - Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => Some(server.as_str()), - _ => None - }; + let server = channel.server(); if let Some(members) = members { let mut participants = Vec::with_capacity(members.len()); diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs index 0b2ecdbec..e6067f790 100644 --- a/crates/core/database/src/voice/voice_client.rs +++ b/crates/core/database/src/voice/voice_client.rs @@ -89,7 +89,7 @@ impl VoiceClient { let room = self.get_node(node)?; let voice = match channel { - Channel::DirectMessage { .. } | Channel::VoiceChannel { .. } => Some(Cow::Owned(v0::VoiceInformation::default())), + Channel::DirectMessage { .. } => Some(Cow::Owned(v0::VoiceInformation::default())), Channel::TextChannel { voice: Some(voice), .. } => Some(Cow::Borrowed(voice)), _ => None } diff --git a/crates/core/models/src/v0/channels.rs b/crates/core/models/src/v0/channels.rs index ecd895c42..b08de26b2 100644 --- a/crates/core/models/src/v0/channels.rs +++ b/crates/core/models/src/v0/channels.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use super::{File, UserVoiceState}; use revolt_permissions::{Override, OverrideField}; @@ -112,7 +113,7 @@ auto_derived!( #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] voice: Option }, - /// DEPRECATED (use TextChannel { voice }): Voice channel belonging to a server + #[deprecated = "Use TextChannel { voice } instead"] VoiceChannel { /// Unique Id #[cfg_attr(feature = "serde", serde(rename = "_id"))] @@ -183,6 +184,8 @@ auto_derived!( pub default_permissions: Option, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub last_message_id: Option, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub voice: Option, } /// Optional fields on channel object @@ -218,6 +221,9 @@ auto_derived!( /// Whether this channel is archived pub archived: Option, + /// Voice Information for voice channels + pub voice: Option, + /// Fields to remove from channel #[cfg_attr(feature = "serde", serde(default))] pub remove: Option>, diff --git a/crates/daemons/pushd/src/consumers/outbound/apn.rs b/crates/daemons/pushd/src/consumers/outbound/apn.rs index 3b6ac3a71..c7ac6c875 100644 --- a/crates/daemons/pushd/src/consumers/outbound/apn.rs +++ b/crates/daemons/pushd/src/consumers/outbound/apn.rs @@ -62,6 +62,7 @@ impl ApnsOutboundConsumer { // in a dm it should just be "Sendername". // not sure how feasible all those are given the PushNotification object as it currently stands. + #[allow(deprecated)] match ¬ification.channel { Channel::DirectMessage { .. } => notification.author.clone(), Channel::Group { name, .. } => format!("{}, #{}", notification.author, name), diff --git a/crates/daemons/pushd/src/consumers/outbound/fcm.rs b/crates/daemons/pushd/src/consumers/outbound/fcm.rs index 61700bbb9..a22708d87 100644 --- a/crates/daemons/pushd/src/consumers/outbound/fcm.rs +++ b/crates/daemons/pushd/src/consumers/outbound/fcm.rs @@ -26,6 +26,7 @@ impl FcmOutboundConsumer { // in a dm it should just be "Sendername". // not sure how feasible all those are given the PushNotification object as it currently stands. + #[allow(deprecated)] match ¬ification.channel { Channel::DirectMessage { .. } => notification.author.clone(), Channel::Group { name, .. } => format!("{}, #{}", notification.author, name), diff --git a/crates/delta/src/routes/channels/channel_delete.rs b/crates/delta/src/routes/channels/channel_delete.rs index 823e02331..ebb589b79 100644 --- a/crates/delta/src/routes/channels/channel_delete.rs +++ b/crates/delta/src/routes/channels/channel_delete.rs @@ -26,6 +26,7 @@ pub async fn delete( permissions.throw_if_lacking_channel_permission(ChannelPermission::ViewChannel)?; + #[allow(deprecated)] match &channel { Channel::SavedMessages { .. } => Err(create_error!(NoEffect))?, Channel::DirectMessage { .. } => channel diff --git a/crates/delta/src/routes/channels/channel_edit.rs b/crates/delta/src/routes/channels/channel_edit.rs index 1977ec9ba..d69fb8b3e 100644 --- a/crates/delta/src/routes/channels/channel_edit.rs +++ b/crates/delta/src/routes/channels/channel_edit.rs @@ -95,22 +95,6 @@ pub async fn edit( icon, nsfw, .. - } - | Channel::TextChannel { - id, - name, - description, - icon, - nsfw, - .. - } - | Channel::VoiceChannel { - id, - name, - description, - icon, - nsfw, - .. } => { if let Some(fields) = &data.remove { if fields.contains(&v0::FieldsChannel::Icon) { @@ -153,77 +137,129 @@ pub async fn edit( } // Send out mutation system messages. - if let Channel::Group { .. } = &channel { - if let Some(name) = &partial.name { - SystemMessage::ChannelRenamed { - name: name.to_string(), - by: user.id.clone(), - } - .into_message(channel.id().to_string()) - .send( - db, - Some(amqp), - user.as_author_for_system(), - None, - None, - &channel, - false, - ) - .await - .ok(); + if let Some(name) = &partial.name { + SystemMessage::ChannelRenamed { + name: name.to_string(), + by: user.id.clone(), } + .into_message(channel.id().to_string()) + .send( + db, + Some(amqp), + user.as_author_for_system(), + None, + None, + &channel, + false, + ) + .await + .ok(); + } - if partial.description.is_some() { - SystemMessage::ChannelDescriptionChanged { - by: user.id.clone(), + if partial.description.is_some() { + SystemMessage::ChannelDescriptionChanged { + by: user.id.clone(), + } + .into_message(channel.id().to_string()) + .send( + db, + Some(amqp), + user.as_author_for_system(), + None, + None, + &channel, + false, + ) + .await + .ok(); + } + + if partial.icon.is_some() { + SystemMessage::ChannelIconChanged { + by: user.id.clone(), + } + .into_message(channel.id().to_string()) + .send( + db, + Some(amqp), + user.as_author_for_system(), + None, + None, + &channel, + false, + ) + .await + .ok(); + } + } + Channel::TextChannel { + id, + name, + description, + icon, + nsfw, + voice, + .. + } => { + if let Some(fields) = &data.remove { + if fields.contains(&v0::FieldsChannel::Icon) { + if let Some(icon) = &icon { + db.mark_attachment_as_deleted(&icon.id).await?; } - .into_message(channel.id().to_string()) - .send( - db, - Some(amqp), - user.as_author_for_system(), - None, - None, - &channel, - false, - ) - .await - .ok(); } - if partial.icon.is_some() { - SystemMessage::ChannelIconChanged { - by: user.id.clone(), + for field in fields { + match field { + v0::FieldsChannel::Description => { + description.take(); + } + v0::FieldsChannel::Icon => { + icon.take(); + } + _ => {} } - .into_message(channel.id().to_string()) - .send( - db, - Some(amqp), - user.as_author_for_system(), - None, - None, - &channel, - false, - ) - .await - .ok(); } } - channel - .update( - db, - partial, - data.remove - .unwrap_or_default() - .into_iter() - .map(|f| f.into()) - .collect(), - ) - .await?; + if let Some(icon_id) = data.icon { + partial.icon = Some(File::use_channel_icon(db, &icon_id, id, &user.id).await?); + *icon = partial.icon.clone(); + } + + if let Some(new_name) = data.name { + *name = new_name.clone(); + partial.name = Some(new_name); + } + + if let Some(new_description) = data.description { + partial.description = Some(new_description); + *description = partial.description.clone(); + } + + if let Some(new_nsfw) = data.nsfw { + *nsfw = new_nsfw; + partial.nsfw = Some(new_nsfw); + } + + if let Some(new_voice) = data.voice { + *voice = Some(new_voice.clone()); + partial.voice = Some(new_voice); + } } _ => return Err(create_error!(InvalidOperation)), }; + channel + .update( + db, + partial, + data.remove + .unwrap_or_default() + .into_iter() + .map(|f| f.into()) + .collect(), + ) + .await?; + Ok(Json(channel.into())) } diff --git a/crates/delta/src/routes/channels/message_query.rs b/crates/delta/src/routes/channels/message_query.rs index 5193bd02a..2ab6e8da5 100644 --- a/crates/delta/src/routes/channels/message_query.rs +++ b/crates/delta/src/routes/channels/message_query.rs @@ -65,6 +65,7 @@ pub async fn query( }, &user, include_users, + #[allow(deprecated)] match channel { Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => { Some(server) diff --git a/crates/delta/src/routes/channels/message_search.rs b/crates/delta/src/routes/channels/message_search.rs index eb173bc05..b3b90071e 100644 --- a/crates/delta/src/routes/channels/message_search.rs +++ b/crates/delta/src/routes/channels/message_search.rs @@ -69,6 +69,7 @@ pub async fn search( }, &user, include_users, + #[allow(deprecated)] match channel { Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => { Some(server) diff --git a/crates/delta/src/routes/channels/message_send.rs b/crates/delta/src/routes/channels/message_send.rs index 8ad9fb422..d55bd8bda 100644 --- a/crates/delta/src/routes/channels/message_send.rs +++ b/crates/delta/src/routes/channels/message_send.rs @@ -192,6 +192,7 @@ mod test { d: ChannelPermission::ViewChannel as i64, }), last_message_id: None, + voice: None, }; locked_channel .update(&harness.db, partial, vec![]) diff --git a/crates/delta/src/routes/channels/permissions_set_default.rs b/crates/delta/src/routes/channels/permissions_set_default.rs index 91f53b2fb..14386281c 100644 --- a/crates/delta/src/routes/channels/permissions_set_default.rs +++ b/crates/delta/src/routes/channels/permissions_set_default.rs @@ -48,10 +48,6 @@ pub async fn set_default_permissions( Channel::TextChannel { default_permissions, .. - } - | Channel::VoiceChannel { - default_permissions, - .. } => { if let DataDefaultChannelPermissions::Field { permissions: field } = data { permissions diff --git a/crates/delta/src/routes/invites/invite_fetch.rs b/crates/delta/src/routes/invites/invite_fetch.rs index a56c30bb9..edf547446 100644 --- a/crates/delta/src/routes/invites/invite_fetch.rs +++ b/crates/delta/src/routes/invites/invite_fetch.rs @@ -23,13 +23,6 @@ pub async fn fetch(db: &State, target: Reference) -> Result { let server = db.fetch_server(&server).await?; From a4246f39e4cc3f889d67e767373ec1f6005985b8 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 17:19:54 +0000 Subject: [PATCH 21/62] chore: add pushd to debug image script --- scripts/publish-debug-image.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/publish-debug-image.sh b/scripts/publish-debug-image.sh index 1f0eb2e48..247338ab5 100755 --- a/scripts/publish-debug-image.sh +++ b/scripts/publish-debug-image.sh @@ -25,6 +25,7 @@ docker build -t ghcr.io/revoltchat/server:$TAG - < crates/delta/Dockerfile docker build -t ghcr.io/revoltchat/bonfire:$TAG - < crates/bonfire/Dockerfile docker build -t ghcr.io/revoltchat/autumn:$TAG - < crates/services/autumn/Dockerfile docker build -t ghcr.io/revoltchat/january:$TAG - < crates/services/january/Dockerfile +docker build -t ghcr.io/revoltchat/pushd:$TAG - < crates/daemons/pushd/Dockerfile if [ "$DEBUG" = "true" ]; then git restore Cargo.toml @@ -34,3 +35,4 @@ docker push ghcr.io/revoltchat/server:$TAG docker push ghcr.io/revoltchat/bonfire:$TAG docker push ghcr.io/revoltchat/autumn:$TAG docker push ghcr.io/revoltchat/january:$TAG +docker push ghcr.io/revoltchat/pushd:$TAG From 343b90b9af24ee08c7e4c65335722bcb49e7ed96 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 17:20:29 +0000 Subject: [PATCH 22/62] refactor: add error messages for Rabbit issues --- crates/delta/src/main.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/delta/src/main.rs b/crates/delta/src/main.rs index aaa769325..ae7556cea 100644 --- a/crates/delta/src/main.rs +++ b/crates/delta/src/main.rs @@ -93,8 +93,12 @@ pub async fn web() -> Rocket { &config.rabbit.password, )) .await - .unwrap(); - let channel = connection.open_channel(None).await.unwrap(); + .expect("Failed to connect to RabbitMQ"); + + let channel = connection + .open_channel(None) + .await + .expect("Failed to open RabbitMQ channel"); channel .exchange_declare( From e8cb0bb88a1fbd6e197e1920122e0960d124adb7 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 17:21:23 +0000 Subject: [PATCH 23/62] docs: add some notes on overrides; fix defaults --- .gitignore | 1 + README.md | 34 +++++++++++++++++++++++++++++++++- Revolt.toml | 7 +++++-- default.nix | 20 ++------------------ 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 3699f3fc4..4bbb168a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ Rocket.toml Revolt.*.toml +compose.override.yml target .data diff --git a/README.md b/README.md index ea8cab84b..c480340f5 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ As a heads-up, the development environment uses the following ports: | MinIO | 14009 | | Maildev | 14025
14080 | | Revolt Web App | 14701 | -| RabbitMQ | 5672
15672 | +| RabbitMQ | 5672
15672 | | `crates/delta` | 14702 | | `crates/bonfire` | 14703 | | `crates/services/autumn` | 14704 | @@ -91,6 +91,38 @@ If you'd like to change anything, create a `Revolt.overrides.toml` file and spec > proxy = "https://abc@your.sentry/1" > ``` +> [!TIP] +> If you have port conflicts on common services, you can try the following: +> +> ```yaml +> # compose.override.yml +> services: +> redis: +> ports: !override +> - "14079:6379" +> +> database: +> ports: !override +> - "14017:27017" +> +> rabbit: +> ports: !override +> - "14072:5672" +> - "14672:15672" +> ``` +> +> And corresponding Revolt configuration: +> +> ```toml +> # Revolt.overrides.toml +> [database] +> mongodb = "mongodb://127.0.0.1:14017" +> redis = "redis://127.0.0.1:14079/" +> +> [rabbit] +> port = 14072 +> ``` + Then continue: ```bash diff --git a/Revolt.toml b/Revolt.toml index e5058c51b..5fc7f4511 100644 --- a/Revolt.toml +++ b/Revolt.toml @@ -4,10 +4,13 @@ [database] # MongoDB connection URL # Defaults to the container name specified in self-hosted -mongodb = "mongodb://127.0.0.1:14017" +mongodb = "mongodb://127.0.0.1:27017" # Redis connection URL # Defaults to the container name specified in self-hosted -redis = "redis://127.0.0.1:14079/" +redis = "redis://127.0.0.1:6379/" + +[rabbit] +host = "127.0.0.1" [hosts] # Web locations of various services diff --git a/default.nix b/default.nix index 16096d502..f87363083 100644 --- a/default.nix +++ b/default.nix @@ -1,26 +1,10 @@ -let - # Pinned nixpkgs, deterministic. Last updated: 28-07-2024. - pkgs = import (fetchTarball("https://github.com/NixOS/nixpkgs/archive/9b34ca580417e1ebc56c4df57d8b387dad686665.tar.gz")) {}; - - # Rolling updates, not deterministic. - # pkgs = import (fetchTarball("channel:nixpkgs-unstable")) {}; -in pkgs.mkShell { - name = "revoltEnv"; - - # LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ - # pkgs.gcc-unwrapped - # pkgs.zlib - # pkgs.glib - # pkgs.libGL - # ]; +{ pkgs ? import {} }: +pkgs.mkShell rec { buildInputs = [ # Tools pkgs.git - # Database - # pkgs.mongodb - # Cargo pkgs.cargo pkgs.cargo-nextest From 9a2a7901e7f782b25a871d40be179a3a9e01ccf3 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 17:51:22 +0000 Subject: [PATCH 24/62] fix: ensure limit is always at least 1 when sent to database --- crates/core/database/src/models/messages/ops/mongodb.rs | 2 +- crates/core/models/src/v0/messages.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core/database/src/models/messages/ops/mongodb.rs b/crates/core/database/src/models/messages/ops/mongodb.rs index 18eeb3796..721a3b7c9 100644 --- a/crates/core/database/src/models/messages/ops/mongodb.rs +++ b/crates/core/database/src/models/messages/ops/mongodb.rs @@ -95,7 +95,7 @@ impl AbstractMessages for MongoDb { COL, older_message_filter, FindOptions::builder() - .limit(limit / 2) + .limit(limit / 2 + 1) .sort(doc! { "_id": -1_i32 }) diff --git a/crates/core/models/src/v0/messages.rs b/crates/core/models/src/v0/messages.rs index 21541984b..37ed526ed 100644 --- a/crates/core/models/src/v0/messages.rs +++ b/crates/core/models/src/v0/messages.rs @@ -282,7 +282,7 @@ auto_derived!( pub struct OptionsQueryMessages { /// Maximum number of messages to fetch /// - /// For fetching nearby messages, this is \`(limit + 1)\`. + /// For fetching nearby messages, this is \`(limit + 2)\`. #[cfg_attr(feature = "validator", validate(range(min = 1, max = 100)))] pub limit: Option, /// Message id before which messages should be fetched From 12854d0f1b4746265f8812840895c47782a95422 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 18:04:46 +0000 Subject: [PATCH 25/62] fix: change permission check for fetching channel webhooks --- crates/delta/src/routes/channels/webhook_fetch_all.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/delta/src/routes/channels/webhook_fetch_all.rs b/crates/delta/src/routes/channels/webhook_fetch_all.rs index 06d94d802..6afc7ac8b 100644 --- a/crates/delta/src/routes/channels/webhook_fetch_all.rs +++ b/crates/delta/src/routes/channels/webhook_fetch_all.rs @@ -22,7 +22,7 @@ pub async fn fetch_webhooks( let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel); calculate_channel_permissions(&mut query) .await - .throw_if_lacking_channel_permission(ChannelPermission::ViewChannel)?; + .throw_if_lacking_channel_permission(ChannelPermission::ManageWebhooks)?; Ok(Json( db.fetch_webhooks_for_channel(channel.id()) From a7345a7b9068aa654b88a3ba5cf260f6f078ebca Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 18:19:34 +0000 Subject: [PATCH 26/62] chore: mount API at /0.8/ as well --- crates/delta/src/main.rs | 11 ++++++++- crates/delta/src/routes/mod.rs | 41 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/crates/delta/src/main.rs b/crates/delta/src/main.rs index ae7556cea..c5977c57a 100644 --- a/crates/delta/src/main.rs +++ b/crates/delta/src/main.rs @@ -77,7 +77,15 @@ pub async fn web() -> Rocket { // Configure Swagger let swagger = revolt_rocket_okapi::swagger_ui::make_swagger_ui( &revolt_rocket_okapi::swagger_ui::SwaggerUIConfig { - url: "../openapi.json".to_owned(), + url: "/openapi.json".to_owned(), + ..Default::default() + }, + ) + .into(); + + let swagger_0_8 = revolt_rocket_okapi::swagger_ui::make_swagger_ui( + &revolt_rocket_okapi::swagger_ui::SwaggerUIConfig { + url: "/0.8/openapi.json".to_owned(), ..Default::default() }, ) @@ -124,6 +132,7 @@ pub async fn web() -> Rocket { .mount("/", rocket_cors::catch_all_options_routes()) .mount("/", util::ratelimiter::routes()) .mount("/swagger/", swagger) + .mount("/0.8/swagger/", swagger_0_8) .manage(authifier) .manage(db) .manage(amqp) diff --git a/crates/delta/src/routes/mod.rs b/crates/delta/src/routes/mod.rs index 516c2166c..1f150e0b6 100644 --- a/crates/delta/src/routes/mod.rs +++ b/crates/delta/src/routes/mod.rs @@ -61,6 +61,47 @@ pub fn mount(config: Settings, mut rocket: Rocket) -> Rocket { }; } + if config.features.webhooks_enabled { + mount_endpoints_and_merged_docs! { + rocket, "/0.8".to_owned(), settings, + "/" => (vec![], custom_openapi_spec()), + "" => openapi_get_routes_spec![root::root], + "/users" => users::routes(), + "/bots" => bots::routes(), + "/channels" => channels::routes(), + "/servers" => servers::routes(), + "/invites" => invites::routes(), + "/custom" => customisation::routes(), + "/safety" => safety::routes(), + "/auth/account" => rocket_authifier::routes::account::routes(), + "/auth/session" => rocket_authifier::routes::session::routes(), + "/auth/mfa" => rocket_authifier::routes::mfa::routes(), + "/onboard" => onboard::routes(), + "/push" => push::routes(), + "/sync" => sync::routes(), + "/webhooks" => webhooks::routes() + }; + } else { + mount_endpoints_and_merged_docs! { + rocket, "/0.8".to_owned(), settings, + "/" => (vec![], custom_openapi_spec()), + "" => openapi_get_routes_spec![root::root], + "/users" => users::routes(), + "/bots" => bots::routes(), + "/channels" => channels::routes(), + "/servers" => servers::routes(), + "/invites" => invites::routes(), + "/custom" => customisation::routes(), + "/safety" => safety::routes(), + "/auth/account" => rocket_authifier::routes::account::routes(), + "/auth/session" => rocket_authifier::routes::session::routes(), + "/auth/mfa" => rocket_authifier::routes::mfa::routes(), + "/onboard" => onboard::routes(), + "/push" => push::routes(), + "/sync" => sync::routes() + }; + } + rocket } From 8bfe3ec6b0a35689317c429650a9d1f3f5af179a Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 18:34:37 +0000 Subject: [PATCH 27/62] fix: ensure ratelimiter adjusts for version prefix --- crates/delta/src/routes/mod.rs | 7 ++++++- crates/delta/src/util/ratelimiter.rs | 22 ++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/crates/delta/src/routes/mod.rs b/crates/delta/src/routes/mod.rs index 1f150e0b6..5107e7c26 100644 --- a/crates/delta/src/routes/mod.rs +++ b/crates/delta/src/routes/mod.rs @@ -231,10 +231,15 @@ fn custom_openapi_spec() -> OpenApi { ..Default::default() }, Server { - url: "http://local.revolt.chat:8000".to_owned(), + url: "http://local.revolt.chat:14702".to_owned(), description: Some("Local Revolt Environment".to_owned()), ..Default::default() }, + Server { + url: "http://local.revolt.chat:14702/0.8".to_owned(), + description: Some("Local Revolt Environment (v0.8)".to_owned()), + ..Default::default() + }, ], external_docs: Some(ExternalDocs { url: "https://developers.revolt.chat".to_owned(), diff --git a/crates/delta/src/util/ratelimiter.rs b/crates/delta/src/util/ratelimiter.rs index c95f818df..95952918e 100644 --- a/crates/delta/src/util/ratelimiter.rs +++ b/crates/delta/src/util/ratelimiter.rs @@ -94,14 +94,28 @@ pub struct Ratelimiter { /// /// Optionally, include a resource id to hash against. fn resolve_bucket<'r>(request: &'r rocket::Request<'_>) -> (&'r str, Option<&'r str>) { - if let Some(segment) = request.routed_segment(0) { - let resource = request.routed_segment(1); + let (segment, resource, extra) = if matches!(request.routed_segment(0), Some("0.8")) { + ( + request.routed_segment(1), + request.routed_segment(2), + request.routed_segment(3), + ) + } else { + ( + request.routed_segment(0), + request.routed_segment(1), + request.routed_segment(2), + ) + }; + + if let Some(segment) = segment { + let resource = resource; let method = request.method(); match (segment, resource, method) { ("users", target, Method::Patch) => ("user_edit", target), ("users", _, _) => { - if let Some("default_avatar") = request.routed_segment(2) { + if let Some("default_avatar") = extra { return ("default_avatar", None); } @@ -110,7 +124,7 @@ fn resolve_bucket<'r>(request: &'r rocket::Request<'_>) -> (&'r str, Option<&'r ("bots", _, _) => ("bots", None), ("channels", Some(id), _) => { if request.method() == Method::Post { - if let Some("messages") = request.routed_segment(2) { + if let Some("messages") = extra { return ("messaging", Some(id)); } } From 0b02c84f0471b464c6971b592c77f477f17c6f5a Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 18:37:21 +0000 Subject: [PATCH 28/62] chore: bump version to 0.8.2 --- Cargo.lock | 26 +++++++++++++------------- crates/bindings/node/Cargo.toml | 8 ++++---- crates/bonfire/Cargo.toml | 4 ++-- crates/core/config/Cargo.toml | 4 ++-- crates/core/database/Cargo.toml | 12 ++++++------ crates/core/files/Cargo.toml | 6 +++--- crates/core/models/Cargo.toml | 6 +++--- crates/core/permissions/Cargo.toml | 4 ++-- crates/core/presence/Cargo.toml | 2 +- crates/core/result/Cargo.toml | 2 +- crates/daemons/pushd/Cargo.toml | 8 ++++---- crates/delta/Cargo.toml | 2 +- crates/services/autumn/Cargo.toml | 10 +++++----- crates/services/january/Cargo.toml | 10 +++++----- 14 files changed, 52 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80cd01bdd..1c5bd6016 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6210,7 +6210,7 @@ dependencies = [ [[package]] name = "revolt-autumn" -version = "0.8.1" +version = "0.8.2" dependencies = [ "axum", "axum-macros", @@ -6247,7 +6247,7 @@ dependencies = [ [[package]] name = "revolt-bonfire" -version = "0.8.1" +version = "0.8.2" dependencies = [ "async-channel 2.3.1", "async-std", @@ -6277,7 +6277,7 @@ dependencies = [ [[package]] name = "revolt-config" -version = "0.8.1" +version = "0.8.2" dependencies = [ "async-std", "cached", @@ -6293,7 +6293,7 @@ dependencies = [ [[package]] name = "revolt-database" -version = "0.8.1" +version = "0.8.2" dependencies = [ "amqprs", "async-lock 2.8.0", @@ -6345,7 +6345,7 @@ dependencies = [ [[package]] name = "revolt-delta" -version = "0.8.1" +version = "0.8.2" dependencies = [ "amqprs", "async-channel 1.9.0", @@ -6394,7 +6394,7 @@ dependencies = [ [[package]] name = "revolt-files" -version = "0.8.1" +version = "0.8.2" dependencies = [ "aes-gcm", "aws-config", @@ -6417,7 +6417,7 @@ dependencies = [ [[package]] name = "revolt-january" -version = "0.8.1" +version = "0.8.2" dependencies = [ "async-recursion", "axum", @@ -6445,7 +6445,7 @@ dependencies = [ [[package]] name = "revolt-models" -version = "0.8.1" +version = "0.8.2" dependencies = [ "indexmap 1.9.3", "iso8601-timestamp 0.2.17", @@ -6463,7 +6463,7 @@ dependencies = [ [[package]] name = "revolt-nodejs-bindings" -version = "0.8.1" +version = "0.8.2" dependencies = [ "async-std", "neon", @@ -6476,7 +6476,7 @@ dependencies = [ [[package]] name = "revolt-permissions" -version = "0.8.1" +version = "0.8.2" dependencies = [ "async-std", "async-trait", @@ -6491,7 +6491,7 @@ dependencies = [ [[package]] name = "revolt-presence" -version = "0.8.1" +version = "0.8.2" dependencies = [ "async-std", "log", @@ -6502,7 +6502,7 @@ dependencies = [ [[package]] name = "revolt-pushd" -version = "0.8.1" +version = "0.8.2" dependencies = [ "amqprs", "async-trait", @@ -6526,7 +6526,7 @@ dependencies = [ [[package]] name = "revolt-result" -version = "0.8.1" +version = "0.8.2" dependencies = [ "axum", "log", diff --git a/crates/bindings/node/Cargo.toml b/crates/bindings/node/Cargo.toml index 55ada675f..43b9b8567 100644 --- a/crates/bindings/node/Cargo.toml +++ b/crates/bindings/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-nodejs-bindings" -version = "0.8.1" +version = "0.8.2" description = "Node.js bindings for the Revolt software" authors = ["Paul Makles "] license = "MIT" @@ -20,6 +20,6 @@ serde = { version = "1", features = ["derive"] } async-std = "1.12.0" -revolt-config = { version = "0.8.1", path = "../../core/config" } -revolt-result = { version = "0.8.1", path = "../../core/result" } -revolt-database = { version = "0.8.1", path = "../../core/database" } +revolt-config = { version = "0.8.2", path = "../../core/config" } +revolt-result = { version = "0.8.2", path = "../../core/result" } +revolt-database = { version = "0.8.2", path = "../../core/database" } diff --git a/crates/bonfire/Cargo.toml b/crates/bonfire/Cargo.toml index 86627e3cb..ed13358b5 100644 --- a/crates/bonfire/Cargo.toml +++ b/crates/bonfire/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-bonfire" -version = "0.8.1" +version = "0.8.2" license = "AGPL-3.0-or-later" edition = "2021" @@ -41,7 +41,7 @@ revolt-result = { path = "../core/result" } revolt-models = { path = "../core/models" } revolt-config = { path = "../core/config" } revolt-database = { path = "../core/database" } -revolt-permissions = { version = "0.8.1", path = "../core/permissions" } +revolt-permissions = { version = "0.8.2", path = "../core/permissions" } revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] } # redis diff --git a/crates/core/config/Cargo.toml b/crates/core/config/Cargo.toml index 1e9c96d6e..b23c10b94 100644 --- a/crates/core/config/Cargo.toml +++ b/crates/core/config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-config" -version = "0.8.1" +version = "0.8.2" edition = "2021" license = "MIT" authors = ["Paul Makles "] @@ -34,4 +34,4 @@ pretty_env_logger = "0.4.0" sentry = "0.31.5" # Core -revolt-result = { version = "0.8.1", path = "../result", optional = true } +revolt-result = { version = "0.8.2", path = "../result", optional = true } diff --git a/crates/core/database/Cargo.toml b/crates/core/database/Cargo.toml index 329217ffa..16e0b43fd 100644 --- a/crates/core/database/Cargo.toml +++ b/crates/core/database/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-database" -version = "0.8.1" +version = "0.8.2" edition = "2021" license = "AGPL-3.0-or-later" authors = ["Paul Makles "] @@ -24,15 +24,15 @@ default = ["mongodb", "async-std-runtime", "tasks"] [dependencies] # Core -revolt-config = { version = "0.8.1", path = "../config", features = [ +revolt-config = { version = "0.8.2", path = "../config", features = [ "report-macros", ] } -revolt-result = { version = "0.8.1", path = "../result" } -revolt-models = { version = "0.8.1", path = "../models", features = [ +revolt-result = { version = "0.8.2", path = "../result" } +revolt-models = { version = "0.8.2", path = "../models", features = [ "validator", ] } -revolt-presence = { version = "0.8.1", path = "../presence" } -revolt-permissions = { version = "0.8.1", path = "../permissions", features = [ +revolt-presence = { version = "0.8.2", path = "../presence" } +revolt-permissions = { version = "0.8.2", path = "../permissions", features = [ "serde", "bson", ] } diff --git a/crates/core/files/Cargo.toml b/crates/core/files/Cargo.toml index 59ce115ab..de7d2d175 100644 --- a/crates/core/files/Cargo.toml +++ b/crates/core/files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-files" -version = "0.8.1" +version = "0.8.2" edition = "2021" license = "AGPL-3.0-or-later" authors = ["Paul Makles "] @@ -20,10 +20,10 @@ typenum = "1.17.0" aws-config = "1.5.5" aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] } -revolt-config = { version = "0.8.1", path = "../config", features = [ +revolt-config = { version = "0.8.2", path = "../config", features = [ "report-macros", ] } -revolt-result = { version = "0.8.1", path = "../result" } +revolt-result = { version = "0.8.2", path = "../result" } # image processing jxl-oxide = "0.8.1" diff --git a/crates/core/models/Cargo.toml b/crates/core/models/Cargo.toml index b840d47f7..4d0c7fa6d 100644 --- a/crates/core/models/Cargo.toml +++ b/crates/core/models/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-models" -version = "0.8.1" +version = "0.8.2" edition = "2021" license = "MIT" authors = ["Paul Makles "] @@ -20,8 +20,8 @@ default = ["serde", "partials", "rocket"] [dependencies] # Core -revolt-config = { version = "0.8.1", path = "../config" } -revolt-permissions = { version = "0.8.1", path = "../permissions" } +revolt-config = { version = "0.8.2", path = "../config" } +revolt-permissions = { version = "0.8.2", path = "../permissions" } # Utility regex = "1" diff --git a/crates/core/permissions/Cargo.toml b/crates/core/permissions/Cargo.toml index c30e9b743..daf61e8d9 100644 --- a/crates/core/permissions/Cargo.toml +++ b/crates/core/permissions/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-permissions" -version = "0.8.1" +version = "0.8.2" edition = "2021" license = "MIT" authors = ["Paul Makles "] @@ -21,7 +21,7 @@ async-std = { version = "1.8.0", features = ["attributes"] } [dependencies] # Core -revolt-result = { version = "0.8.1", path = "../result" } +revolt-result = { version = "0.8.2", path = "../result" } # Utility auto_ops = "0.3.0" diff --git a/crates/core/presence/Cargo.toml b/crates/core/presence/Cargo.toml index 48ef797a3..7df7c7026 100644 --- a/crates/core/presence/Cargo.toml +++ b/crates/core/presence/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-presence" -version = "0.8.1" +version = "0.8.2" edition = "2021" license = "AGPL-3.0-or-later" authors = ["Paul Makles "] diff --git a/crates/core/result/Cargo.toml b/crates/core/result/Cargo.toml index 62475f5bb..8e057ba6e 100644 --- a/crates/core/result/Cargo.toml +++ b/crates/core/result/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-result" -version = "0.8.1" +version = "0.8.2" edition = "2021" license = "MIT" authors = ["Paul Makles "] diff --git a/crates/daemons/pushd/Cargo.toml b/crates/daemons/pushd/Cargo.toml index 231bba2fd..a6f1c61f1 100644 --- a/crates/daemons/pushd/Cargo.toml +++ b/crates/daemons/pushd/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "revolt-pushd" -version = "0.8.1" +version = "0.8.2" edition = "2021" [dependencies] -revolt-config = { version = "0.8.1", path = "../../core/config" } -revolt-database = { version = "0.8.1", path = "../../core/database" } -revolt-models = { version = "0.8.1", path = "../../core/models", features = [ +revolt-config = { version = "0.8.2", path = "../../core/config" } +revolt-database = { version = "0.8.2", path = "../../core/database" } +revolt-models = { version = "0.8.2", path = "../../core/models", features = [ "validator", ] } diff --git a/crates/delta/Cargo.toml b/crates/delta/Cargo.toml index 5de0a2c03..9797a5f0c 100644 --- a/crates/delta/Cargo.toml +++ b/crates/delta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-delta" -version = "0.8.1" +version = "0.8.2" license = "AGPL-3.0-or-later" authors = ["Paul Makles "] edition = "2018" diff --git a/crates/services/autumn/Cargo.toml b/crates/services/autumn/Cargo.toml index 580316c8d..31356a89b 100644 --- a/crates/services/autumn/Cargo.toml +++ b/crates/services/autumn/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-autumn" -version = "0.8.1" +version = "0.8.2" edition = "2021" [dependencies] @@ -42,12 +42,12 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } # Core crates -revolt-files = { version = "0.8.1", path = "../../core/files" } -revolt-config = { version = "0.8.1", path = "../../core/config" } -revolt-database = { version = "0.8.1", path = "../../core/database", features = [ +revolt-files = { version = "0.8.2", path = "../../core/files" } +revolt-config = { version = "0.8.2", path = "../../core/config" } +revolt-database = { version = "0.8.2", path = "../../core/database", features = [ "axum-impl", ] } -revolt-result = { version = "0.8.1", path = "../../core/result", features = [ +revolt-result = { version = "0.8.2", path = "../../core/result", features = [ "utoipa", "axum", ] } diff --git a/crates/services/january/Cargo.toml b/crates/services/january/Cargo.toml index b7ec1d1d6..005ba4065 100644 --- a/crates/services/january/Cargo.toml +++ b/crates/services/january/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-january" -version = "0.8.1" +version = "0.8.2" edition = "2021" [dependencies] @@ -31,13 +31,13 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } # Core crates -revolt-config = { version = "0.8.1", path = "../../core/config" } -revolt-models = { version = "0.8.1", path = "../../core/models" } -revolt-result = { version = "0.8.1", path = "../../core/result", features = [ +revolt-config = { version = "0.8.2", path = "../../core/config" } +revolt-models = { version = "0.8.2", path = "../../core/models" } +revolt-result = { version = "0.8.2", path = "../../core/result", features = [ "utoipa", "axum", ] } -revolt-files = { version = "0.8.1", path = "../../core/files" } +revolt-files = { version = "0.8.2", path = "../../core/files" } # Axum / web server axum = { version = "0.7.5" } From 9e5fbec9b073c7ac54c42f87e3b4065d74763d82 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 18:50:19 +0000 Subject: [PATCH 29/62] chore: disable LTO because it likely overloads GitHub worker --- Cargo.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e8c4eab68..57b4459c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,5 +18,9 @@ redis23 = { package = "redis", version = "0.23.1", git = "https://github.com/rev # rocket_authifier = { package = "rocket_authifier", version = "1.0.8", path = "../authifier/crates/rocket_authifier" } # rocket = { git = "https://github.com/rwf2/Rocket/", rev = "4dcd928" } -[profile.release] -lto = true +# I'm 99% sure this is overloading the GitHub worker +# hence builds have been failing since, let's just +# disable it for now. In the future, we could use this +# if we were rolling our own CI. +# [profile.release] +# lto = true From ae15a0b4c709631b1f4810c0960ec45f2ee7e17b Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 18:54:15 +0000 Subject: [PATCH 30/62] fix: don't allow users to time themselves out closes #376 --- crates/core/result/src/lib.rs | 1 + crates/delta/src/routes/servers/member_edit.rs | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index 84eaaaa18..2a3022557 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -117,6 +117,7 @@ pub enum ErrorType { max: usize, }, AlreadyInServer, + CannotTimeoutYourself, // ? Bot related errors ReachedMaximumBots, diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index b6e205b74..89a74b0e0 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -21,13 +21,13 @@ use validator::Validate; /// /// Edit a member by their id. #[openapi(tag = "Server Members")] -#[patch("//members/", data = "")] +#[patch("//members/", data = "")] pub async fn edit( db: &State, voice_client: &State, user: User, server: Reference, - target: Reference, + member: Reference, data: Json, ) -> Result> { let data = data.into_inner(); @@ -37,10 +37,10 @@ pub async fn edit( }) })?; - // Fetch server and target member + // Fetch server and member let mut server = server.as_server(db).await?; - let mut member = target.as_member(db, &server.id).await?; - let target_user = target.as_user(&db).await?; + let target_user = member.as_user(&db).await?; + let mut member = member.as_member(db, &server.id).await?; // Fetch our currrent permissions let mut query = DatabasePermissionQuery::new(db, &user).server(&server); @@ -92,6 +92,10 @@ pub async fn edit( .map(|x| x.contains(&v0::FieldsMember::Timeout)) .unwrap_or_default() { + if data.timeout.is_some() && member.id.user == user.id { + return Err(create_error!(CannotTimeoutYourself)); + } + permissions.throw_if_lacking_channel_permission(ChannelPermission::TimeoutMembers)?; } From 4ea08a189dba293850d5a1b3b7fab125c8c8ece3 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 19:05:21 +0000 Subject: [PATCH 31/62] fix: match new error type for status code --- crates/core/result/src/axum.rs | 1 + crates/core/result/src/rocket.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/core/result/src/axum.rs b/crates/core/result/src/axum.rs index c04d9aa5c..68b880832 100644 --- a/crates/core/result/src/axum.rs +++ b/crates/core/result/src/axum.rs @@ -42,6 +42,7 @@ impl IntoResponse for Error { ErrorType::InvalidRole => StatusCode::NOT_FOUND, ErrorType::Banned => StatusCode::FORBIDDEN, ErrorType::AlreadyInServer => StatusCode::CONFLICT, + ErrorType::CannotTimeoutYourself => StatusCode::BAD_REQUEST, ErrorType::TooManyServers { .. } => StatusCode::BAD_REQUEST, ErrorType::TooManyEmbeds { .. } => StatusCode::BAD_REQUEST, diff --git a/crates/core/result/src/rocket.rs b/crates/core/result/src/rocket.rs index da01649b9..6e5b55784 100644 --- a/crates/core/result/src/rocket.rs +++ b/crates/core/result/src/rocket.rs @@ -47,6 +47,7 @@ impl<'r> Responder<'r, 'static> for Error { ErrorType::InvalidRole => Status::NotFound, ErrorType::Banned => Status::Forbidden, ErrorType::AlreadyInServer => Status::Conflict, + ErrorType::CannotTimeoutYourself => Status::BadRequest, ErrorType::TooManyServers { .. } => Status::BadRequest, ErrorType::TooManyEmbeds { .. } => Status::BadRequest, From 9a98c53aff5dc5972083c7cc7bdd28e158199419 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Wed, 27 Nov 2024 16:56:30 +0000 Subject: [PATCH 32/62] feat: init crond crate --- Cargo.lock | 10 ++++++++++ Cargo.toml | 3 +-- crates/core/database/src/models/users/model.rs | 11 +++++++++-- crates/daemons/crond/Cargo.toml | 14 ++++++++++++++ crates/daemons/crond/src/main.rs | 3 +++ 5 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 crates/daemons/crond/Cargo.toml create mode 100644 crates/daemons/crond/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 1c5bd6016..ab295d309 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1663,6 +1663,16 @@ dependencies = [ [[package]] name = "crc64fast-nvme" version = "1.1.1" +name = "crond" +version = "0.7.19" +dependencies = [ + "revolt-database", + "revolt-files", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5e2ee08013e3f228d6d2394116c4549a6df77708442c62d887d83f68ef2ee37" dependencies = [ diff --git a/Cargo.toml b/Cargo.toml index 57b4459c6..0eaad88c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,7 @@ members = [ "crates/core/*", "crates/services/*", "crates/bindings/*", - "crates/daemons/pushd", - "crates/daemons/voice-ingress" + "crates/daemons/*", ] [patch.crates-io] diff --git a/crates/core/database/src/models/users/model.rs b/crates/core/database/src/models/users/model.rs index d85801e75..6768258e6 100644 --- a/crates/core/database/src/models/users/model.rs +++ b/crates/core/database/src/models/users/model.rs @@ -153,7 +153,7 @@ pub static DISCRIMINATOR_SEARCH_SPACE: Lazy> = Lazy::new(|| { .collect::>(); for discrim in [ - 123, 1234, 1111, 2222, 3333, 4444, 5555, 6666, 7777, 8888, 9999, + 123, 1234, 1111, 2222, 3333, 4444, 5555, 6666, 7777, 8888, 9999, 1488, ] { set.remove(&format!("{:0>4}", discrim)); } @@ -294,7 +294,14 @@ impl User { } // Ensure none of the following substrings show up in the username - const BLOCKED_SUBSTRINGS: &[&str] = &["```"]; + const BLOCKED_SUBSTRINGS: &[&str] = &[ + "```", + "discord.gg", + "rvlt.gg", + "guilded.gg", + "https://", + "http://", + ]; for substr in BLOCKED_SUBSTRINGS { if username_lowercase.contains(substr) { diff --git a/crates/daemons/crond/Cargo.toml b/crates/daemons/crond/Cargo.toml new file mode 100644 index 000000000..85ec4349a --- /dev/null +++ b/crates/daemons/crond/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "crond" +version = "0.7.19" +license = "AGPL-3.0-or-later" +authors = ["Paul Makles "] +edition = "2021" +description = "Revolt Daemon Service: Timed data clean up tasks" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# Core +revolt-database = { version = "0.7.19", path = "../../core/database" } +revolt-files = { version = "0.7.19", path = "../../core/files" } diff --git a/crates/daemons/crond/src/main.rs b/crates/daemons/crond/src/main.rs new file mode 100644 index 000000000..e7a11a969 --- /dev/null +++ b/crates/daemons/crond/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From a42746c55b6ff9f9885a16ca5c8833b9d46d819e Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Wed, 27 Nov 2024 21:54:39 +0000 Subject: [PATCH 33/62] feat: file deletion implementation --- Cargo.lock | 18 +++++++ crates/core/config/Revolt.toml | 1 + crates/core/config/src/lib.rs | 1 + .../database/src/models/file_hashes/ops.rs | 3 ++ .../src/models/file_hashes/ops/mongodb.rs | 5 ++ .../src/models/file_hashes/ops/reference.rs | 12 ++++- crates/core/database/src/models/files/ops.rs | 12 +++++ .../database/src/models/files/ops/mongodb.rs | 54 ++++++++++++++++++- .../src/models/files/ops/reference.rs | 45 ++++++++++++++++ crates/core/files/src/lib.rs | 17 ++++++ crates/daemons/crond/Cargo.toml | 10 +++- crates/daemons/crond/src/main.rs | 20 ++++++- .../daemons/crond/src/tasks/file_deletion.rs | 39 ++++++++++++++ crates/daemons/crond/src/tasks/mod.rs | 2 + .../crond/src/tasks/prune_dangling_files.rs | 36 +++++++++++++ 15 files changed, 269 insertions(+), 6 deletions(-) create mode 100644 crates/daemons/crond/src/tasks/file_deletion.rs create mode 100644 crates/daemons/crond/src/tasks/mod.rs create mode 100644 crates/daemons/crond/src/tasks/prune_dangling_files.rs diff --git a/Cargo.lock b/Cargo.lock index ab295d309..3316dc6dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2624,6 +2624,9 @@ name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -2655,6 +2658,9 @@ name = "futures-executor" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -6301,6 +6307,18 @@ dependencies = [ "serde", ] +[[package]] +name = "revolt-crond" +version = "0.7.19" +dependencies = [ + "log", + "revolt-config", + "revolt-database", + "revolt-files", + "revolt-result", + "tokio 1.35.1", +] + [[package]] name = "revolt-database" version = "0.8.2" diff --git a/crates/core/config/Revolt.toml b/crates/core/config/Revolt.toml index c2c2c2b3a..442daf027 100644 --- a/crates/core/config/Revolt.toml +++ b/crates/core/config/Revolt.toml @@ -265,3 +265,4 @@ events = "" voice_ingress = "" files = "" proxy = "" +crond = "" diff --git a/crates/core/config/src/lib.rs b/crates/core/config/src/lib.rs index 5e6404348..4fa300848 100644 --- a/crates/core/config/src/lib.rs +++ b/crates/core/config/src/lib.rs @@ -339,6 +339,7 @@ pub struct Sentry { pub voice_ingress: String, pub files: String, pub proxy: String, + pub crond: String, } #[derive(Deserialize, Debug, Clone)] diff --git a/crates/core/database/src/models/file_hashes/ops.rs b/crates/core/database/src/models/file_hashes/ops.rs index e7f626584..648a1a2f6 100644 --- a/crates/core/database/src/models/file_hashes/ops.rs +++ b/crates/core/database/src/models/file_hashes/ops.rs @@ -15,4 +15,7 @@ pub trait AbstractAttachmentHashes: Sync + Send { /// Update an attachment hash nonce value. async fn set_attachment_hash_nonce(&self, hash: &str, nonce: &str) -> Result<()>; + + /// Delete attachment hash by id. + async fn delete_attachment_hash(&self, id: &str) -> Result<()>; } diff --git a/crates/core/database/src/models/file_hashes/ops/mongodb.rs b/crates/core/database/src/models/file_hashes/ops/mongodb.rs index c55a06104..153cb72f1 100644 --- a/crates/core/database/src/models/file_hashes/ops/mongodb.rs +++ b/crates/core/database/src/models/file_hashes/ops/mongodb.rs @@ -48,4 +48,9 @@ impl AbstractAttachmentHashes for MongoDb { .map(|_| ()) .map_err(|_| create_database_error!("update_one", COL)) } + + /// Delete attachment hash by id. + async fn delete_attachment_hash(&self, id: &str) -> Result<()> { + query!(self, delete_one_by_id, COL, id).map(|_| ()) + } } diff --git a/crates/core/database/src/models/file_hashes/ops/reference.rs b/crates/core/database/src/models/file_hashes/ops/reference.rs index 86bbeee8b..7733523e3 100644 --- a/crates/core/database/src/models/file_hashes/ops/reference.rs +++ b/crates/core/database/src/models/file_hashes/ops/reference.rs @@ -23,8 +23,8 @@ impl AbstractAttachmentHashes for ReferenceDb { let hashes = self.file_hashes.lock().await; hashes .values() + .find(|&hash| hash.id == hash_value || hash.processed_hash == hash_value) .cloned() - .find(|hash| hash.id == hash_value || hash.processed_hash == hash_value) .ok_or(create_error!(NotFound)) } @@ -38,4 +38,14 @@ impl AbstractAttachmentHashes for ReferenceDb { Err(create_error!(NotFound)) } } + + /// Delete attachment hash by id. + async fn delete_attachment_hash(&self, id: &str) -> Result<()> { + let mut file_hashes = self.file_hashes.lock().await; + if file_hashes.remove(id).is_some() { + Ok(()) + } else { + Err(create_error!(NotFound)) + } + } } diff --git a/crates/core/database/src/models/files/ops.rs b/crates/core/database/src/models/files/ops.rs index 58d599d35..b30d3acc8 100644 --- a/crates/core/database/src/models/files/ops.rs +++ b/crates/core/database/src/models/files/ops.rs @@ -15,6 +15,15 @@ pub trait AbstractAttachments: Sync + Send { /// Fetch an attachment by its id. async fn fetch_attachment(&self, tag: &str, file_id: &str) -> Result; + /// Fetch all deleted attachments. + async fn fetch_deleted_attachments(&self) -> Result>; + + /// Fetch all dangling attachments. + async fn fetch_dangling_files(&self) -> Result>; + + /// Count references to a given hash. + async fn count_file_hash_references(&self, hash: &str) -> Result; + /// Find an attachment by its details and mark it as used by a given parent. async fn find_and_use_attachment( &self, @@ -32,4 +41,7 @@ pub trait AbstractAttachments: Sync + Send { /// Mark multiple attachments as having been deleted. async fn mark_attachments_as_deleted(&self, ids: &[String]) -> Result<()>; + + /// Delete the attachment entry. + async fn delete_attachment(&self, id: &str) -> Result<()>; } diff --git a/crates/core/database/src/models/files/ops/mongodb.rs b/crates/core/database/src/models/files/ops/mongodb.rs index c2295cf3d..43e8e946d 100644 --- a/crates/core/database/src/models/files/ops/mongodb.rs +++ b/crates/core/database/src/models/files/ops/mongodb.rs @@ -32,6 +32,51 @@ impl AbstractAttachments for MongoDb { .ok_or_else(|| create_error!(NotFound)) } + /// Fetch all deleted attachments. + async fn fetch_deleted_attachments(&self) -> Result> { + query!( + self, + find, + COL, + doc! { + "deleted": true, + "reported": { + "$ne": true + } + } + ) + } + + /// Fetch all dangling attachments. + async fn fetch_dangling_files(&self) -> Result> { + query!( + self, + find, + COL, + doc! { + "used_for.type": { + "$exists": 0 + }, + "deleted": { + "$ne": true + } + } + ) + } + + /// Count references to a given hash. + async fn count_file_hash_references(&self, hash: &str) -> Result { + query!( + self, + count_documents, + COL, + doc! { + "hash": hash + } + ) + .map(|count| count as usize) + } + /// Find an attachment by its details and mark it as used by a given parent. async fn find_and_use_attachment( &self, @@ -114,7 +159,7 @@ impl AbstractAttachments for MongoDb { /// Mark multiple attachments as having been deleted. async fn mark_attachments_as_deleted(&self, ids: &[String]) -> Result<()> { self.col::(COL) - .update_one( + .update_many( doc! { "_id": { "$in": ids @@ -129,7 +174,12 @@ impl AbstractAttachments for MongoDb { ) .await .map(|_| ()) - .map_err(|_| create_database_error!("update_one", COL)) + .map_err(|_| create_database_error!("update_many", COL)) + } + + /// Delete the attachment entry. + async fn delete_attachment(&self, id: &str) -> Result<()> { + query!(self, delete_one_by_id, COL, id).map(|_| ()) } } diff --git a/crates/core/database/src/models/files/ops/reference.rs b/crates/core/database/src/models/files/ops/reference.rs index e18794272..208b0b0a9 100644 --- a/crates/core/database/src/models/files/ops/reference.rs +++ b/crates/core/database/src/models/files/ops/reference.rs @@ -33,6 +33,41 @@ impl AbstractAttachments for ReferenceDb { } } + /// Fetch all deleted attachments. + async fn fetch_deleted_attachments(&self) -> Result> { + let files = self.files.lock().await; + Ok(files + .values() + .filter(|file| { + // file has been marked as deleted + file.deleted.is_some_and(|v| v) + // and it has not been reported + && !file.reported.is_some_and(|v| v) + }) + .cloned() + .collect()) + } + + /// Fetch all dangling attachments. + async fn fetch_dangling_files(&self) -> Result> { + let files = self.files.lock().await; + Ok(files + .values() + .filter(|file| file.used_for.is_none() && !file.deleted.is_some_and(|v| v)) + .cloned() + .collect()) + } + + /// Count references to a given hash. + async fn count_file_hash_references(&self, hash: &str) -> Result { + let files = self.files.lock().await; + Ok(files + .values() + .filter(|file| file.hash.as_ref().is_some_and(|h| h == hash)) + .cloned() + .count()) + } + /// Find an attachment by its details and mark it as used by a given parent. async fn find_and_use_attachment( &self, @@ -96,4 +131,14 @@ impl AbstractAttachments for ReferenceDb { Ok(()) } + + /// Delete the attachment entry. + async fn delete_attachment(&self, id: &str) -> Result<()> { + let mut files = self.files.lock().await; + if files.remove(id).is_some() { + Ok(()) + } else { + Err(create_error!(NotFound)) + } + } } diff --git a/crates/core/files/src/lib.rs b/crates/core/files/src/lib.rs index c09853dd9..dacc988e5 100644 --- a/crates/core/files/src/lib.rs +++ b/crates/core/files/src/lib.rs @@ -113,6 +113,23 @@ pub async fn upload_to_s3(bucket_id: &str, path: &str, buf: &[u8]) -> Result Result<()> { + let config = config().await; + let client = create_client(config.files.s3); + + report_internal_error!( + client + .delete_object() + .bucket(bucket_id) + .key(path) + .send() + .await + )?; + + Ok(()) +} + /// Determine size of image at temp file pub fn image_size(f: &NamedTempFile) -> Option<(usize, usize)> { if let Ok(size) = imagesize::size(f.path()) diff --git a/crates/daemons/crond/Cargo.toml b/crates/daemons/crond/Cargo.toml index 85ec4349a..f2c4b94fe 100644 --- a/crates/daemons/crond/Cargo.toml +++ b/crates/daemons/crond/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "crond" +name = "revolt-crond" version = "0.7.19" license = "AGPL-3.0-or-later" authors = ["Paul Makles "] @@ -9,6 +9,14 @@ description = "Revolt Daemon Service: Timed data clean up tasks" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Utility +log = "0.4" + +# Async +tokio = { version = "1" } + # Core revolt-database = { version = "0.7.19", path = "../../core/database" } +revolt-result = { version = "0.7.19", path = "../../core/result" } +revolt-config = { version = "0.7.19", path = "../../core/config" } revolt-files = { version = "0.7.19", path = "../../core/files" } diff --git a/crates/daemons/crond/src/main.rs b/crates/daemons/crond/src/main.rs index e7a11a969..40af6a2bd 100644 --- a/crates/daemons/crond/src/main.rs +++ b/crates/daemons/crond/src/main.rs @@ -1,3 +1,19 @@ -fn main() { - println!("Hello, world!"); +use revolt_config::configure; +use revolt_database::DatabaseInfo; +use revolt_result::Result; +use tasks::{file_deletion, prune_dangling_files}; +use tokio::try_join; + +pub mod tasks; + +#[tokio::main] +async fn main() -> Result<()> { + configure!(crond); + + let db = DatabaseInfo::Auto.connect().await.expect("database"); + try_join!( + file_deletion::task(db.clone()), + prune_dangling_files::task(db) + ) + .map(|_| ()) } diff --git a/crates/daemons/crond/src/tasks/file_deletion.rs b/crates/daemons/crond/src/tasks/file_deletion.rs new file mode 100644 index 000000000..912173b3f --- /dev/null +++ b/crates/daemons/crond/src/tasks/file_deletion.rs @@ -0,0 +1,39 @@ +use std::time::Duration; + +use log::info; +use revolt_database::Database; +use revolt_files::delete_from_s3; +use revolt_result::Result; +use tokio::time::sleep; + +pub async fn task(db: Database) -> Result<()> { + loop { + let files = db.fetch_deleted_attachments().await?; + + for file in files { + let count = db + .count_file_hash_references(file.hash.as_ref().expect("no `hash` present")) + .await?; + + // No other files reference this file on disk anymore + if count <= 1 { + let file_hash = db + .fetch_attachment_hash(file.hash.as_ref().expect("no `hash` present")) + .await?; + + // Delete from S3 + delete_from_s3(&file_hash.bucket_id, &file_hash.path).await?; + + // Delete the hash + db.delete_attachment_hash(&file_hash.id).await?; + info!("Deleted file hash {}", file_hash.id); + } + + // Delete the file + db.delete_attachment(&file.id).await?; + info!("Deleted file {}", file.id); + } + + sleep(Duration::from_secs(60)).await; + } +} diff --git a/crates/daemons/crond/src/tasks/mod.rs b/crates/daemons/crond/src/tasks/mod.rs new file mode 100644 index 000000000..2a668f14c --- /dev/null +++ b/crates/daemons/crond/src/tasks/mod.rs @@ -0,0 +1,2 @@ +pub mod file_deletion; +pub mod prune_dangling_files; diff --git a/crates/daemons/crond/src/tasks/prune_dangling_files.rs b/crates/daemons/crond/src/tasks/prune_dangling_files.rs new file mode 100644 index 000000000..be1eb6d6b --- /dev/null +++ b/crates/daemons/crond/src/tasks/prune_dangling_files.rs @@ -0,0 +1,36 @@ +use std::time::Duration; + +use revolt_database::{iso8601_timestamp::Timestamp, Database}; +use revolt_result::Result; +use tokio::time::sleep; + +use log::info; + +pub async fn task(db: Database) -> Result<()> { + loop { + // This could just be a single database query + // ... but timestamps are inconsistently serialised + // ... sometimes they are dates/numbers, hard to query + // ... in the future, we could use Postgres instead! :D + // ... + // ... on the plus side, it's still only 2 queries + + let files = db.fetch_dangling_files().await?; + let file_ids: Vec = files + .into_iter() + .filter(|file| { + file.uploaded_at.is_some_and(|uploaded_at| { + Timestamp::now_utc().duration_since(uploaded_at) > Duration::from_secs(60 * 60) + }) + }) + .map(|file| file.id) + .collect(); + + if !file_ids.is_empty() { + db.mark_attachments_as_deleted(&file_ids).await?; + info!("Marked {} dangling files for deletion", file_ids.len()); + } + + sleep(Duration::from_secs(60)).await; + } +} From d54c5e80fa6c59312e34a1f3be81ebda06eccba5 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 19:03:48 +0000 Subject: [PATCH 34/62] chore: fix version references --- Cargo.lock | 4 ++-- crates/daemons/crond/Cargo.toml | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3316dc6dd..af86e6dde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6309,14 +6309,14 @@ dependencies = [ [[package]] name = "revolt-crond" -version = "0.7.19" +version = "0.8.2" dependencies = [ "log", "revolt-config", "revolt-database", "revolt-files", "revolt-result", - "tokio 1.35.1", + "tokio 1.40.0", ] [[package]] diff --git a/crates/daemons/crond/Cargo.toml b/crates/daemons/crond/Cargo.toml index f2c4b94fe..bd5d3f3b2 100644 --- a/crates/daemons/crond/Cargo.toml +++ b/crates/daemons/crond/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-crond" -version = "0.7.19" +version = "0.8.2" license = "AGPL-3.0-or-later" authors = ["Paul Makles "] edition = "2021" @@ -16,7 +16,7 @@ log = "0.4" tokio = { version = "1" } # Core -revolt-database = { version = "0.7.19", path = "../../core/database" } -revolt-result = { version = "0.7.19", path = "../../core/result" } -revolt-config = { version = "0.7.19", path = "../../core/config" } -revolt-files = { version = "0.7.19", path = "../../core/files" } +revolt-database = { version = "0.8.2", path = "../../core/database" } +revolt-result = { version = "0.8.2", path = "../../core/result" } +revolt-config = { version = "0.8.2", path = "../../core/config" } +revolt-files = { version = "0.8.2", path = "../../core/files" } From 370442bf4edeb94dd296e97ec199a66a9605b85d Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 10 Feb 2025 20:28:48 +0000 Subject: [PATCH 35/62] feat: add crond container definitions --- .github/workflows/docker.yaml | 4 ++++ Dockerfile | 1 + Dockerfile.useCurrentArch | 1 + README.md | 1 + crates/daemons/crond/Dockerfile | 10 ++++++++++ scripts/build-image-layer.sh | 3 +++ scripts/publish-debug-image.sh | 2 ++ 7 files changed, 22 insertions(+) create mode 100644 crates/daemons/crond/Dockerfile diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 58e186d0b..761078765 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -110,6 +110,10 @@ jobs: "pushd": { "path": "crates/daemons/pushd", "tag": "${{ github.repository_owner }}/pushd" + }, + "crond": { + "path": "crates/daemons/crond", + "tag": "${{ github.repository_owner }}/crond" } } export_to: output diff --git a/Dockerfile b/Dockerfile index 10112b667..c75748c20 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,6 +29,7 @@ COPY crates/core/presence/Cargo.toml ./crates/core/presence/ COPY crates/core/result/Cargo.toml ./crates/core/result/ COPY crates/services/autumn/Cargo.toml ./crates/services/autumn/ COPY crates/services/january/Cargo.toml ./crates/services/january/ +COPY crates/daemons/crond/Cargo.toml ./crates/daemons/crond/ COPY crates/daemons/pushd/Cargo.toml ./crates/daemons/pushd/ RUN sh /tmp/build-image-layer.sh deps diff --git a/Dockerfile.useCurrentArch b/Dockerfile.useCurrentArch index 34a69462e..54b508ef7 100644 --- a/Dockerfile.useCurrentArch +++ b/Dockerfile.useCurrentArch @@ -25,6 +25,7 @@ COPY crates/core/presence/Cargo.toml ./crates/core/presence/ COPY crates/core/result/Cargo.toml ./crates/core/result/ COPY crates/services/autumn/Cargo.toml ./crates/services/autumn/ COPY crates/services/january/Cargo.toml ./crates/services/january/ +COPY crates/daemons/crond/Cargo.toml ./crates/daemons/crond/ COPY crates/daemons/pushd/Cargo.toml ./crates/daemons/pushd/ RUN sh /tmp/build-image-layer.sh deps diff --git a/README.md b/README.md index c480340f5..d0894dee7 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ The services and libraries that power the Revolt service.
| `services/january` | [crates/services/january](crates/services/january) | Proxy server | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | | `services/autumn` | [crates/services/autumn](crates/services/autumn) | File server | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | | `bindings/node` | [crates/bindings/node](crates/bindings/node) | Node.js bindings for the Revolt software | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | +| `daemons/crond` | [crates/daemons/crond](crates/daemons/crond) | Timed data clean up daemon server | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | | `daemons/pushd` | [crates/daemons/pushd](crates/daemons/pushd) | Push notification daemon server | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | diff --git a/crates/daemons/crond/Dockerfile b/crates/daemons/crond/Dockerfile new file mode 100644 index 000000000..8a4c915a9 --- /dev/null +++ b/crates/daemons/crond/Dockerfile @@ -0,0 +1,10 @@ +# Build Stage +FROM ghcr.io/revoltchat/base:latest AS builder +FROM debian:12 AS debian + +# Bundle Stage +FROM gcr.io/distroless/cc-debian12:nonroot +COPY --from=builder /home/rust/src/target/release/revolt-crond ./ + +USER nonroot +CMD ["./revolt-crond"] \ No newline at end of file diff --git a/scripts/build-image-layer.sh b/scripts/build-image-layer.sh index 4b2e18c47..064ee7564 100644 --- a/scripts/build-image-layer.sh +++ b/scripts/build-image-layer.sh @@ -34,12 +34,14 @@ deps() { crates/core/result/src \ crates/services/autumn/src \ crates/services/january/src \ + crates/daemons/crond/src \ crates/daemons/pushd/src echo 'fn main() { panic!("stub"); }' | tee crates/bonfire/src/main.rs | tee crates/delta/src/main.rs | tee crates/services/autumn/src/main.rs | tee crates/services/january/src/main.rs | + tee crates/daemons/crond/src/main.rs | tee crates/daemons/pushd/src/main.rs echo '' | tee crates/bindings/node/src/lib.rs | @@ -62,6 +64,7 @@ apps() { touch -am \ crates/bonfire/src/main.rs \ crates/delta/src/main.rs \ + crates/daemons/crond/src/main.rs \ crates/daemons/pushd/src/main.rs \ crates/core/config/src/lib.rs \ crates/core/database/src/lib.rs \ diff --git a/scripts/publish-debug-image.sh b/scripts/publish-debug-image.sh index 247338ab5..6686344cb 100755 --- a/scripts/publish-debug-image.sh +++ b/scripts/publish-debug-image.sh @@ -25,6 +25,7 @@ docker build -t ghcr.io/revoltchat/server:$TAG - < crates/delta/Dockerfile docker build -t ghcr.io/revoltchat/bonfire:$TAG - < crates/bonfire/Dockerfile docker build -t ghcr.io/revoltchat/autumn:$TAG - < crates/services/autumn/Dockerfile docker build -t ghcr.io/revoltchat/january:$TAG - < crates/services/january/Dockerfile +docker build -t ghcr.io/revoltchat/crond:$TAG - < crates/daemons/crond/Dockerfile docker build -t ghcr.io/revoltchat/pushd:$TAG - < crates/daemons/pushd/Dockerfile if [ "$DEBUG" = "true" ]; then @@ -35,4 +36,5 @@ docker push ghcr.io/revoltchat/server:$TAG docker push ghcr.io/revoltchat/bonfire:$TAG docker push ghcr.io/revoltchat/autumn:$TAG docker push ghcr.io/revoltchat/january:$TAG +docker push ghcr.io/revoltchat/crond:$TAG docker push ghcr.io/revoltchat/pushd:$TAG From 92f4b44cb3affe983e53da909de277ea5820b47f Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Tue, 18 Feb 2025 11:53:21 +0000 Subject: [PATCH 36/62] chore: clarify cargo deny / tomls --- crates/daemons/pushd/Cargo.toml | 1 + crates/services/autumn/Cargo.toml | 1 + crates/services/january/Cargo.toml | 1 + deny.toml | 185 ++++++++++++++++++----------- 4 files changed, 116 insertions(+), 72 deletions(-) diff --git a/crates/daemons/pushd/Cargo.toml b/crates/daemons/pushd/Cargo.toml index a6f1c61f1..be0e3cc5d 100644 --- a/crates/daemons/pushd/Cargo.toml +++ b/crates/daemons/pushd/Cargo.toml @@ -2,6 +2,7 @@ name = "revolt-pushd" version = "0.8.2" edition = "2021" +license = "AGPL-3.0-or-later" [dependencies] revolt-config = { version = "0.8.2", path = "../../core/config" } diff --git a/crates/services/autumn/Cargo.toml b/crates/services/autumn/Cargo.toml index 31356a89b..4b944f553 100644 --- a/crates/services/autumn/Cargo.toml +++ b/crates/services/autumn/Cargo.toml @@ -2,6 +2,7 @@ name = "revolt-autumn" version = "0.8.2" edition = "2021" +license = "AGPL-3.0-or-later" [dependencies] # ID generation diff --git a/crates/services/january/Cargo.toml b/crates/services/january/Cargo.toml index 005ba4065..a053d11a9 100644 --- a/crates/services/january/Cargo.toml +++ b/crates/services/january/Cargo.toml @@ -2,6 +2,7 @@ name = "revolt-january" version = "0.8.2" edition = "2021" +license = "AGPL-3.0-or-later" [dependencies] # Utility diff --git a/deny.toml b/deny.toml index 48dc5f08a..d8f1bf679 100644 --- a/deny.toml +++ b/deny.toml @@ -9,6 +9,11 @@ # The values provided in this template are the default values that will be used # when any section or field is not specified in your own configuration +# Root options + +# The graph table configures how the dependency graph is constructed and thus +# which crates the checks are performed against +[graph] # If 1 or more target triples (and optionally, target_features) are specified, # only the specified targets will be checked when running `cargo deny check`. # This means, if a particular package is only ever used as a target specific @@ -20,85 +25,83 @@ targets = [ # The triple can be any string, but only the target triples built in to # rustc (as of 1.40) can be checked against actual config expressions - #{ triple = "x86_64-unknown-linux-musl" }, + #"x86_64-unknown-linux-musl", # You can also specify which target_features you promise are enabled for a # particular target. target_features are currently not validated against # the actual valid features supported by the target architecture. #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, ] +# When creating the dependency graph used as the source of truth when checks are +# executed, this field can be used to prune crates from the graph, removing them +# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate +# is pruned from the graph, all of its dependencies will also be pruned unless +# they are connected to another crate in the graph that hasn't been pruned, +# so it should be used with care. The identifiers are [Package ID Specifications] +# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html) +#exclude = [] +# If true, metadata will be collected with `--all-features`. Note that this can't +# be toggled off if true, if you want to conditionally enable `--all-features` it +# is recommended to pass `--all-features` on the cmd line instead +all-features = false +# If true, metadata will be collected with `--no-default-features`. The same +# caveat with `all-features` applies +no-default-features = false +# If set, these feature will be enabled when collecting metadata. If `--features` +# is specified on the cmd line they will take precedence over this option. +#features = [] + +# The output table provides options for how/if diagnostics are outputted +[output] +# When outputting inclusion graphs in diagnostics that include features, this +# option can be used to specify the depth at which feature edges will be added. +# This option is included since the graphs can be quite large and the addition +# of features from the crate(s) to all of the graph roots can be far too verbose. +# This option can be overridden via `--feature-depth` on the cmd line +feature-depth = 1 # This section is considered when running `cargo deny check advisories` # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] -# The path where the advisory database is cloned/fetched into -db-path = "~/.cargo/advisory-db" +# The path where the advisory databases are cloned/fetched into +#db-path = "$CARGO_HOME/advisory-dbs" # The url(s) of the advisory databases to use -db-urls = ["https://github.com/rustsec/advisory-db"] -# The lint level for security vulnerabilities -vulnerability = "deny" -# The lint level for unmaintained crates -unmaintained = "warn" -# The lint level for crates that have been yanked from their source registry -yanked = "warn" -# The lint level for crates with security notices. Note that as of -# 2019-12-17 there are no security notice advisories in -# https://github.com/rustsec/advisory-db -notice = "warn" +#db-urls = ["https://github.com/rustsec/advisory-db"] # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ #"RUSTSEC-0000-0000", + #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" }, + #"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish + #{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" }, ] -# Threshold for security vulnerabilities, any vulnerability with a CVSS score -# lower than the range specified will be ignored. Note that ignored advisories -# will still output a note when they are encountered. -# * None - CVSS Score 0.0 -# * Low - CVSS Score 0.1 - 3.9 -# * Medium - CVSS Score 4.0 - 6.9 -# * High - CVSS Score 7.0 - 8.9 -# * Critical - CVSS Score 9.0 - 10.0 -#severity-threshold = +# If this is true, then cargo deny will use the git executable to fetch advisory database. +# If this is false, then it uses a built-in git library. +# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. +# See Git Authentication for more information about setting up git authentication. +#git-fetch-with-cli = true # This section is considered when running `cargo deny check licenses` # More documentation for the licenses section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] -# The lint level for crates which do not have a detectable license -unlicensed = "warn" # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. allow = [ + # any non-copyleft licenses are okay! "MIT", "ISC", "0BSD", + "Zlib", "CC0-1.0", + "MPL-2.0", "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", "BSD-2-Clause", "BSD-3-Clause", - #"Apache-2.0 WITH LLVM-exception", -] -# List of explicitly disallowed licenses -# See https://spdx.org/licenses/ for list of possible licenses -# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. -deny = [ - #"Nokia", + "OpenSSL", ] -# Lint level for licenses considered copyleft -copyleft = "warn" -# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses -# * both - The license will be approved if it is both OSI-approved *AND* FSF -# * either - The license will be approved if it is either OSI-approved *OR* FSF -# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF -# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved -# * neither - This predicate is ignored and the default lint level is used -allow-osi-fsf-free = "neither" -# Lint level used when no other predicates are matched -# 1. License isn't in the allow or deny lists -# 2. License isn't copyleft -# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" -default = "deny" # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the # canonical license text of a valid SPDX license file. @@ -108,29 +111,34 @@ confidence-threshold = 0.8 # aren't accepted for every possible crate as with the normal allow list exceptions = [ # Each entry is the crate and version constraint, and its specific allow - # list - #{ allow = ["Zlib"], name = "adler32", version = "*" }, + { allow = ["AGPL-3.0"], crate = "revolt-database" }, + { allow = ["AGPL-3.0"], crate = "revolt-presence" }, + { allow = ["AGPL-3.0"], crate = "revolt-files" }, + { allow = ["AGPL-3.0"], crate = "revolt-pushd" }, + { allow = ["AGPL-3.0"], crate = "revolt-crond" }, + { allow = ["AGPL-3.0"], crate = "revolt-delta" }, + { allow = ["AGPL-3.0"], crate = "revolt-bonfire" }, + { allow = ["AGPL-3.0"], crate = "revolt-autumn" }, + { allow = ["AGPL-3.0"], crate = "revolt-january" }, ] # Some crates don't have (easily) machine readable licensing information, # adding a clarification entry for it allows you to manually specify the # licensing information -#[[licenses.clarify]] -# The name of the crate the clarification applies to -#name = "ring" -# The optional version constraint for the crate -#version = "*" +[[licenses.clarify]] +# The package spec the clarification applies to +crate = "ring" # The SPDX expression for the license requirements of the crate -#expression = "MIT AND ISC AND OpenSSL" +expression = "MIT AND ISC AND OpenSSL" # One or more files in the crate's source used as the "source of truth" for # the license expression. If the contents match, the clarification will be used # when running the license check, otherwise the clarification will be ignored # and the crate will be checked normally, which may produce warnings or errors # depending on the rest of your configuration -#license-files = [ +license-files = [ # Each entry is a crate relative path, and the (opaque) hash of its contents - #{ path = "LICENSE", hash = 0xbd0eed23 } -#] + { path = "LICENSE", hash = 0xbd0eed23 }, +] [licenses.private] # If true, ignores workspace crates that aren't published, or are only @@ -159,30 +167,63 @@ wildcards = "allow" # * simplest-path - The path to the version with the fewest edges is highlighted # * all - Both lowest-version and simplest-path are used highlight = "all" +# The default lint level for `default` features for crates that are members of +# the workspace that is being checked. This can be overridden by allowing/denying +# `default` on a crate-by-crate basis if desired. +workspace-default-features = "allow" +# The default lint level for `default` features for external crates that are not +# members of the workspace. This can be overridden by allowing/denying `default` +# on a crate-by-crate basis if desired. +external-default-features = "allow" # List of crates that are allowed. Use with care! allow = [ - #{ name = "ansi_term", version = "=0.11.0" }, + #"ansi_term@0.11.0", + #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is allowed" }, ] # List of crates to deny deny = [ - # Each entry the name of a crate and a version range. If version is - # not specified, all versions will be matched. - #{ name = "ansi_term", version = "=0.11.0" }, - # + #"ansi_term@0.11.0", + #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is banned" }, # Wrapper crates can optionally be specified to allow the crate when it # is a direct dependency of the otherwise banned crate - #{ name = "ansi_term", version = "=0.11.0", wrappers = [] }, + #{ crate = "ansi_term@0.11.0", wrappers = ["this-crate-directly-depends-on-ansi_term"] }, ] + +# List of features to allow/deny +# Each entry the name of a crate and a version range. If version is +# not specified, all versions will be matched. +#[[bans.features]] +#crate = "reqwest" +# Features to not allow +#deny = ["json"] +# Features to allow +#allow = [ +# "rustls", +# "__rustls", +# "__tls", +# "hyper-rustls", +# "rustls", +# "rustls-pemfile", +# "rustls-tls-webpki-roots", +# "tokio-rustls", +# "webpki-roots", +#] +# If true, the allowed features must exactly match the enabled feature set. If +# this is set there is no point setting `deny` +#exact = true + # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ - #{ name = "ansi_term", version = "=0.11.0" }, + #"ansi_term@0.11.0", + #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason why it can't be updated/removed" }, ] # Similarly to `skip` allows you to skip certain crates during duplicate # detection. Unlike skip, it also includes the entire tree of transitive # dependencies starting at the specified crate, up to a certain depth, which is -# by default infinite +# by default infinite. skip-tree = [ - #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, + #"ansi_term@0.11.0", # will be skipped along with _all_ of its direct and transitive dependencies + #{ crate = "ansi_term@0.11.0", depth = 20 }, ] # This section is considered when running `cargo deny check sources`. @@ -202,9 +243,9 @@ allow-registry = ["https://github.com/rust-lang/crates.io-index"] allow-git = [] [sources.allow-org] -# 1 or more github.com organizations to allow git sources for -github = ["insertish"] -# 1 or more gitlab.com organizations to allow git sources for -# gitlab = [] -# 1 or more bitbucket.org organizations to allow git sources for -# bitbucket = [""] +# github.com organizations to allow git sources for +github = [] +# gitlab.com organizations to allow git sources for +gitlab = [] +# bitbucket.org organizations to allow git sources for +bitbucket = [] From 6558e9f7c4d2bf7a1569d93783c23eceb59047b6 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 24 Feb 2025 22:17:48 +0000 Subject: [PATCH 37/62] feat: add company information to email footers --- crates/core/config/src/lib.rs | 10 +- crates/core/database/src/drivers/mod.rs | 64 +- crates/core/database/templates/deletion.html | 527 +++------------- .../database/templates/deletion.original.html | 8 + crates/core/database/templates/deletion.txt | 7 + .../templates/deletion.whitelabel.txt | 9 + .../templates/policy-change.original.html | 53 ++ crates/core/database/templates/reset.html | 524 +++------------- .../database/templates/reset.original.html | 8 + crates/core/database/templates/reset.txt | 7 + .../database/templates/reset.whitelabel.txt | 9 + .../core/database/templates/suspension.html | 587 +++--------------- .../templates/suspension.original.html | 8 + crates/core/database/templates/suspension.txt | 7 + .../templates/suspension.whitelabel.txt | 8 + crates/core/database/templates/verify.html | 524 +++------------- .../database/templates/verify.original.html | 8 + crates/core/database/templates/verify.txt | 7 + .../database/templates/verify.whitelabel.txt | 10 + 19 files changed, 502 insertions(+), 1883 deletions(-) create mode 100644 crates/core/database/templates/deletion.whitelabel.txt create mode 100644 crates/core/database/templates/policy-change.original.html create mode 100644 crates/core/database/templates/reset.whitelabel.txt create mode 100644 crates/core/database/templates/suspension.whitelabel.txt create mode 100644 crates/core/database/templates/verify.whitelabel.txt diff --git a/crates/core/config/src/lib.rs b/crates/core/config/src/lib.rs index 4fa300848..fa12b84b8 100644 --- a/crates/core/config/src/lib.rs +++ b/crates/core/config/src/lib.rs @@ -352,6 +352,7 @@ pub struct Settings { pub files: Files, pub features: Features, pub sentry: Sentry, + pub production: bool, } impl Settings { @@ -379,7 +380,14 @@ pub async fn read() -> Config { #[cached(time = 30)] pub async fn config() -> Settings { - read().await.try_deserialize::().unwrap() + let mut config = read().await.try_deserialize::().unwrap(); + + // auto-detect production nodes + if config.hosts.api.contains("https") && config.hosts.api.contains("revolt.chat") { + config.production = true; + } + + config } /// Configure logging and common Rust variables diff --git a/crates/core/database/src/drivers/mod.rs b/crates/core/database/src/drivers/mod.rs index f24263aef..f3ffbede2 100644 --- a/crates/core/database/src/drivers/mod.rs +++ b/crates/core/database/src/drivers/mod.rs @@ -120,26 +120,50 @@ impl Database { use_tls: config.api.smtp.use_tls, }, expiry: Default::default(), - templates: Templates { - verify: Template { - title: "Verify your Revolt account.".into(), - text: include_str!("../../templates/verify.txt").into(), - url: format!("{}/login/verify/", config.hosts.app), - html: Some(include_str!("../../templates/verify.html").into()), - }, - reset: Template { - title: "Reset your Revolt password.".into(), - text: include_str!("../../templates/reset.txt").into(), - url: format!("{}/login/reset/", config.hosts.app), - html: Some(include_str!("../../templates/reset.html").into()), - }, - deletion: Template { - title: "Confirm account deletion.".into(), - text: include_str!("../../templates/deletion.txt").into(), - url: format!("{}/delete/", config.hosts.app), - html: Some(include_str!("../../templates/deletion.html").into()), - }, - welcome: None, + templates: if config.production { + Templates { + verify: Template { + title: "Verify your Revolt account.".into(), + text: include_str!("../../templates/verify.txt").into(), + url: format!("{}/login/verify/", config.hosts.app), + html: Some(include_str!("../../templates/verify.html").into()), + }, + reset: Template { + title: "Reset your Revolt password.".into(), + text: include_str!("../../templates/reset.txt").into(), + url: format!("{}/login/reset/", config.hosts.app), + html: Some(include_str!("../../templates/reset.html").into()), + }, + deletion: Template { + title: "Confirm account deletion.".into(), + text: include_str!("../../templates/deletion.txt").into(), + url: format!("{}/delete/", config.hosts.app), + html: Some(include_str!("../../templates/deletion.html").into()), + }, + welcome: None, + } + } else { + Templates { + verify: Template { + title: "Verify your account.".into(), + text: include_str!("../../templates/verify.whitelabel.txt").into(), + url: format!("{}/login/verify/", config.hosts.app), + html: None, + }, + reset: Template { + title: "Reset your password.".into(), + text: include_str!("../../templates/reset.whitelabel.txt").into(), + url: format!("{}/login/reset/", config.hosts.app), + html: None, + }, + deletion: Template { + title: "Confirm account deletion.".into(), + text: include_str!("../../templates/deletion.whitelabel.txt").into(), + url: format!("{}/delete/", config.hosts.app), + html: None, + }, + welcome: None, + } }, } } else { diff --git a/crates/core/database/templates/deletion.html b/crates/core/database/templates/deletion.html index ef19ee40f..d25138838 100644 --- a/crates/core/database/templates/deletion.html +++ b/crates/core/database/templates/deletion.html @@ -1,464 +1,93 @@ - - - - - - + + + + + - - + + -