Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 39d6f5bac6 | |||
| 3d73b53136 | |||
| 101fdbc9b1 | |||
| e53c2fbc5a | |||
| 95006f7e46 | |||
| cce270d938 | |||
| 5ec49b3f62 | |||
| e4dc4a1ba5 | |||
| d2fb6d04c9 | |||
| 05efd9b044 | |||
| b3f2288d07 | |||
| 084751ae38 | |||
| aa7a310200 | |||
| 38c989a07e | |||
| 5be679e17b | |||
| b8baa1223d | |||
| b87f1649d4 | |||
| 4a6f089b23 | |||
| 4600c7f32d | |||
| 4cc92dd175 | |||
| 93ec4e579b | |||
| c111d2e395 | |||
| 03d890cd49 | |||
| a35b6cbfdd | |||
| 5570220c89 | |||
| 899b79873e | |||
| 57969f9480 | |||
| d88ab37120 | |||
| bc58e5002d | |||
| 160f48043e | |||
| 0023b09f5b | |||
| db3c718ddc | |||
| e73aa2aa21 | |||
| 61f2a3c68b | |||
| 0627b46f40 | |||
| f10f5319db | |||
| 16e76d45cb | |||
| 2a304c2b6c | |||
| 271f720286 | |||
| 2d251eb19c | |||
| 01b2928d55 | |||
| 50c2d2b801 | |||
| 5dcdafe207 | |||
| c62d653989 | |||
| 454dd43d4c | |||
| 8077e910f6 | |||
| b5d4a1c1b0 | |||
| aa9540af21 | |||
| 080975ab0e | |||
| f129d90900 | |||
| 53223a4d5f | |||
| 05befa4ba2 | |||
| a8e690f22b | |||
| 192c1e08da | |||
| 0fa6976d86 | |||
| 93e7cf461d | |||
| d036d8adcb | |||
| 7a4bbe2ff6 | |||
| 438911c18d | |||
| bd71435a22 | |||
| 53fa7c3729 | |||
| d2facaee0b | |||
| 0dae9280d9 | |||
| 35336eb686 | |||
| efea13a675 | |||
| b5ee15a216 | |||
| 0873e18e14 | |||
| 6abc4ad798 | |||
| 373991a8d6 | |||
| 68ad351f84 | |||
| 83e853e7a3 | |||
| 1c453b1b55 | |||
| 60141950f7 | |||
| 391bfd986e | |||
| da03de1d32 | |||
| e54f4d4397 | |||
| daa5c34ea3 | |||
| 3259ea08b5 | |||
| 158de10fe6 | |||
| 4718387dbe | |||
| a43c78e55f | |||
| 7ba0777bd3 | |||
| 59d86d3641 | |||
| 51df946911 | |||
| 23a9055199 | |||
| 100c6f572b | |||
| eb5556e74e | |||
| 8296e0ed67 | |||
| 8e3be6feb0 | |||
| 59c4062305 | |||
| 113a27c1d5 | |||
| 5af880e4f4 | |||
| 56a1b0e761 | |||
| 5722c4ae39 | |||
| dfd13780df | |||
| dcd7422c45 | |||
| 7397064edd | |||
| 52a561ff9e | |||
| 5e72d36800 | |||
| 611f09829e | |||
| a388c2e06e | |||
| 24b37e03a0 | |||
| a309ef55c9 | |||
| c1c084dda1 | |||
| 72d9e8ed2b | |||
| a3638dbb15 | |||
| 5254eb4f72 | |||
| b31e81a469 | |||
| 0e580292a6 | |||
| 38a24e0170 | |||
| 8350aced39 | |||
| 04e3de08eb | |||
| 2bc53139fa | |||
| 8691141237 |
+11
-14
@@ -129,15 +129,10 @@ jobs:
|
||||
run: |
|
||||
direnv exec . engage > >(tee -a test_output.log)
|
||||
|
||||
- name: Sync Complement repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'matrix-org/complement'
|
||||
path: complement_src
|
||||
|
||||
- name: Run Complement tests
|
||||
run: |
|
||||
direnv exec . bin/complement 'complement_src' 'complement_test_logs.jsonl' 'complement_test_results.jsonl'
|
||||
# the nix devshell sets $COMPLEMENT_SRC, so "/dev/null" is no-op
|
||||
direnv exec . bin/complement "/dev/null" complement_test_logs.jsonl complement_test_results.jsonl > >(tee -a test_output.log)
|
||||
cp -v -f result complement_oci_image.tar.gz
|
||||
|
||||
- name: Upload Complement OCI image
|
||||
@@ -163,11 +158,7 @@ jobs:
|
||||
|
||||
- name: Diff Complement results with checked-in repo results
|
||||
run: |
|
||||
diff -u --color=always tests/test_results/complement/test_results.jsonl complement_test_results.jsonl > >(tee -a complement_test_output.log)
|
||||
echo '# Complement diff results' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```diff' >> $GITHUB_STEP_SUMMARY
|
||||
tail -n 100 complement_test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
diff -u --color=always tests/test_results/complement/test_results.jsonl complement_test_results.jsonl > >(tee -a complement_diff_output.log)
|
||||
|
||||
- name: Update Job Summary
|
||||
if: success() || failure()
|
||||
@@ -175,9 +166,15 @@ jobs:
|
||||
if [ ${{ job.status }} == 'success' ]; then
|
||||
echo '# ✅ completed suwuccessfully' >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo '# CI failure' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
tail -n 40 test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
echo '# Complement diff results' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```diff' >> $GITHUB_STEP_SUMMARY
|
||||
tail -n 100 complement_diff_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
build:
|
||||
@@ -249,7 +246,7 @@ jobs:
|
||||
CARGO_DEB_TARGET_TUPLE=$(echo ${{ matrix.target }} | grep -o -E '^([^-]*-){3}[^-]*')
|
||||
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
|
||||
|
||||
bin/nix-build-and-cache just .#static-${{ matrix.target }}
|
||||
bin/nix-build-and-cache just .#static-${{ matrix.target }}-all-features
|
||||
mkdir -v -p target/release/
|
||||
mkdir -v -p target/$CARGO_DEB_TARGET_TUPLE/release/
|
||||
cp -v -f result/bin/conduit target/release/conduwuit
|
||||
@@ -276,7 +273,7 @@ jobs:
|
||||
|
||||
- name: Build OCI image ${{ matrix.target }}
|
||||
run: |
|
||||
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}
|
||||
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}-all-features
|
||||
cp -v -f result oci-image-${{ matrix.target }}.tar.gz
|
||||
|
||||
- name: Upload OCI image ${{ matrix.target }}
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Trivy code and vulnerability scanner on repo
|
||||
uses: aquasecurity/trivy-action@0.23.0
|
||||
uses: aquasecurity/trivy-action@0.24.0
|
||||
with:
|
||||
scan-type: repo
|
||||
format: sarif
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
severity: CRITICAL,HIGH,MEDIUM,LOW
|
||||
|
||||
- name: Run Trivy code and vulnerability scanner on filesystem
|
||||
uses: aquasecurity/trivy-action@0.23.0
|
||||
uses: aquasecurity/trivy-action@0.24.0
|
||||
with:
|
||||
scan-type: fs
|
||||
format: sarif
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ before_script:
|
||||
|
||||
ci:
|
||||
stage: ci
|
||||
image: nixos/nix:2.23.1
|
||||
image: nixos/nix:2.23.3
|
||||
script:
|
||||
# Cache CI dependencies
|
||||
- ./bin/nix-build-and-cache ci
|
||||
@@ -83,7 +83,7 @@ ci:
|
||||
|
||||
artifacts:
|
||||
stage: artifacts
|
||||
image: nixos/nix:2.23.1
|
||||
image: nixos/nix:2.23.3
|
||||
script:
|
||||
- ./bin/nix-build-and-cache just .#static-x86_64-unknown-linux-musl
|
||||
- cp result/bin/conduit x86_64-unknown-linux-musl
|
||||
|
||||
Generated
+163
-147
@@ -118,18 +118,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
||||
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -166,7 +166,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.29",
|
||||
"hyper 0.14.30",
|
||||
"itoa",
|
||||
"matchit",
|
||||
"memchr",
|
||||
@@ -192,9 +192,9 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.4.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
@@ -253,7 +253,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
@@ -276,7 +276,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"headers",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
@@ -296,9 +296,9 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.4.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"rustls 0.21.12",
|
||||
@@ -377,7 +377,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -457,9 +457,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2-sys"
|
||||
@@ -474,9 +474,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.104"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490"
|
||||
checksum = "18e2d530f35b40a84124146478cd16f34225306a8441998836466a2e2961c950"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -504,6 +504,15 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "checked_ops"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b491d76efc1d99d74de3c8529bee64c62312c275c7eb124f9185291de45801d5"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
@@ -526,9 +535,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.8"
|
||||
version = "4.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
|
||||
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -536,9 +545,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.8"
|
||||
version = "4.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
|
||||
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
@@ -553,7 +562,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -570,7 +579,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "conduit"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"conduit_admin",
|
||||
@@ -580,6 +589,7 @@ dependencies = [
|
||||
"conduit_router",
|
||||
"conduit_service",
|
||||
"console-subscriber",
|
||||
"const-str",
|
||||
"hardened_malloc-rs",
|
||||
"log",
|
||||
"opentelemetry",
|
||||
@@ -598,19 +608,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduit_admin"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"conduit_api",
|
||||
"conduit_core",
|
||||
"conduit_database",
|
||||
"conduit_service",
|
||||
"const-str",
|
||||
"futures-util",
|
||||
"log",
|
||||
"loole",
|
||||
"regex",
|
||||
"ruma",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"tokio",
|
||||
@@ -620,7 +627,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduit_api"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
dependencies = [
|
||||
"axum 0.7.5",
|
||||
"axum-client-ip",
|
||||
@@ -630,10 +637,12 @@ dependencies = [
|
||||
"conduit_core",
|
||||
"conduit_database",
|
||||
"conduit_service",
|
||||
"const-str",
|
||||
"futures-util",
|
||||
"hmac",
|
||||
"http 1.1.0",
|
||||
"hyper 1.4.0",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"image",
|
||||
"ipaddress",
|
||||
"jsonwebtoken",
|
||||
@@ -645,7 +654,6 @@ dependencies = [
|
||||
"serde_html_form",
|
||||
"serde_json",
|
||||
"sha-1",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"webpage",
|
||||
@@ -653,12 +661,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduit_core"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum 0.7.5",
|
||||
"bytes",
|
||||
"checked_ops",
|
||||
"chrono",
|
||||
"const-str",
|
||||
"either",
|
||||
"figment",
|
||||
"hardened_malloc-rs",
|
||||
@@ -684,6 +694,7 @@ dependencies = [
|
||||
"tikv-jemalloc-sys",
|
||||
"tikv-jemallocator",
|
||||
"tokio",
|
||||
"tokio-metrics",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
@@ -692,11 +703,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduit_database"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
dependencies = [
|
||||
"conduit_core",
|
||||
"const-str",
|
||||
"log",
|
||||
"ruma",
|
||||
"rust-rocksdb-uwu",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -704,25 +715,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduit_router"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
dependencies = [
|
||||
"axum 0.7.5",
|
||||
"axum-client-ip",
|
||||
"axum-server",
|
||||
"axum-server-dual-protocol",
|
||||
"bytes",
|
||||
"clap",
|
||||
"conduit_admin",
|
||||
"conduit_api",
|
||||
"conduit_core",
|
||||
"conduit_database",
|
||||
"conduit_service",
|
||||
"const-str",
|
||||
"http 1.1.0",
|
||||
"http-body-util",
|
||||
"hyper 1.4.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"log",
|
||||
"regex",
|
||||
"ruma",
|
||||
"sd-notify",
|
||||
"sentry",
|
||||
@@ -737,13 +746,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduit_service"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"conduit_core",
|
||||
"conduit_database",
|
||||
"const-str",
|
||||
"cyborgtime",
|
||||
"futures-util",
|
||||
"hickory-resolver",
|
||||
@@ -815,6 +825,12 @@ version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "const-str"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3618cccc083bb987a415d85c02ca6c9994ea5b44731ec28b9ecf09658655fba9"
|
||||
|
||||
[[package]]
|
||||
name = "const_panic"
|
||||
version = "0.2.8"
|
||||
@@ -1006,7 +1022,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1110,7 +1126,7 @@ dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1246,7 +1262,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1527,7 +1543,7 @@ dependencies = [
|
||||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1574,9 +1590,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 1.1.0",
|
||||
@@ -1591,7 +1607,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body 1.0.1",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@@ -1615,9 +1631,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.29"
|
||||
version = "0.14.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33"
|
||||
checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -1639,16 +1655,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc"
|
||||
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"h2 0.4.5",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body 1.0.1",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
@@ -1666,9 +1682,9 @@ checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"hyper 1.4.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"rustls 0.23.10",
|
||||
"rustls 0.23.11",
|
||||
"rustls-native-certs",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
@@ -1683,7 +1699,7 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
|
||||
dependencies = [
|
||||
"hyper 0.14.29",
|
||||
"hyper 0.14.30",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-io-timeout",
|
||||
@@ -1699,8 +1715,8 @@ dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"hyper 1.4.0",
|
||||
"http-body 1.0.1",
|
||||
"hyper 1.4.1",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
@@ -1936,7 +1952,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1964,7 +1980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2405,7 +2421,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2445,7 +2461,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2538,7 +2554,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2626,7 +2642,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
@@ -2651,7 +2667,7 @@ dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2698,7 +2714,7 @@ dependencies = [
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls 0.23.10",
|
||||
"rustls 0.23.11",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -2714,7 +2730,7 @@ dependencies = [
|
||||
"rand",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls 0.23.10",
|
||||
"rustls 0.23.11",
|
||||
"slab",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
@@ -2841,9 +2857,9 @@ dependencies = [
|
||||
"h2 0.4.5",
|
||||
"hickory-resolver",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.4.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
@@ -2854,7 +2870,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.10",
|
||||
"rustls 0.23.11",
|
||||
"rustls-native-certs",
|
||||
"rustls-pemfile",
|
||||
"rustls-pki-types",
|
||||
@@ -3060,7 +3076,7 @@ dependencies = [
|
||||
"quote",
|
||||
"ruma-identifiers-validation 0.9.5 (git+https://github.com/girlbossceo/ruwuma?rev=c51ccb2c68d2e3557eb12b1a49036531711ec0e5)",
|
||||
"serde",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
"toml",
|
||||
]
|
||||
|
||||
@@ -3122,8 +3138,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-librocksdb-sys"
|
||||
version = "0.23.0+9.3.1"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=b4887edfb84771336930855727390edec07d63fa#b4887edfb84771336930855727390edec07d63fa"
|
||||
version = "0.23.1+9.3.1"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=db1ba33c2b78ad228e2525e8902d059c24fc81a1#db1ba33c2b78ad228e2525e8902d059c24fc81a1"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"bzip2-sys",
|
||||
@@ -3139,8 +3155,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-rocksdb"
|
||||
version = "0.27.0"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=b4887edfb84771336930855727390edec07d63fa#b4887edfb84771336930855727390edec07d63fa"
|
||||
version = "0.27.1"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=db1ba33c2b78ad228e2525e8902d059c24fc81a1#db1ba33c2b78ad228e2525e8902d059c24fc81a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rust-librocksdb-sys",
|
||||
@@ -3196,30 +3212,31 @@ dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.4",
|
||||
"rustls-webpki 0.102.5",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.10"
|
||||
version = "0.23.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
|
||||
checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.4",
|
||||
"rustls-webpki 0.102.5",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792"
|
||||
checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile",
|
||||
@@ -3256,9 +3273,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.4"
|
||||
version = "0.102.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
|
||||
checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -3274,8 +3291,7 @@ checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
[[package]]
|
||||
name = "rustyline-async"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6eb06391513b2184f0a5405c11a4a0a5302e8be442f4c5c35267187c2b37d5"
|
||||
source = "git+https://github.com/girlbossceo/rustyline-async?rev=de26100b0db03e419a3d8e1dd26895d170d1fe50#de26100b0db03e419a3d8e1dd26895d170d1fe50"
|
||||
dependencies = [
|
||||
"crossterm",
|
||||
"futures-channel",
|
||||
@@ -3500,22 +3516,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.203"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.203"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3808,9 +3824,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.68"
|
||||
version = "2.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3868,22 +3884,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
version = "1.0.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
version = "1.0.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3982,9 +3998,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
@@ -4032,7 +4048,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4043,6 +4059,7 @@ checksum = "eace09241d62c98b7eeb1107d4c5c64ca3bd7da92e8c218c153ab3a78f9be112"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
]
|
||||
|
||||
@@ -4062,7 +4079,7 @@ version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||
dependencies = [
|
||||
"rustls 0.23.10",
|
||||
"rustls 0.23.11",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
@@ -4112,7 +4129,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.22.14",
|
||||
"toml_edit 0.22.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4137,9 +4154,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.14"
|
||||
version = "0.22.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
||||
checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1"
|
||||
dependencies = [
|
||||
"indexmap 2.2.6",
|
||||
"serde",
|
||||
@@ -4162,7 +4179,7 @@ dependencies = [
|
||||
"h2 0.3.26",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.29",
|
||||
"hyper 0.14.30",
|
||||
"hyper-timeout",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
@@ -4207,7 +4224,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
@@ -4248,7 +4265,7 @@ source = "git+https://github.com/girlbossceo/tracing?rev=b348dca742af641c47bc390
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4417,16 +4434,15 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.9.7"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd"
|
||||
checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls 0.22.4",
|
||||
"rustls 0.23.11",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.4",
|
||||
"url",
|
||||
"webpki-roots",
|
||||
]
|
||||
@@ -4457,9 +4473,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.9.1"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
|
||||
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"serde",
|
||||
@@ -4519,7 +4535,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4553,7 +4569,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.71",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -4662,7 +4678,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4671,7 +4687,7 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4689,7 +4705,7 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4709,18 +4725,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.5",
|
||||
"windows_aarch64_msvc 0.52.5",
|
||||
"windows_i686_gnu 0.52.5",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.5",
|
||||
"windows_x86_64_gnu 0.52.5",
|
||||
"windows_x86_64_gnullvm 0.52.5",
|
||||
"windows_x86_64_msvc 0.52.5",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4731,9 +4747,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@@ -4743,9 +4759,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@@ -4755,15 +4771,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@@ -4773,9 +4789,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@@ -4785,9 +4801,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@@ -4797,9 +4813,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@@ -4809,9 +4825,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
@@ -4876,27 +4892,27 @@ checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.1"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
|
||||
checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.1.0"
|
||||
version = "7.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
|
||||
checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.11+zstd.1.5.6"
|
||||
version = "2.0.12+zstd.1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4"
|
||||
checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
|
||||
+27
-27
@@ -20,11 +20,14 @@ license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/girlbossceo/conduwuit"
|
||||
rust-version = "1.77.0"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "conduit"
|
||||
|
||||
[workspace.dependencies.const-str]
|
||||
version = "0.5.7"
|
||||
|
||||
[workspace.dependencies.sanitize-filename]
|
||||
version = "0.5.0"
|
||||
|
||||
@@ -50,7 +53,7 @@ version = "0.8.5"
|
||||
|
||||
# Used for the http request / response body type for Ruma endpoints used with reqwest
|
||||
[workspace.dependencies.bytes]
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
|
||||
[workspace.dependencies.http-body-util]
|
||||
version = "0.1.1"
|
||||
@@ -113,7 +116,7 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.serde]
|
||||
version = "1.0.203"
|
||||
version = "1.0.204"
|
||||
features = ["rc"]
|
||||
|
||||
[workspace.dependencies.serde_json]
|
||||
@@ -169,7 +172,7 @@ default-features = false
|
||||
|
||||
# used for conduit's CLI and admin room command parsing
|
||||
[workspace.dependencies.clap]
|
||||
version = "4.5.4"
|
||||
version = "4.5.9"
|
||||
default-features = false
|
||||
features = [
|
||||
"std",
|
||||
@@ -197,6 +200,9 @@ features = [
|
||||
"io-util",
|
||||
]
|
||||
|
||||
[workspace.dependencies.tokio-metrics]
|
||||
version = "0.3.1"
|
||||
|
||||
[workspace.dependencies.libloading]
|
||||
version = "0.8.3"
|
||||
|
||||
@@ -245,7 +251,7 @@ default-features = false
|
||||
|
||||
# Used for conduit::Error type
|
||||
[workspace.dependencies.thiserror]
|
||||
version = "1.0.61"
|
||||
version = "1.0.62"
|
||||
|
||||
# Used when hashing the state
|
||||
[workspace.dependencies.ring]
|
||||
@@ -265,7 +271,7 @@ version = "2.1.1"
|
||||
version = "0.3.1"
|
||||
|
||||
[workspace.dependencies.async-trait]
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
|
||||
[workspace.dependencies.lru-cache]
|
||||
version = "0.1.2"
|
||||
@@ -311,7 +317,6 @@ package = "rust-rocksdb-uwu"
|
||||
features = [
|
||||
"multi-threaded-cf",
|
||||
"mt_static",
|
||||
"snappy",
|
||||
"lz4",
|
||||
"zstd",
|
||||
"zlib",
|
||||
@@ -328,7 +333,7 @@ version = "0.10.8"
|
||||
|
||||
# optional opentelemetry, performance measurements, flamegraphs, etc for performance measurements and monitoring
|
||||
[workspace.dependencies.opentelemetry]
|
||||
version = "0.21.0"
|
||||
version = "0.24.0"
|
||||
|
||||
[workspace.dependencies.tracing-flame]
|
||||
version = "0.2.0"
|
||||
@@ -337,11 +342,11 @@ version = "0.2.0"
|
||||
version = "0.22.0"
|
||||
|
||||
[workspace.dependencies.opentelemetry_sdk]
|
||||
version = "0.21.2"
|
||||
version = "0.24.0"
|
||||
features = ["rt-tokio"]
|
||||
|
||||
[workspace.dependencies.opentelemetry-jaeger]
|
||||
version = "0.20.0"
|
||||
version = "0.22.0"
|
||||
features = ["rt-tokio"]
|
||||
|
||||
# optional sentry metrics for crash/panic reporting
|
||||
@@ -380,10 +385,6 @@ version = "0.5.4"
|
||||
default-features = false
|
||||
features = ["use_std"]
|
||||
|
||||
[workspace.dependencies.tokio-metrics]
|
||||
version = "0.3.1"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.console-subscriber]
|
||||
version = "0.3"
|
||||
|
||||
@@ -404,12 +405,16 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.rustyline-async]
|
||||
version = "0.4.2"
|
||||
git = "https://github.com/girlbossceo/rustyline-async"
|
||||
rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
|
||||
|
||||
[workspace.dependencies.termimad]
|
||||
version = "0.29.4"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.checked_ops]
|
||||
version = "0.1"
|
||||
|
||||
|
||||
#
|
||||
# Patches
|
||||
@@ -726,7 +731,6 @@ nursery = "warn"
|
||||
|
||||
## some sadness
|
||||
missing_const_for_fn = { level = "allow", priority = 1 } # TODO
|
||||
needless_collect = { level = "allow", priority = 1 } # TODO
|
||||
option_if_let_else = { level = "allow", priority = 1 } # TODO
|
||||
redundant_pub_crate = { level = "allow", priority = 1 } # TODO
|
||||
significant_drop_in_scrutinee = { level = "allow", priority = 1 } # TODO
|
||||
@@ -736,21 +740,14 @@ significant_drop_tightening = { level = "allow", priority = 1 } # TODO
|
||||
pedantic = "warn"
|
||||
|
||||
## some sadness
|
||||
cast_possible_truncation = { level = "allow", priority = 1 }
|
||||
cast_precision_loss = { level = "allow", priority = 1 }
|
||||
cast_sign_loss = { level = "allow", priority = 1 }
|
||||
doc_markdown = { level = "allow", priority = 1 }
|
||||
error_impl_error = { level = "allow", priority = 1 }
|
||||
expect_used = { level = "allow", priority = 1 }
|
||||
enum_glob_use = { level = "allow", priority = 1 }
|
||||
if_not_else = { level = "allow", priority = 1 }
|
||||
if_then_some_else_none = { level = "allow", priority = 1 }
|
||||
implicit_return = { level = "allow", priority = 1 }
|
||||
inline_always = { level = "allow", priority = 1 }
|
||||
map_err_ignore = { level = "allow", priority = 1 }
|
||||
missing_docs_in_private_items = { level = "allow", priority = 1 }
|
||||
missing_errors_doc = { level = "allow", priority = 1 }
|
||||
missing_panics_doc = { level = "allow", priority = 1 }
|
||||
mod_module_files = { level = "allow", priority = 1 }
|
||||
module_name_repetitions = { level = "allow", priority = 1 }
|
||||
no_effect_underscore_binding = { level = "allow", priority = 1 }
|
||||
similar_names = { level = "allow", priority = 1 }
|
||||
@@ -764,8 +761,10 @@ perf = "warn"
|
||||
###################
|
||||
#restriction = "warn"
|
||||
|
||||
#arithmetic_side_effects = "warn" # TODO
|
||||
#as_conversions = "warn" # TODO
|
||||
allow_attributes = "warn"
|
||||
arithmetic_side_effects = "warn"
|
||||
as_conversions = "warn"
|
||||
as_underscore = "warn"
|
||||
assertions_on_result_states = "warn"
|
||||
dbg_macro = "warn"
|
||||
default_union_representation = "warn"
|
||||
@@ -779,7 +778,6 @@ fn_to_numeric_cast_any = "warn"
|
||||
format_push_string = "warn"
|
||||
get_unwrap = "warn"
|
||||
impl_trait_in_params = "warn"
|
||||
let_underscore_must_use = "warn"
|
||||
let_underscore_untyped = "warn"
|
||||
lossy_float_literal = "warn"
|
||||
mem_forget = "warn"
|
||||
@@ -793,6 +791,7 @@ rest_pat_in_fully_bound_structs = "warn"
|
||||
semicolon_outside_block = "warn"
|
||||
str_to_string = "warn"
|
||||
string_lit_chars_any = "warn"
|
||||
string_slice = "warn"
|
||||
string_to_string = "warn"
|
||||
suspicious_xor_used_as_pow = "warn"
|
||||
tests_outside_test_module = "warn"
|
||||
@@ -803,6 +802,7 @@ unnecessary_safety_doc = "warn"
|
||||
unnecessary_self_imports = "warn"
|
||||
unneeded_field_pattern = "warn"
|
||||
unseparated_literal_suffix = "warn"
|
||||
#unwrap_used = "warn" # TODO
|
||||
verbose_file_reads = "warn"
|
||||
|
||||
###################
|
||||
|
||||
+12
-5
@@ -7,7 +7,7 @@ set -euo pipefail
|
||||
# The `COMPLEMENT_SRC` environment variable is set in the Nix dev shell, which
|
||||
# points to a store path containing the Complement source code. It's likely you
|
||||
# want to just pass that as the first argument to use it here.
|
||||
COMPLEMENT_SRC="$1"
|
||||
COMPLEMENT_SRC="${COMPLEMENT_SRC:-$1}"
|
||||
|
||||
# A `.jsonl` file to write test logs to
|
||||
LOG_FILE="$2"
|
||||
@@ -17,12 +17,19 @@ RESULTS_FILE="$3"
|
||||
|
||||
OCI_IMAGE="complement-conduit:main"
|
||||
|
||||
# Complement tests that are skipped due to flakiness/reliability issues (likely
|
||||
# Complement itself induced based on various open issues)
|
||||
#
|
||||
# According to Go docs, these are separated by forward slashes and not pipes (why)
|
||||
# Complement tests that are skipped due to flakiness/reliability issues
|
||||
SKIPPED_COMPLEMENT_TESTS='-skip=TestClientSpacesSummary.*|TestJoinFederatedRoomFromApplicationServiceBridgeUser.*|TestJumpToDateEndpoint.*'
|
||||
|
||||
# $COMPLEMENT_SRC needs to be a directory to Complement source code
|
||||
if [ -f "$COMPLEMENT_SRC" ]; then
|
||||
echo "\$COMPLEMENT_SRC must be a directory/path to Complement source code"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# quick test to make sure we can actually write to $LOG_FILE and $RESULTS_FILE
|
||||
touch $LOG_FILE && rm -v $LOG_FILE
|
||||
touch $RESULTS_FILE && rm -v $RESULTS_FILE
|
||||
|
||||
toplevel="$(git rev-parse --show-toplevel)"
|
||||
|
||||
pushd "$toplevel" > /dev/null
|
||||
|
||||
@@ -57,6 +57,16 @@
|
||||
# Defaults to 0.15
|
||||
#sentry_traces_sample_rate = 0.15
|
||||
|
||||
# Whether to attach a stacktrace to Sentry reports.
|
||||
#sentry_attach_stacktrace = false
|
||||
|
||||
# Send panics to sentry. This is true by default, but sentry has to be enabled.
|
||||
#sentry_send_panic = true
|
||||
|
||||
# Send errors to sentry. This is true by default, but sentry has to be enabled. This option is
|
||||
# only effective in release-mode; forced to false in debug-mode.
|
||||
#sentry_send_error = true
|
||||
|
||||
|
||||
### Database configuration
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -10,7 +10,7 @@ repository.workspace = true
|
||||
version = "0.0.1"
|
||||
|
||||
[features]
|
||||
default = ["snappy", "lz4", "zstd", "zlib", "bzip2"]
|
||||
default = ["lz4", "zstd", "zlib", "bzip2"]
|
||||
jemalloc = ["rust-rocksdb/jemalloc"]
|
||||
io-uring = ["rust-rocksdb/io-uring"]
|
||||
valgrind = ["rust-rocksdb/valgrind"]
|
||||
@@ -27,7 +27,7 @@ malloc-usable-size = ["rust-rocksdb/malloc-usable-size"]
|
||||
|
||||
[dependencies.rust-rocksdb]
|
||||
git = "https://github.com/zaidoon1/rust-rocksdb"
|
||||
rev = "b4887edfb84771336930855727390edec07d63fa"
|
||||
rev = "db1ba33c2b78ad228e2525e8902d059c24fc81a1"
|
||||
#branch = "master"
|
||||
default-features = false
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# conduwuit - Behind Traefik Reverse Proxy
|
||||
version: '2.4' # uses '2.4' for cpuset
|
||||
|
||||
services:
|
||||
homeserver:
|
||||
@@ -24,7 +23,7 @@ services:
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONDUWUIT_LOG: warn,state_res=warn
|
||||
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||
#CONDUWUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
|
||||
|
||||
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# conduwuit - Traefik Reverse Proxy Labels
|
||||
version: '2.4' # uses '2.4' for cpuset
|
||||
|
||||
services:
|
||||
homeserver:
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
services:
|
||||
caddy:
|
||||
# This compose file uses caddy-docker-proxy as the reverse proxy for conduwuit!
|
||||
# For more info, visit https://github.com/lucaslorentz/caddy-docker-proxy
|
||||
image: lucaslorentz/caddy-docker-proxy:ci-alpine
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
environment:
|
||||
- CADDY_INGRESS_NETWORKS=caddy
|
||||
networks:
|
||||
- caddy
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./data:/data
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
caddy: example.com
|
||||
caddy.0_respond: /.well-known/matrix/server {"m.server":"matrix.example.com:443"}
|
||||
caddy.1_respond: /.well-known/matrix/client {"m.server":{"base_url":"https://matrix.example.com"},"m.homeserver":{"base_url":"https://matrix.example.com"},"org.matrix.msc3575.proxy":{"url":"https://matrix.example.com"}}
|
||||
|
||||
homeserver:
|
||||
### If you already built the conduwuit image with 'docker build' or want to use a registry image,
|
||||
### then you are ready to go.
|
||||
image: girlbossceo/conduwuit:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db:/var/lib/conduwuit
|
||||
#- ./conduwuit.toml:/etc/conduwuit.toml
|
||||
environment:
|
||||
CONDUWUIT_SERVER_NAME: example.com # EDIT THIS
|
||||
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
|
||||
CONDUWUIT_DATABASE_BACKEND: rocksdb
|
||||
CONDUWUIT_PORT: 6167
|
||||
CONDUWUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||
CONDUWUIT_ALLOW_REGISTRATION: 'true'
|
||||
CONDUWUIT_ALLOW_FEDERATION: 'true'
|
||||
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONDUWUIT_LOG: warn,state_res=warn
|
||||
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
networks:
|
||||
- caddy
|
||||
labels:
|
||||
caddy: matrix.example.com
|
||||
caddy.reverse_proxy: "{{upstreams 6167}}"
|
||||
|
||||
volumes:
|
||||
db:
|
||||
|
||||
networks:
|
||||
caddy:
|
||||
external: true
|
||||
@@ -1,5 +1,4 @@
|
||||
# conduwuit - Behind Traefik Reverse Proxy
|
||||
version: '2.4' # uses '2.4' for cpuset
|
||||
|
||||
services:
|
||||
homeserver:
|
||||
@@ -16,7 +15,7 @@ services:
|
||||
CONDUWUIT_SERVER_NAME: your.server.name # EDIT THIS
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
CONDUWUIT_ALLOW_REGISTRATION : 'true'
|
||||
#CONDUWUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
### Uncomment and change values as desired
|
||||
# CONDUWUIT_ADDRESS: 0.0.0.0
|
||||
# CONDUWUIT_PORT: 6167
|
||||
@@ -28,7 +27,6 @@ services:
|
||||
# CONDUWUIT_DATABASE_PATH: /srv/conduwuit/.local/share/conduwuit
|
||||
# CONDUWUIT_WORKERS: 10
|
||||
# CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
|
||||
|
||||
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
|
||||
# to serve those two as static files. If you want to use a different way, delete or comment the below service, here
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# conduwuit
|
||||
version: '2.4' # uses '2.4' for cpuset
|
||||
|
||||
services:
|
||||
homeserver:
|
||||
@@ -24,8 +23,7 @@ services:
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONDUWUIT_LOG: warn,state_res=warn
|
||||
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||
#CONDUWUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
|
||||
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
#
|
||||
### Uncomment if you want to use your own Element-Web App.
|
||||
### Note: You need to provide a config.json for Element and you also need a second
|
||||
|
||||
@@ -59,13 +59,22 @@ If the `docker run` command is not for you or your setup, you can also use one o
|
||||
Depending on your proxy setup, you can use one of the following files;
|
||||
|
||||
- If you already have a `traefik` instance set up, use [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml)
|
||||
- If you don't have a `traefik` instance set up (or any other reverse proxy), use [`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)
|
||||
- If you don't have a `traefik` instance set up and would like to use it, use [`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)
|
||||
- If you want a setup that works out of the box with `caddy-docker-proxy`, use [`docker-compose.with-caddy.yml`](docker-compose.with-caddy.yml) and replace all `example.com` placeholders with your own domain
|
||||
- For any other reverse proxy, use [`docker-compose.yml`](docker-compose.yml)
|
||||
|
||||
When picking the traefik-related compose file, rename it so it matches `docker-compose.yml`, and
|
||||
rename the override file to `docker-compose.override.yml`. Edit the latter with the values you want
|
||||
for your server.
|
||||
|
||||
When picking the `caddy-docker-proxy` compose file, it's important to first create the `caddy` network before spinning up the containers:
|
||||
|
||||
```bash
|
||||
docker network create caddy
|
||||
```
|
||||
|
||||
After that, you can rename it so it matches `docker-compose.yml` and spin up the containers!
|
||||
|
||||
Additional info about deploying conduwuit can be found [here](generic.md).
|
||||
|
||||
### Build
|
||||
|
||||
+2
-1
@@ -184,5 +184,6 @@ cargo test \
|
||||
name = "nix-default"
|
||||
group = "tests"
|
||||
script = """
|
||||
nix run .#default -- --help
|
||||
bin/nix-build-and-cache just .#default
|
||||
nix run -L .#default -- --help
|
||||
"""
|
||||
|
||||
Generated
+12
-12
@@ -123,11 +123,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1716569590,
|
||||
"narHash": "sha256-5eDbq8TuXFGGO3mqJFzhUbt5zHVTf5zilQoyW5jnJwo=",
|
||||
"lastModified": 1720226507,
|
||||
"narHash": "sha256-yHVvNsgrpyNTXZBEokL8uyB2J6gB1wEx0KOJzoeZi1A=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "109987da061a1bf452f435f1653c47511587d919",
|
||||
"rev": "0aed560c5c0a61c9385bddff471a13036203e11c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -209,11 +209,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1716359173,
|
||||
"narHash": "sha256-pYcjP6Gy7i6jPWrjiWAVV0BCQp+DdmGaI/k65lBb/kM=",
|
||||
"lastModified": 1720420198,
|
||||
"narHash": "sha256-OIuDb6pHDyGpo7YMFyuRzMLcHm7mRvlYOz0Ht7ps2sU=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "b6fc5035b28e36a98370d0eac44f4ef3fd323df6",
|
||||
"rev": "abc0549e3560189462a7d394cc9d50af4608d103",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -606,11 +606,11 @@
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1716330097,
|
||||
"narHash": "sha256-8BO3B7e3BiyIDsaKA0tY8O88rClYRTjvAp66y+VBUeU=",
|
||||
"lastModified": 1720418205,
|
||||
"narHash": "sha256-cPJoFPXU44GlhWg4pUk9oUPqurPlCFZ11ZQPk21GTPU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5710852ba686cc1fd0d3b8e22b3117d43ba374c2",
|
||||
"rev": "655a58a72a6601292512670343087c2d75d859c1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -705,11 +705,11 @@
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1716107283,
|
||||
"narHash": "sha256-NJgrwLiLGHDrCia5AeIvZUHUY7xYGVryee0/9D3Ir1I=",
|
||||
"lastModified": 1720344064,
|
||||
"narHash": "sha256-STmaV9Zu74QtkGGrbr9uMhskwagfCjJqOAYapXabiuk=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "21ec8f523812b88418b2bfc64240c62b3dd967bd",
|
||||
"rev": "a5b21ea0aa644dffd7cf958b43f11f221d53404e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -42,6 +42,13 @@
|
||||
"v"
|
||||
(builtins.fromJSON (builtins.readFile ./flake.lock))
|
||||
.nodes.rocksdb.original.ref;
|
||||
# we have this already at https://github.com/girlbossceo/rocksdb/commit/a935c0273e1ba44eacf88ce3685a9b9831486155
|
||||
# unsetting this so i don't have to revert it and make this nix exclusive
|
||||
patches = [];
|
||||
# no real reason to have snappy, no one uses this
|
||||
cmakeFlags = pkgs.lib.subtractLists
|
||||
[ "-DWITH_SNAPPY=1" ]
|
||||
old.cmakeFlags;
|
||||
});
|
||||
# TODO: remove once https://github.com/NixOS/nixpkgs/pull/314945 is available
|
||||
liburing = pkgs.liburing.overrideAttrs (old: {
|
||||
@@ -50,16 +57,6 @@
|
||||
configureFlags = pkgs.lib.subtractLists
|
||||
[ "--enable-static" "--disable-shared" ]
|
||||
old.configureFlags;
|
||||
|
||||
postInstall = old.postInstall + ''
|
||||
# we remove the extra outputs
|
||||
#
|
||||
# we need to do this to prevent rocksdb from trying to link the
|
||||
# static library in a dynamic stdenv
|
||||
rm $out/lib/liburing*${
|
||||
if pkgs.stdenv.hostPlatform.isStatic then ".so*" else ".a"
|
||||
}
|
||||
'';
|
||||
});
|
||||
});
|
||||
|
||||
@@ -124,9 +121,21 @@
|
||||
{
|
||||
packages = {
|
||||
default = scopeHost.main;
|
||||
all-features = scopeHost.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
|
||||
|
||||
oci-image = scopeHost.oci-image;
|
||||
oci-image-all-features = scopeHost.oci-image.override {
|
||||
main = scopeHost.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
};
|
||||
oci-image-hmalloc = scopeHost.oci-image.override {
|
||||
main = scopeHost.main.override {
|
||||
features = ["hardened_malloc"];
|
||||
@@ -161,6 +170,16 @@
|
||||
value = scopeCrossStatic.main;
|
||||
}
|
||||
|
||||
# An output for a statically-linked binary with `--all-features`
|
||||
{
|
||||
name = "${binaryName}-all-features";
|
||||
value = scopeCrossStatic.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
}
|
||||
|
||||
# An output for a statically-linked binary with hardened_malloc
|
||||
{
|
||||
name = "${binaryName}-hmalloc";
|
||||
@@ -175,6 +194,18 @@
|
||||
value = scopeCrossStatic.oci-image;
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that binary with `--all-features`
|
||||
{
|
||||
name = "oci-image-${crossSystem}-all-features";
|
||||
value = scopeCrossStatic.oci-image.override {
|
||||
main = scopeCrossStatic.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that binary with hardened_malloc
|
||||
{
|
||||
name = "oci-image-${crossSystem}-hmalloc";
|
||||
@@ -196,7 +227,11 @@
|
||||
devShells.default = mkDevShell scopeHostStatic;
|
||||
devShells.all-features = mkDevShell
|
||||
(scopeHostStatic.overrideScope (final: prev: {
|
||||
main = prev.main.override { all_features = true; };
|
||||
main = prev.main.override {
|
||||
all_features = true;
|
||||
# this is non-functional on nix for some reason
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
}));
|
||||
devShells.no-features = mkDevShell
|
||||
(scopeHostStatic.overrideScope (final: prev: {
|
||||
|
||||
@@ -5,13 +5,17 @@ allow_guest_registration = true
|
||||
allow_public_room_directory_over_federation = true
|
||||
allow_public_room_directory_without_auth = true
|
||||
allow_registration = true
|
||||
allow_unstable_room_versions = true
|
||||
database_backend = "rocksdb"
|
||||
database_path = "/database"
|
||||
log = "trace"
|
||||
log = "trace,h2=warn,hyper=warn"
|
||||
port = [8008, 8448]
|
||||
trusted_servers = []
|
||||
query_trusted_key_servers_first = false
|
||||
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
||||
ip_range_denylist = []
|
||||
url_preview_domain_contains_allowlist = ["*"]
|
||||
media_compat_file_link = false
|
||||
media_statup_check = false
|
||||
rocksdb_direct_io = false
|
||||
|
||||
[global.tls]
|
||||
certs = "/certificate.crt"
|
||||
|
||||
@@ -25,11 +25,7 @@ let
|
||||
# on the nix side depend on feature values.
|
||||
crateFeatures = path:
|
||||
let manifest = lib.importTOML "${path}/Cargo.toml"; in
|
||||
lib.remove "default" (lib.attrNames manifest.features) ++
|
||||
lib.attrNames
|
||||
(lib.filterAttrs
|
||||
(_: dependency: dependency.optional or false)
|
||||
manifest.dependencies);
|
||||
lib.remove "default" (lib.attrNames manifest.features);
|
||||
crateDefaultFeatures = path:
|
||||
(lib.importTOML "${path}/Cargo.toml").features.default;
|
||||
allDefaultFeatures = crateDefaultFeatures "${inputs.self}/src/main";
|
||||
@@ -43,7 +39,7 @@ features'' = lib.subtractLists disable_features' features';
|
||||
|
||||
featureEnabled = feature : builtins.elem feature features'';
|
||||
|
||||
enableLiburing = featureEnabled "io_uring" && stdenv.isLinux;
|
||||
enableLiburing = featureEnabled "io_uring" && !stdenv.isDarwin;
|
||||
|
||||
# This derivation will set the JEMALLOC_OVERRIDE variable, causing the
|
||||
# tikv-jemalloc-sys crate to use the nixpkgs jemalloc instead of building it's
|
||||
@@ -70,12 +66,15 @@ buildDepsOnlyEnv =
|
||||
#
|
||||
# [1]: https://github.com/tikv/jemallocator/blob/ab0676d77e81268cd09b059260c75b38dbef2d51/jemalloc-sys/src/env.rs#L17
|
||||
enableJemalloc = featureEnabled "jemalloc" && !stdenv.isDarwin;
|
||||
|
||||
# for some reason enableLiburing in nixpkgs rocksdb is default true
|
||||
# which breaks Darwin entirely
|
||||
enableLiburing = enableLiburing;
|
||||
}).overrideAttrs (old: {
|
||||
# TODO: static rocksdb fails to build on darwin
|
||||
# build log at <https://girlboss.ceo/~strawberry/pb/JjGH>
|
||||
meta.broken = stdenv.hostPlatform.isStatic && stdenv.isDarwin;
|
||||
# TODO: switch to enableUring option once https://github.com/NixOS/nixpkgs/pull/314945 is available
|
||||
buildInputs = old.buildInputs ++ lib.optional enableLiburing liburing;
|
||||
enableLiburing = enableLiburing;
|
||||
});
|
||||
in
|
||||
{
|
||||
|
||||
@@ -29,15 +29,12 @@ release_max_log_level = [
|
||||
clap.workspace = true
|
||||
conduit-api.workspace = true
|
||||
conduit-core.workspace = true
|
||||
conduit-database.workspace = true
|
||||
conduit-service.workspace = true
|
||||
const-str.workspace = true
|
||||
futures-util.workspace = true
|
||||
log.workspace = true
|
||||
loole.workspace = true
|
||||
regex.workspace = true
|
||||
ruma.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
serde_yaml.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
|
||||
@@ -58,7 +58,7 @@ pub(super) async fn parse_pdu(body: Vec<&str>) -> Result<RoomMessageEventContent
|
||||
));
|
||||
}
|
||||
|
||||
let string = body[1..body.len() - 1].join("\n");
|
||||
let string = body[1..body.len().saturating_sub(1)].join("\n");
|
||||
match serde_json::from_str(&string) {
|
||||
Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
||||
Ok(hash) => {
|
||||
@@ -314,6 +314,8 @@ pub(super) async fn force_device_list_updates(_body: Vec<&str>) -> Result<RoomMe
|
||||
pub(super) async fn change_log_level(
|
||||
_body: Vec<&str>, filter: Option<String>, reset: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let handles = &["console"];
|
||||
|
||||
if reset {
|
||||
let old_filter_layer = match EnvFilter::try_new(&services().globals.config.log) {
|
||||
Ok(s) => s,
|
||||
@@ -324,7 +326,12 @@ pub(super) async fn change_log_level(
|
||||
},
|
||||
};
|
||||
|
||||
match services().server.log.reload.reload(&old_filter_layer) {
|
||||
match services()
|
||||
.server
|
||||
.log
|
||||
.reload
|
||||
.reload(&old_filter_layer, Some(handles))
|
||||
{
|
||||
Ok(()) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Successfully changed log level back to config value {}",
|
||||
@@ -349,7 +356,12 @@ pub(super) async fn change_log_level(
|
||||
},
|
||||
};
|
||||
|
||||
match services().server.log.reload.reload(&new_filter_layer) {
|
||||
match services()
|
||||
.server
|
||||
.log
|
||||
.reload
|
||||
.reload(&new_filter_layer, Some(handles))
|
||||
{
|
||||
Ok(()) => {
|
||||
return Ok(RoomMessageEventContent::text_plain("Successfully changed log level"));
|
||||
},
|
||||
@@ -570,7 +582,7 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
.state_compressor
|
||||
.save_state(room_id.clone().as_ref(), new_room_state)?;
|
||||
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(&room_id).await;
|
||||
let state_lock = services().rooms.state.mutex.lock(&room_id).await;
|
||||
services()
|
||||
.rooms
|
||||
.state
|
||||
@@ -632,12 +644,46 @@ pub(super) async fn resolve_true_destination(
|
||||
pub(super) fn memory_stats() -> RoomMessageEventContent {
|
||||
let html_body = conduit::alloc::memory_stats();
|
||||
|
||||
if html_body.is_empty() {
|
||||
if html_body.is_none() {
|
||||
return RoomMessageEventContent::text_plain("malloc stats are not supported on your compiled malloc.");
|
||||
}
|
||||
|
||||
RoomMessageEventContent::text_html(
|
||||
"This command's output can only be viewed by clients that render HTML.".to_owned(),
|
||||
html_body,
|
||||
html_body.expect("string result"),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
pub(super) async fn runtime_metrics(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let out = services().server.metrics.runtime_metrics().map_or_else(
|
||||
|| "Runtime metrics are not available.".to_owned(),
|
||||
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
||||
);
|
||||
|
||||
Ok(RoomMessageEventContent::text_markdown(out))
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_unstable))]
|
||||
pub(super) async fn runtime_metrics(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(RoomMessageEventContent::text_markdown(
|
||||
"Runtime metrics require building with `tokio_unstable`.",
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
pub(super) async fn runtime_interval(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let out = services().server.metrics.runtime_interval().map_or_else(
|
||||
|| "Runtime metrics are not available.".to_owned(),
|
||||
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
||||
);
|
||||
|
||||
Ok(RoomMessageEventContent::text_markdown(out))
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_unstable))]
|
||||
pub(super) async fn runtime_interval(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(RoomMessageEventContent::text_markdown(
|
||||
"Runtime metrics require building with `tokio_unstable`.",
|
||||
))
|
||||
}
|
||||
|
||||
@@ -160,6 +160,13 @@ pub(super) enum DebugCommand {
|
||||
/// - Print extended memory usage
|
||||
MemoryStats,
|
||||
|
||||
/// - Print general tokio runtime metric totals.
|
||||
RuntimeMetrics,
|
||||
|
||||
/// - Print detailed tokio runtime metrics accumulated since last command
|
||||
/// invocation.
|
||||
RuntimeInterval,
|
||||
|
||||
/// - Developer test stubs
|
||||
#[command(subcommand)]
|
||||
Tester(TesterCommand),
|
||||
@@ -213,6 +220,8 @@ pub(super) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<Ro
|
||||
no_cache,
|
||||
} => resolve_true_destination(body, server_name, no_cache).await?,
|
||||
DebugCommand::MemoryStats => memory_stats(),
|
||||
DebugCommand::RuntimeMetrics => runtime_metrics(body).await?,
|
||||
DebugCommand::RuntimeInterval => runtime_interval(body).await?,
|
||||
DebugCommand::Tester(command) => tester::process(command, body).await?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@ pub(super) async fn enable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Resul
|
||||
|
||||
pub(super) async fn incoming_federation(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let map = services()
|
||||
.globals
|
||||
.roomid_federationhandletime
|
||||
.rooms
|
||||
.event_handler
|
||||
.federation_handletime
|
||||
.read()
|
||||
.expect("locked");
|
||||
let mut msg = format!("Handling {} incoming pdus:\n", map.len());
|
||||
|
||||
+134
-64
@@ -1,15 +1,19 @@
|
||||
use std::time::Instant;
|
||||
use std::{panic::AssertUnwindSafe, time::Instant};
|
||||
|
||||
use clap::Parser;
|
||||
use conduit::trace;
|
||||
use ruma::events::{
|
||||
relation::InReplyTo,
|
||||
room::message::{Relation::Reply, RoomMessageEventContent},
|
||||
use clap::{CommandFactory, Parser};
|
||||
use conduit::{error, trace, Error};
|
||||
use futures_util::future::FutureExt;
|
||||
use ruma::{
|
||||
events::{
|
||||
relation::InReplyTo,
|
||||
room::message::{Relation::Reply, RoomMessageEventContent},
|
||||
},
|
||||
OwnedEventId,
|
||||
};
|
||||
|
||||
extern crate conduit_service as service;
|
||||
|
||||
use conduit::Result;
|
||||
use conduit::{utils::string::common_prefix, Result};
|
||||
pub(crate) use service::admin::{Command, Service};
|
||||
use service::admin::{CommandOutput, CommandResult, HandlerResult};
|
||||
|
||||
@@ -20,7 +24,6 @@ use crate::{
|
||||
};
|
||||
pub(crate) const PAGE_SIZE: usize = 100;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Parser)]
|
||||
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
|
||||
pub(crate) enum AdminCommand {
|
||||
@@ -62,25 +65,46 @@ pub(crate) enum AdminCommand {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn handle(command: Command) -> HandlerResult { Box::pin(handle_command(command)) }
|
||||
pub(crate) fn handle(command: Command) -> HandlerResult { Box::pin(handle_command(command)) }
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn complete(line: &str) -> String { complete_admin_command(AdminCommand::command(), line) }
|
||||
|
||||
#[tracing::instrument(skip_all, name = "admin")]
|
||||
async fn handle_command(command: Command) -> CommandResult {
|
||||
let Some(mut content) = process_admin_message(command.command).await else {
|
||||
return Ok(None);
|
||||
};
|
||||
AssertUnwindSafe(process_command(&command))
|
||||
.catch_unwind()
|
||||
.await
|
||||
.map_err(Error::from_panic)
|
||||
.or_else(|error| handle_panic(&error, command))
|
||||
}
|
||||
|
||||
content.relates_to = command.reply_id.map(|event_id| Reply {
|
||||
async fn process_command(command: &Command) -> CommandOutput {
|
||||
process_admin_message(&command.command)
|
||||
.await
|
||||
.and_then(|content| reply(content, command.reply_id.clone()))
|
||||
}
|
||||
|
||||
fn handle_panic(error: &Error, command: Command) -> CommandResult {
|
||||
let link = "Please submit a [bug report](https://github.com/girlbossceo/conduwuit/issues/new). 🥺";
|
||||
let msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
|
||||
let content = RoomMessageEventContent::notice_markdown(msg);
|
||||
error!("Panic while processing command: {error:?}");
|
||||
Ok(reply(content, command.reply_id))
|
||||
}
|
||||
|
||||
fn reply(mut content: RoomMessageEventContent, reply_id: Option<OwnedEventId>) -> Option<RoomMessageEventContent> {
|
||||
content.relates_to = reply_id.map(|event_id| Reply {
|
||||
in_reply_to: InReplyTo {
|
||||
event_id,
|
||||
},
|
||||
});
|
||||
|
||||
Ok(Some(content))
|
||||
Some(content)
|
||||
}
|
||||
|
||||
// Parse and process a message from the admin room
|
||||
async fn process_admin_message(msg: String) -> CommandOutput {
|
||||
async fn process_admin_message(msg: &str) -> CommandOutput {
|
||||
let mut lines = msg.lines().filter(|l| !l.trim().is_empty());
|
||||
let command = lines.next().expect("each string has at least one line");
|
||||
let body = lines.collect::<Vec<_>>();
|
||||
@@ -100,59 +124,11 @@ async fn process_admin_message(msg: String) -> CommandOutput {
|
||||
match result {
|
||||
Ok(reply) => Some(reply),
|
||||
Err(error) => Some(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Encountered an error while handling the command:\n```\n{error}\n```"
|
||||
"Encountered an error while handling the command:\n```\n{error:#?}\n```"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
// Parse chat messages from the admin room into an AdminCommand object
|
||||
fn parse_admin_command(command_line: &str) -> Result<AdminCommand, String> {
|
||||
let mut argv = command_line.split_whitespace().collect::<Vec<_>>();
|
||||
|
||||
// Remove any escapes that came with a server-side escape command
|
||||
if !argv.is_empty() && argv[0].ends_with("admin") {
|
||||
argv[0] = argv[0].trim_start_matches('\\');
|
||||
}
|
||||
|
||||
// First indice has to be "admin" but for console convenience we add it here
|
||||
let server_user = services().globals.server_user.as_str();
|
||||
if !argv.is_empty() && !argv[0].ends_with("admin") && !argv[0].starts_with(server_user) {
|
||||
argv.insert(0, "admin");
|
||||
}
|
||||
|
||||
// Replace `help command` with `command --help`
|
||||
// Clap has a help subcommand, but it omits the long help description.
|
||||
if argv.len() > 1 && argv[1] == "help" {
|
||||
argv.remove(1);
|
||||
argv.push("--help");
|
||||
}
|
||||
|
||||
// Backwards compatibility with `register_appservice`-style commands
|
||||
let command_with_dashes_argv1;
|
||||
if argv.len() > 1 && argv[1].contains('_') {
|
||||
command_with_dashes_argv1 = argv[1].replace('_', "-");
|
||||
argv[1] = &command_with_dashes_argv1;
|
||||
}
|
||||
|
||||
// Backwards compatibility with `register_appservice`-style commands
|
||||
let command_with_dashes_argv2;
|
||||
if argv.len() > 2 && argv[2].contains('_') {
|
||||
command_with_dashes_argv2 = argv[2].replace('_', "-");
|
||||
argv[2] = &command_with_dashes_argv2;
|
||||
}
|
||||
|
||||
// if the user is using the `query` command (argv[1]), replace the database
|
||||
// function/table calls with underscores to match the codebase
|
||||
let command_with_dashes_argv3;
|
||||
if argv.len() > 3 && argv[1].eq("query") {
|
||||
command_with_dashes_argv3 = argv[3].replace('_', "-");
|
||||
argv[3] = &command_with_dashes_argv3;
|
||||
}
|
||||
|
||||
trace!(?command_line, ?argv, "parse");
|
||||
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, name = "command")]
|
||||
async fn process_admin_command(command: AdminCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let reply_message_content = match command {
|
||||
@@ -169,3 +145,97 @@ async fn process_admin_command(command: AdminCommand, body: Vec<&str>) -> Result
|
||||
|
||||
Ok(reply_message_content)
|
||||
}
|
||||
|
||||
// Parse chat messages from the admin room into an AdminCommand object
|
||||
fn parse_admin_command(command_line: &str) -> Result<AdminCommand, String> {
|
||||
let argv = parse_command_line(command_line);
|
||||
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
fn complete_admin_command(mut cmd: clap::Command, line: &str) -> String {
|
||||
let argv = parse_command_line(line);
|
||||
let mut ret = Vec::<String>::with_capacity(argv.len().saturating_add(1));
|
||||
|
||||
'token: for token in argv.into_iter().skip(1) {
|
||||
let cmd_ = cmd.clone();
|
||||
let mut choice = Vec::new();
|
||||
|
||||
for sub in cmd_.get_subcommands() {
|
||||
let name = sub.get_name();
|
||||
if *name == token {
|
||||
// token already complete; recurse to subcommand
|
||||
ret.push(token);
|
||||
cmd.clone_from(sub);
|
||||
continue 'token;
|
||||
} else if name.starts_with(&token) {
|
||||
// partial match; add to choices
|
||||
choice.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
if choice.len() == 1 {
|
||||
// One choice. Add extra space because it's complete
|
||||
let choice = *choice.first().expect("only choice");
|
||||
ret.push(choice.to_owned());
|
||||
ret.push(String::new());
|
||||
} else if choice.is_empty() {
|
||||
// Nothing found, return original string
|
||||
ret.push(token);
|
||||
} else {
|
||||
// Find the common prefix
|
||||
ret.push(common_prefix(&choice).into());
|
||||
}
|
||||
|
||||
// Return from completion
|
||||
return ret.join(" ");
|
||||
}
|
||||
|
||||
// Return from no completion. Needs a space though.
|
||||
ret.push(String::new());
|
||||
ret.join(" ")
|
||||
}
|
||||
|
||||
// Parse chat messages from the admin room into an AdminCommand object
|
||||
fn parse_command_line(command_line: &str) -> Vec<String> {
|
||||
let mut argv = command_line
|
||||
.split_whitespace()
|
||||
.map(str::to_owned)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
// Remove any escapes that came with a server-side escape command
|
||||
if !argv.is_empty() && argv[0].ends_with("admin") {
|
||||
argv[0] = argv[0].trim_start_matches('\\').into();
|
||||
}
|
||||
|
||||
// First indice has to be "admin" but for console convenience we add it here
|
||||
let server_user = services().globals.server_user.as_str();
|
||||
if !argv.is_empty() && !argv[0].ends_with("admin") && !argv[0].starts_with(server_user) {
|
||||
argv.insert(0, "admin".to_owned());
|
||||
}
|
||||
|
||||
// Replace `help command` with `command --help`
|
||||
// Clap has a help subcommand, but it omits the long help description.
|
||||
if argv.len() > 1 && argv[1] == "help" {
|
||||
argv.remove(1);
|
||||
argv.push("--help".to_owned());
|
||||
}
|
||||
|
||||
// Backwards compatibility with `register_appservice`-style commands
|
||||
if argv.len() > 1 && argv[1].contains('_') {
|
||||
argv[1] = argv[1].replace('_', "-");
|
||||
}
|
||||
|
||||
// Backwards compatibility with `register_appservice`-style commands
|
||||
if argv.len() > 2 && argv[2].contains('_') {
|
||||
argv[2] = argv[2].replace('_', "-");
|
||||
}
|
||||
|
||||
// if the user is using the `query` command (argv[1]), replace the database
|
||||
// function/table calls with underscores to match the codebase
|
||||
if argv.len() > 3 && argv[1].eq("query") {
|
||||
argv[3] = argv[3].replace('_', "-");
|
||||
}
|
||||
|
||||
trace!(?command_line, ?argv, "parse");
|
||||
argv
|
||||
}
|
||||
|
||||
+26
-26
@@ -9,6 +9,7 @@ pub(crate) mod media;
|
||||
pub(crate) mod query;
|
||||
pub(crate) mod room;
|
||||
pub(crate) mod server;
|
||||
mod tests;
|
||||
pub(crate) mod user;
|
||||
pub(crate) mod utils;
|
||||
|
||||
@@ -17,7 +18,6 @@ extern crate conduit_core as conduit;
|
||||
extern crate conduit_service as service;
|
||||
|
||||
pub(crate) use conduit::{mod_ctor, mod_dtor, Result};
|
||||
pub use handler::handle;
|
||||
pub(crate) use service::{services, user_is_local};
|
||||
|
||||
pub(crate) use crate::{
|
||||
@@ -28,29 +28,29 @@ pub(crate) use crate::{
|
||||
mod_ctor! {}
|
||||
mod_dtor! {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use clap::Parser;
|
||||
|
||||
use crate::handler::AdminCommand;
|
||||
|
||||
#[test]
|
||||
fn get_help_short() { get_help_inner("-h"); }
|
||||
|
||||
#[test]
|
||||
fn get_help_long() { get_help_inner("--help"); }
|
||||
|
||||
#[test]
|
||||
fn get_help_subcommand() { get_help_inner("help"); }
|
||||
|
||||
fn get_help_inner(input: &str) {
|
||||
let error = AdminCommand::try_parse_from(["argv[0] doesn't matter", input])
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
|
||||
// Search for a handful of keywords that suggest the help printed properly
|
||||
assert!(error.contains("Usage:"));
|
||||
assert!(error.contains("Commands:"));
|
||||
assert!(error.contains("Options:"));
|
||||
}
|
||||
/// Install the admin command handler
|
||||
pub async fn init() {
|
||||
_ = services()
|
||||
.admin
|
||||
.complete
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
.insert(handler::complete);
|
||||
_ = services()
|
||||
.admin
|
||||
.handle
|
||||
.write()
|
||||
.await
|
||||
.insert(handler::handle);
|
||||
}
|
||||
|
||||
/// Uninstall the admin command handler
|
||||
pub async fn fini() {
|
||||
_ = services().admin.handle.write().await.take();
|
||||
_ = services()
|
||||
.admin
|
||||
.complete
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
.take();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ pub(super) async fn globals(subcommand: Globals) -> Result<RoomMessageEventConte
|
||||
},
|
||||
Globals::LastCheckForUpdatesId => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().globals.db.last_check_for_updates_id();
|
||||
let results = services().updates.last_check_for_updates_id();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
|
||||
+11
-1
@@ -16,6 +16,14 @@ pub(super) enum RoomCommand {
|
||||
/// - List all rooms the server knows about
|
||||
List {
|
||||
page: Option<usize>,
|
||||
|
||||
/// Excludes rooms that we have federation disabled with
|
||||
#[arg(long)]
|
||||
exclude_disabled: bool,
|
||||
|
||||
/// Excludes rooms that we have banned
|
||||
#[arg(long)]
|
||||
exclude_banned: bool,
|
||||
},
|
||||
|
||||
#[command(subcommand)]
|
||||
@@ -179,6 +187,8 @@ pub(super) async fn process(command: RoomCommand, body: Vec<&str>) -> Result<Roo
|
||||
|
||||
RoomCommand::List {
|
||||
page,
|
||||
} => list(body, page).await?,
|
||||
exclude_disabled,
|
||||
exclude_banned,
|
||||
} => list(body, page, exclude_disabled, exclude_banned).await?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,18 +1,46 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
|
||||
|
||||
pub(super) async fn list(_body: Vec<&str>, page: Option<usize>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn list(
|
||||
_body: Vec<&str>, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||
let page = page.unwrap_or(1);
|
||||
let mut rooms = services()
|
||||
.rooms
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.filter_map(Result::ok)
|
||||
.map(|id: OwnedRoomId| get_room_info(&id))
|
||||
.filter_map(|room_id| {
|
||||
room_id
|
||||
.ok()
|
||||
.filter(|room_id| {
|
||||
if exclude_disabled
|
||||
&& services()
|
||||
.rooms
|
||||
.metadata
|
||||
.is_disabled(room_id)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if exclude_banned
|
||||
&& services()
|
||||
.rooms
|
||||
.metadata
|
||||
.is_banned(room_id)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
.map(|room_id| get_room_info(&room_id))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
rooms.sort_by_key(|r| r.1);
|
||||
rooms.reverse();
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use api::client::leave_room;
|
||||
use ruma::{
|
||||
events::room::message::RoomMessageEventContent, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomOrAliasId,
|
||||
};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomAliasId, RoomId, RoomOrAliasId};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
use super::{super::Service, RoomModerationCommand};
|
||||
@@ -124,9 +122,7 @@ async fn ban_room(
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(true))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<OwnedUserId>>()
|
||||
{
|
||||
}) {
|
||||
debug!(
|
||||
"Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)",
|
||||
&local_user, &room_id
|
||||
@@ -153,9 +149,7 @@ async fn ban_room(
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(false))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<OwnedUserId>>()
|
||||
{
|
||||
}) {
|
||||
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
||||
error!(
|
||||
@@ -191,7 +185,10 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
||||
));
|
||||
}
|
||||
|
||||
let rooms_s = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>();
|
||||
let rooms_s = body
|
||||
.clone()
|
||||
.drain(1..body.len().saturating_sub(1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let admin_room_alias = &services().globals.admin_alias;
|
||||
|
||||
@@ -332,9 +329,7 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(true))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<OwnedUserId>>()
|
||||
{
|
||||
}) {
|
||||
debug!(
|
||||
"Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)",
|
||||
&local_user, room_id
|
||||
@@ -361,9 +356,7 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(false))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<OwnedUserId>>()
|
||||
{
|
||||
}) {
|
||||
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
||||
error!(
|
||||
|
||||
@@ -1,24 +1,17 @@
|
||||
use conduit::{warn, Error, Result};
|
||||
use conduit::{utils::time, warn, Err, Result};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::services;
|
||||
|
||||
pub(super) async fn uptime(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let seconds = services()
|
||||
let elapsed = services()
|
||||
.server
|
||||
.started
|
||||
.elapsed()
|
||||
.expect("standard duration")
|
||||
.as_secs();
|
||||
let result = format!(
|
||||
"up {} days, {} hours, {} minutes, {} seconds.",
|
||||
seconds / 86400,
|
||||
(seconds % 86400) / 60 / 60,
|
||||
(seconds % 3600) / 60,
|
||||
seconds % 60,
|
||||
);
|
||||
.expect("standard duration");
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain(result))
|
||||
let result = time::pretty(elapsed);
|
||||
Ok(RoomMessageEventContent::notice_plain(format!("{result}.")))
|
||||
}
|
||||
|
||||
pub(super) async fn show_config(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
@@ -27,17 +20,12 @@ pub(super) async fn show_config(_body: Vec<&str>) -> Result<RoomMessageEventCont
|
||||
}
|
||||
|
||||
pub(super) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let response0 = services().memory_usage().await?;
|
||||
let response1 = services().db.db.memory_usage()?;
|
||||
let response2 = conduit::alloc::memory_usage();
|
||||
let services_usage = services().memory_usage().await?;
|
||||
let database_usage = services().db.db.memory_usage()?;
|
||||
let allocator_usage = conduit::alloc::memory_usage().map_or(String::new(), |s| format!("\nAllocator:\n{s}"));
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Services:\n{response0}\nDatabase:\n{response1}\n{}",
|
||||
if !response2.is_empty() {
|
||||
format!("Allocator:\n {response2}")
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
"Services:\n{services_usage}\nDatabase:\n{database_usage}{allocator_usage}",
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -100,11 +88,10 @@ pub(super) async fn restart(_body: Vec<&str>, force: bool) -> Result<RoomMessage
|
||||
use conduit::utils::sys::current_exe_deleted;
|
||||
|
||||
if !force && current_exe_deleted() {
|
||||
return Err(Error::Err(
|
||||
"The server cannot be restarted because the executable was tampered with. If this is expected use --force \
|
||||
to override."
|
||||
.to_owned(),
|
||||
));
|
||||
return Err!(
|
||||
"The server cannot be restarted because the executable changed. If this is expected use --force to \
|
||||
override."
|
||||
);
|
||||
}
|
||||
|
||||
services().server.restart()?;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#![cfg(test)]
|
||||
|
||||
#[test]
|
||||
fn get_help_short() { get_help_inner("-h"); }
|
||||
|
||||
#[test]
|
||||
fn get_help_long() { get_help_inner("--help"); }
|
||||
|
||||
#[test]
|
||||
fn get_help_subcommand() { get_help_inner("help"); }
|
||||
|
||||
fn get_help_inner(input: &str) {
|
||||
use clap::Parser;
|
||||
|
||||
use crate::handler::AdminCommand;
|
||||
|
||||
let Err(error) = AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) else {
|
||||
panic!("no error!");
|
||||
};
|
||||
|
||||
let error = error.to_string();
|
||||
// Search for a handful of keywords that suggest the help printed properly
|
||||
assert!(error.contains("Usage:"));
|
||||
assert!(error.contains("Commands:"));
|
||||
assert!(error.contains("Options:"));
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use ruma::{
|
||||
tag::{TagEvent, TagEventContent, TagInfo},
|
||||
RoomAccountDataEventType,
|
||||
},
|
||||
OwnedRoomId, OwnedUserId, RoomId,
|
||||
OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, RoomId,
|
||||
};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
@@ -23,7 +23,7 @@ pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match services().users.list_local_users() {
|
||||
Ok(users) => {
|
||||
let mut plain_msg = format!("Found {} local user account(s):\n```\n", users.len());
|
||||
plain_msg += &users.join("\n");
|
||||
plain_msg += users.join("\n").as_str();
|
||||
plain_msg += "\n```";
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(plain_msg))
|
||||
@@ -95,7 +95,7 @@ pub(super) async fn create(
|
||||
|
||||
if let Some(room_id_server_name) = room.server_name() {
|
||||
match join_room_by_id_helper(
|
||||
Some(&user_id),
|
||||
&user_id,
|
||||
room,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
&[room_id_server_name.to_owned(), services().globals.server_name().to_owned()],
|
||||
@@ -195,7 +195,10 @@ pub(super) async fn deactivate_all(
|
||||
));
|
||||
}
|
||||
|
||||
let usernames = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>();
|
||||
let usernames = body
|
||||
.clone()
|
||||
.drain(1..body.len().saturating_sub(1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut user_ids: Vec<OwnedUserId> = Vec::with_capacity(usernames.len());
|
||||
let mut admins = Vec::new();
|
||||
@@ -331,6 +334,35 @@ pub(super) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
|
||||
Ok(RoomMessageEventContent::text_html(output_plain, output_html))
|
||||
}
|
||||
|
||||
pub(super) async fn force_join_room(
|
||||
_body: Vec<&str>, user_id: String, room_id: OwnedRoomOrAliasId,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_local_user_id(&user_id)?;
|
||||
let room_id = services().rooms.alias.resolve(&room_id).await?;
|
||||
|
||||
assert!(service::user_is_local(&user_id), "Parsed user_id must be a local user");
|
||||
join_room_by_id_helper(&user_id, &room_id, None, &[], None).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"{user_id} has been joined to {room_id}.",
|
||||
)))
|
||||
}
|
||||
|
||||
pub(super) async fn make_user_admin(_body: Vec<&str>, user_id: String) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_local_user_id(&user_id)?;
|
||||
let displayname = services()
|
||||
.users
|
||||
.displayname(&user_id)?
|
||||
.unwrap_or_else(|| user_id.to_string());
|
||||
|
||||
assert!(service::user_is_local(&user_id), "Parsed user_id must be a local user");
|
||||
service::admin::make_user_admin(&user_id, displayname).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"{user_id} has been granted admin privileges.",
|
||||
)))
|
||||
}
|
||||
|
||||
pub(super) async fn put_room_tag(
|
||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>, tag: String,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
|
||||
+20
-2
@@ -2,7 +2,7 @@ mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomOrAliasId, RoomId};
|
||||
|
||||
use self::commands::*;
|
||||
|
||||
@@ -49,7 +49,7 @@ pub(super) enum UserCommand {
|
||||
/// Markdown code block below the command.
|
||||
DeactivateAll {
|
||||
#[arg(short, long)]
|
||||
/// Remove users from their joined rooms
|
||||
/// Does not leave any rooms the user is in on deactivation
|
||||
no_leave_rooms: bool,
|
||||
#[arg(short, long)]
|
||||
/// Also deactivate admin accounts and will assume leave all rooms too
|
||||
@@ -65,6 +65,17 @@ pub(super) enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Manually join a local user to a room.
|
||||
ForceJoinRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Grant server-admin privileges to a user.
|
||||
MakeUserAdmin {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Puts a room tag for the specified user and room ID.
|
||||
///
|
||||
/// This is primarily useful if you'd like to set your admin room
|
||||
@@ -113,6 +124,13 @@ pub(super) async fn process(command: UserCommand, body: Vec<&str>) -> Result<Roo
|
||||
UserCommand::ListJoinedRooms {
|
||||
user_id,
|
||||
} => list_joined_rooms(body, user_id).await?,
|
||||
UserCommand::ForceJoinRoom {
|
||||
user_id,
|
||||
room_id,
|
||||
} => force_join_room(body, user_id, room_id).await?,
|
||||
UserCommand::MakeUserAdmin {
|
||||
user_id,
|
||||
} => make_user_admin(body, user_id).await?,
|
||||
UserCommand::PutRoomTag {
|
||||
user_id,
|
||||
room_id,
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
use conduit_core::Error;
|
||||
use conduit_core::{err, Err};
|
||||
use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId};
|
||||
use service::user_is_local;
|
||||
|
||||
@@ -33,7 +33,7 @@ pub(crate) fn get_room_info(id: &RoomId) -> (OwnedRoomId, u64, String) {
|
||||
/// Parses user ID
|
||||
pub(crate) fn parse_user_id(user_id: &str) -> Result<OwnedUserId> {
|
||||
UserId::parse_with_server_name(user_id.to_lowercase(), services().globals.server_name())
|
||||
.map_err(|e| Error::Err(format!("The supplied username is not a valid username: {e}")))
|
||||
.map_err(|e| err!("The supplied username is not a valid username: {e}"))
|
||||
}
|
||||
|
||||
/// Parses user ID as our local user
|
||||
@@ -41,7 +41,7 @@ pub(crate) fn parse_local_user_id(user_id: &str) -> Result<OwnedUserId> {
|
||||
let user_id = parse_user_id(user_id)?;
|
||||
|
||||
if !user_is_local(&user_id) {
|
||||
return Err(Error::Err(String::from("User does not belong to our server.")));
|
||||
return Err!("User {user_id:?} does not belong to our server.");
|
||||
}
|
||||
|
||||
Ok(user_id)
|
||||
@@ -52,11 +52,11 @@ pub(crate) fn parse_active_local_user_id(user_id: &str) -> Result<OwnedUserId> {
|
||||
let user_id = parse_local_user_id(user_id)?;
|
||||
|
||||
if !services().users.exists(&user_id)? {
|
||||
return Err(Error::Err(String::from("User does not exist on this server.")));
|
||||
return Err!("User {user_id:?} does not exist on this server.");
|
||||
}
|
||||
|
||||
if services().users.is_deactivated(&user_id)? {
|
||||
return Err(Error::Err(String::from("User is deactivated.")));
|
||||
return Err!("User {user_id:?} is deactivated.");
|
||||
}
|
||||
|
||||
Ok(user_id)
|
||||
|
||||
+2
-1
@@ -41,9 +41,11 @@ bytes.workspace = true
|
||||
conduit-core.workspace = true
|
||||
conduit-database.workspace = true
|
||||
conduit-service.workspace = true
|
||||
const-str.workspace = true
|
||||
futures-util.workspace = true
|
||||
hmac.workspace = true
|
||||
http.workspace = true
|
||||
http-body-util.workspace = true
|
||||
hyper.workspace = true
|
||||
image.workspace = true
|
||||
ipaddress.workspace = true
|
||||
@@ -56,7 +58,6 @@ serde_html_form.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
sha-1.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
webpage.workspace = true
|
||||
|
||||
@@ -309,7 +309,7 @@ pub(crate) async fn register_route(
|
||||
|
||||
// log in conduit admin channel if a guest registered
|
||||
if body.appservice_info.is_none() && is_guest && services().globals.log_guest_registrations() {
|
||||
info!("New guest user \"{user_id}\" registered on this server from IP.");
|
||||
info!("New guest user \"{user_id}\" registered on this server.");
|
||||
|
||||
if let Some(device_display_name) = &body.initial_device_display_name {
|
||||
if body
|
||||
@@ -376,7 +376,7 @@ pub(crate) async fn register_route(
|
||||
|
||||
if let Some(room_id_server_name) = room.server_name() {
|
||||
if let Err(e) = join_room_by_id_helper(
|
||||
Some(&user_id),
|
||||
&user_id,
|
||||
room,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
&[room_id_server_name.to_owned(), services().globals.server_name().to_owned()],
|
||||
@@ -423,7 +423,12 @@ pub(crate) async fn register_route(
|
||||
pub(crate) async fn change_password_route(
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<change_password::v3::Request>,
|
||||
) -> Result<change_password::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
// Authentication for this endpoint was made optional, but we need
|
||||
// authentication currently
|
||||
let sender_user = body
|
||||
.sender_user
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::MissingToken, "Missing access token."))?;
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
@@ -512,7 +517,12 @@ pub(crate) async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoa
|
||||
pub(crate) async fn deactivate_route(
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<deactivate::v3::Request>,
|
||||
) -> Result<deactivate::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
// Authentication for this endpoint was made optional, but we need
|
||||
// authentication currently
|
||||
let sender_user = body
|
||||
.sender_user
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::MissingToken, "Missing access token."))?;
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::{err, info, warn, Error, Result};
|
||||
use ruma::{
|
||||
api::{
|
||||
client::{
|
||||
@@ -10,14 +11,16 @@ use ruma::{
|
||||
},
|
||||
directory::{Filter, PublicRoomJoinRule, PublicRoomsChunk, RoomNetwork},
|
||||
events::{
|
||||
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
room::{
|
||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
},
|
||||
StateEventType,
|
||||
},
|
||||
uint, ServerName, UInt,
|
||||
uint, RoomId, ServerName, UInt, UserId,
|
||||
};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
use crate::{service::server_is_ours, services, Error, Result, Ruma};
|
||||
use crate::{service::server_is_ours, services, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/v3/publicRooms`
|
||||
///
|
||||
@@ -103,8 +106,6 @@ pub(crate) async fn get_public_rooms_route(
|
||||
/// # `PUT /_matrix/client/r0/directory/list/room/{roomId}`
|
||||
///
|
||||
/// Sets the visibility of a given room in the room directory.
|
||||
///
|
||||
/// - TODO: Access control checks
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "room_directory")]
|
||||
pub(crate) async fn set_room_visibility_route(
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<set_room_visibility::v3::Request>,
|
||||
@@ -116,6 +117,8 @@ pub(crate) async fn set_room_visibility_route(
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
||||
}
|
||||
|
||||
user_can_publish_room(sender_user, &body.room_id)?;
|
||||
|
||||
match &body.visibility {
|
||||
room::Visibility::Public => {
|
||||
if services().globals.config.lockdown_public_room_directory && !services().users.is_admin(sender_user)? {
|
||||
@@ -268,8 +271,7 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
_ => None,
|
||||
})
|
||||
.map_err(|e| {
|
||||
error!("Invalid room join rule event in database: {}", e);
|
||||
Error::BadDatabase("Invalid room join rule event in database.")
|
||||
err!(Database(error!("Invalid room join rule event in database: {e}")))
|
||||
})
|
||||
})
|
||||
.transpose()?
|
||||
@@ -351,3 +353,32 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
total_room_count_estimate: Some(total_room_count_estimate),
|
||||
})
|
||||
}
|
||||
|
||||
/// Check whether the user can publish to the room directory via power levels of
|
||||
/// room history visibility event or room creator
|
||||
fn user_can_publish_room(user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
||||
if let Some(event) =
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
||||
{
|
||||
serde_json::from_str(event.content.get())
|
||||
.map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels"))
|
||||
.map(|content: RoomPowerLevelsEventContent| {
|
||||
RoomPowerLevels::from(content).user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
||||
})
|
||||
} else if let Some(event) =
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
|
||||
{
|
||||
Ok(event.sender == user_id)
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Unauthorized,
|
||||
"You are not allowed to publish this room to the room directory",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+8
-13
@@ -1,9 +1,9 @@
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{hash_map, BTreeMap, HashMap, HashSet},
|
||||
time::{Duration, Instant},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use conduit::{utils, utils::math::continue_exponential_backoff_secs, Error, Result};
|
||||
use futures_util::{stream::FuturesUnordered, StreamExt};
|
||||
use ruma::{
|
||||
api::{
|
||||
@@ -18,15 +18,11 @@ use ruma::{
|
||||
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
||||
};
|
||||
use serde_json::json;
|
||||
use service::user_is_local;
|
||||
use tracing::debug;
|
||||
|
||||
use super::SESSION_ID_LENGTH;
|
||||
use crate::{
|
||||
service::user_is_local,
|
||||
services,
|
||||
utils::{self},
|
||||
Error, Result, Ruma,
|
||||
};
|
||||
use crate::{services, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/r0/keys/upload`
|
||||
///
|
||||
@@ -357,11 +353,10 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
.get(server)
|
||||
{
|
||||
// Exponential backoff
|
||||
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
|
||||
let min_elapsed_duration = cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
|
||||
|
||||
if time.elapsed() < min_elapsed_duration {
|
||||
debug!("Backing off query from {:?}", server);
|
||||
const MIN: u64 = 5 * 60;
|
||||
const MAX: u64 = 60 * 60 * 24;
|
||||
if continue_exponential_backoff_secs(MIN, MAX, time.elapsed(), *tries) {
|
||||
debug!("Backing off query from {server:?}");
|
||||
return (server, Err(Error::BadServerResponse("bad query, still backing off")));
|
||||
}
|
||||
}
|
||||
|
||||
+45
-19
@@ -2,6 +2,8 @@
|
||||
|
||||
use std::{io::Cursor, sync::Arc, time::Duration};
|
||||
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::{debug, error, utils::math::ruma_from_usize, warn};
|
||||
use image::io::Reader as ImgReader;
|
||||
use ipaddress::IPAddress;
|
||||
use reqwest::Url;
|
||||
@@ -12,7 +14,6 @@ use ruma::api::client::{
|
||||
get_media_preview,
|
||||
},
|
||||
};
|
||||
use tracing::{debug, error, warn};
|
||||
use webpage::HTML;
|
||||
|
||||
use crate::{
|
||||
@@ -44,7 +45,7 @@ pub(crate) async fn get_media_config_route(
|
||||
_body: Ruma<get_media_config::v3::Request>,
|
||||
) -> Result<get_media_config::v3::Response> {
|
||||
Ok(get_media_config::v3::Response {
|
||||
upload_size: services().globals.max_request_size().into(),
|
||||
upload_size: ruma_from_usize(services().globals.config.max_request_size),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -64,18 +65,22 @@ pub(crate) async fn get_media_config_v1_route(
|
||||
/// # `GET /_matrix/media/v3/preview_url`
|
||||
///
|
||||
/// Returns URL preview.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "url_preview")]
|
||||
pub(crate) async fn get_media_preview_route(
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<get_media_preview::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let url = &body.url;
|
||||
if !url_preview_allowed(url) {
|
||||
warn!(%sender_user, "URL is not allowed to be previewed: {url}");
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "URL is not allowed to be previewed"));
|
||||
}
|
||||
|
||||
match get_url_preview(url).await {
|
||||
Ok(preview) => {
|
||||
let res = serde_json::value::to_raw_value(&preview).map_err(|e| {
|
||||
error!("Failed to convert UrlPreviewData into a serde json value: {}", e);
|
||||
error!(%sender_user, "Failed to convert UrlPreviewData into a serde json value: {e}");
|
||||
Error::BadRequest(
|
||||
ErrorKind::LimitExceeded {
|
||||
retry_after: Some(RetryAfter::Delay(Duration::from_secs(5))),
|
||||
@@ -87,7 +92,7 @@ pub(crate) async fn get_media_preview_route(
|
||||
Ok(get_media_preview::v3::Response::from_raw_value(res))
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("Failed to generate a URL preview: {e}");
|
||||
warn!(%sender_user, "Failed to generate a URL preview: {e}");
|
||||
|
||||
// there doesn't seem to be an agreed-upon error code in the spec.
|
||||
// the only response codes in the preview_url spec page are 200 and 429.
|
||||
@@ -108,10 +113,13 @@ pub(crate) async fn get_media_preview_route(
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// Returns URL preview.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "url_preview")]
|
||||
pub(crate) async fn get_media_preview_v1_route(
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
|
||||
get_media_preview_route(body).await.map(RumaResponse)
|
||||
get_media_preview_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/media/v3/upload`
|
||||
@@ -120,8 +128,9 @@ pub(crate) async fn get_media_preview_v1_route(
|
||||
///
|
||||
/// - Some metadata will be saved in the database
|
||||
/// - Media will be saved in the media/ directory
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_upload")]
|
||||
pub(crate) async fn create_content_route(
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<create_content::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -167,10 +176,13 @@ pub(crate) async fn create_content_route(
|
||||
///
|
||||
/// - Some metadata will be saved in the database
|
||||
/// - Media will be saved in the media/ directory
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_upload")]
|
||||
pub(crate) async fn create_content_v1_route(
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<RumaResponse<create_content::v3::Response>> {
|
||||
create_content_route(body).await.map(RumaResponse)
|
||||
create_content_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}`
|
||||
@@ -181,7 +193,10 @@ pub(crate) async fn create_content_v1_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> Result<get_content::v3::Response> {
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||
pub(crate) async fn get_content_route(
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<get_content::v3::Response> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
@@ -243,10 +258,13 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||
pub(crate) async fn get_content_v1_route(
|
||||
body: Ruma<get_content::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content::v3::Response>> {
|
||||
get_content_route(body).await.map(RumaResponse)
|
||||
get_content_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}`
|
||||
@@ -257,8 +275,9 @@ pub(crate) async fn get_content_v1_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||
pub(crate) async fn get_content_as_filename_route(
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<get_content_as_filename::v3::Response> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
@@ -328,10 +347,13 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||
pub(crate) async fn get_content_as_filename_v1_route(
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
||||
get_content_as_filename_route(body).await.map(RumaResponse)
|
||||
get_content_as_filename_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/thumbnail/{serverName}/{mediaId}`
|
||||
@@ -342,8 +364,9 @@ pub(crate) async fn get_content_as_filename_v1_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
||||
pub(crate) async fn get_content_thumbnail_route(
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<get_content_thumbnail::v3::Response> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
@@ -453,10 +476,13 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
||||
pub(crate) async fn get_content_thumbnail_v1_route(
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
||||
get_content_thumbnail_route(body).await.map(RumaResponse)
|
||||
get_content_thumbnail_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
async fn get_remote_content(
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{hash_map::Entry, BTreeMap, HashMap, HashSet},
|
||||
net::IpAddr,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::utils::mutex_map;
|
||||
use conduit::{
|
||||
debug, debug_warn, error, info, trace, utils, utils::math::continue_exponential_backoff_secs, warn, Error,
|
||||
PduEvent, Result,
|
||||
};
|
||||
use ruma::{
|
||||
api::{
|
||||
client::{
|
||||
@@ -33,17 +35,17 @@ use ruma::{
|
||||
OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
|
||||
};
|
||||
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
||||
use service::sending::convert_to_outgoing_federation_event;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use crate::{
|
||||
client::{update_avatar_url, update_displayname},
|
||||
service::{
|
||||
pdu::{gen_event_id_canonical_json, PduBuilder},
|
||||
rooms::state::RoomMutexGuard,
|
||||
sending::convert_to_outgoing_federation_event,
|
||||
server_is_ours, user_is_local,
|
||||
},
|
||||
services, utils, Error, PduEvent, Result, Ruma,
|
||||
services, Ruma,
|
||||
};
|
||||
|
||||
/// Checks if the room is banned in any way possible and the sender user is not
|
||||
@@ -200,7 +202,7 @@ pub(crate) async fn join_room_by_id_route(
|
||||
}
|
||||
|
||||
join_room_by_id_helper(
|
||||
body.sender_user.as_deref(),
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
body.reason.clone(),
|
||||
&servers,
|
||||
@@ -299,7 +301,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
};
|
||||
|
||||
let join_room_response = join_room_by_id_helper(
|
||||
Some(sender_user),
|
||||
sender_user,
|
||||
&room_id,
|
||||
body.reason.clone(),
|
||||
&servers,
|
||||
@@ -364,6 +366,8 @@ pub(crate) async fn invite_user_route(
|
||||
pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Result<kick_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
let mut event: RoomMemberEventContent = serde_json::from_str(
|
||||
services()
|
||||
.rooms
|
||||
@@ -381,12 +385,6 @@ pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Resul
|
||||
event.membership = MembershipState::Leave;
|
||||
event.reason.clone_from(&body.reason);
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
@@ -415,6 +413,8 @@ pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Resul
|
||||
pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<ban_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
let event = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
@@ -445,12 +445,6 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
|
||||
},
|
||||
)?;
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
@@ -479,6 +473,8 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
|
||||
pub(crate) async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Result<unban_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
let mut event: RoomMemberEventContent = serde_json::from_str(
|
||||
services()
|
||||
.rooms
|
||||
@@ -494,12 +490,6 @@ pub(crate) async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Res
|
||||
event.reason.clone_from(&body.reason);
|
||||
event.join_authorized_via_users_server = None;
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
@@ -651,35 +641,36 @@ pub(crate) async fn joined_members_route(
|
||||
}
|
||||
|
||||
pub async fn join_room_by_id_helper(
|
||||
sender_user: Option<&UserId>, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
|
||||
sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
|
||||
third_party_signed: Option<&ThirdPartySigned>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
let sender_user = sender_user.expect("user is authenticated");
|
||||
let state_lock = services().rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
if matches!(services().rooms.state_cache.is_joined(sender_user, room_id), Ok(true)) {
|
||||
info!("{sender_user} is already joined in {room_id}");
|
||||
debug_warn!("{sender_user} is already joined in {room_id}");
|
||||
return Ok(join_room_by_id::v3::Response {
|
||||
room_id: room_id.into(),
|
||||
});
|
||||
}
|
||||
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
|
||||
// Ask a remote server if we are not participating in this room
|
||||
if !services()
|
||||
if services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services().globals.server_name(), room_id)?
|
||||
|| servers.is_empty()
|
||||
|| (servers.len() == 1 && server_is_ours(&servers[0]))
|
||||
{
|
||||
join_room_by_id_helper_remote(sender_user, room_id, reason, servers, third_party_signed, state_lock).await
|
||||
} else {
|
||||
join_room_by_id_helper_local(sender_user, room_id, reason, servers, third_party_signed, state_lock).await
|
||||
} else {
|
||||
// Ask a remote server if we are not participating in this room
|
||||
join_room_by_id_helper_remote(sender_user, room_id, reason, servers, third_party_signed, state_lock).await
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_remote")]
|
||||
async fn join_room_by_id_helper_remote(
|
||||
sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
|
||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: mutex_map::Guard<()>,
|
||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: RoomMutexGuard,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
info!("Joining {room_id} over federation.");
|
||||
|
||||
@@ -789,14 +780,9 @@ async fn join_room_by_id_helper_remote(
|
||||
info!("send_join finished");
|
||||
|
||||
if join_authorized_via_users_server.is_some() {
|
||||
use RoomVersionId::*;
|
||||
match &room_version_id {
|
||||
RoomVersionId::V1
|
||||
| RoomVersionId::V2
|
||||
| RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
| RoomVersionId::V7 => {
|
||||
V1 | V2 | V3 | V4 | V5 | V6 | V7 => {
|
||||
warn!(
|
||||
"Found `join_authorised_via_users_server` but room {} is version {}. Ignoring.",
|
||||
room_id, &room_version_id
|
||||
@@ -804,7 +790,7 @@ async fn join_room_by_id_helper_remote(
|
||||
},
|
||||
// only room versions 8 and above using `join_authorized_via_users_server` (restricted joins) need to
|
||||
// validate and send signatures
|
||||
RoomVersionId::V8 | RoomVersionId::V9 | RoomVersionId::V10 | RoomVersionId::V11 => {
|
||||
V8 | V9 | V10 | V11 => {
|
||||
if let Some(signed_raw) = &send_join_response.room_state.event {
|
||||
info!(
|
||||
"There is a signed event. This room is probably using restricted joins. Adding signature to \
|
||||
@@ -1012,11 +998,12 @@ async fn join_room_by_id_helper_remote(
|
||||
Ok(join_room_by_id::v3::Response::new(room_id.to_owned()))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_local")]
|
||||
async fn join_room_by_id_helper_local(
|
||||
sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
|
||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: mutex_map::Guard<()>,
|
||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: RoomMutexGuard,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
info!("We can join locally");
|
||||
debug!("We can join locally");
|
||||
|
||||
let join_rules_event =
|
||||
services()
|
||||
@@ -1116,7 +1103,7 @@ async fn join_room_by_id_helper_local(
|
||||
.iter()
|
||||
.any(|server_name| !server_is_ours(server_name))
|
||||
{
|
||||
info!("We couldn't do the join locally, maybe federation can help to satisfy the restricted join requirements");
|
||||
warn!("We couldn't do the join locally, maybe federation can help to satisfy the restricted join requirements");
|
||||
let (make_join_response, remote_server) = make_join_request(sender_user, room_id, servers).await?;
|
||||
|
||||
let room_version_id = match make_join_response.room_version {
|
||||
@@ -1281,16 +1268,12 @@ async fn make_join_request(
|
||||
make_join_counter = make_join_counter.saturating_add(1);
|
||||
|
||||
if let Err(ref e) = make_join_response {
|
||||
trace!("make_join ErrorKind string: {:?}", e.error_code().to_string());
|
||||
trace!("make_join ErrorKind string: {:?}", e.kind().to_string());
|
||||
|
||||
// converting to a string is necessary (i think) because ruma is forcing us to
|
||||
// fill in the struct for M_INCOMPATIBLE_ROOM_VERSION
|
||||
if e.error_code()
|
||||
.to_string()
|
||||
.contains("M_INCOMPATIBLE_ROOM_VERSION")
|
||||
|| e.error_code()
|
||||
.to_string()
|
||||
.contains("M_UNSUPPORTED_ROOM_VERSION")
|
||||
if e.kind().to_string().contains("M_INCOMPATIBLE_ROOM_VERSION")
|
||||
|| e.kind().to_string().contains("M_UNSUPPORTED_ROOM_VERSION")
|
||||
{
|
||||
incompatible_room_version_count = incompatible_room_version_count.saturating_add(1);
|
||||
}
|
||||
@@ -1363,11 +1346,10 @@ pub async fn validate_and_add_event_id(
|
||||
.get(&event_id)
|
||||
{
|
||||
// Exponential backoff
|
||||
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
|
||||
let min_elapsed_duration = cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
|
||||
|
||||
if time.elapsed() < min_elapsed_duration {
|
||||
debug!("Backing off from {}", event_id);
|
||||
const MIN: u64 = 60 * 5;
|
||||
const MAX: u64 = 60 * 60 * 24;
|
||||
if continue_exponential_backoff_secs(MIN, MAX, time.elapsed(), *tries) {
|
||||
debug!("Backing off from {event_id}");
|
||||
return Err(Error::BadServerResponse("bad event, still backing off"));
|
||||
}
|
||||
}
|
||||
@@ -1396,7 +1378,7 @@ pub(crate) async fn invite_helper(
|
||||
|
||||
if !user_is_local(user_id) {
|
||||
let (pdu, pdu_json, invite_room_state) = {
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
let state_lock = services().rooms.state.mutex.lock(room_id).await;
|
||||
let content = to_raw_value(&RoomMemberEventContent {
|
||||
avatar_url: services().users.avatar_url(user_id)?,
|
||||
displayname: None,
|
||||
@@ -1508,7 +1490,7 @@ pub(crate) async fn invite_helper(
|
||||
));
|
||||
}
|
||||
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
let state_lock = services().rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
services()
|
||||
.rooms
|
||||
@@ -1602,7 +1584,7 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<Strin
|
||||
true,
|
||||
)?;
|
||||
} else {
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
let state_lock = services().rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let member_event =
|
||||
services()
|
||||
@@ -1681,8 +1663,7 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> {
|
||||
.filter_map(|event: serde_json::Value| event.get("sender").cloned())
|
||||
.filter_map(|sender| sender.as_str().map(ToOwned::to_owned))
|
||||
.filter_map(|sender| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned())
|
||||
.collect::<HashSet<OwnedServerName>>(),
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
|
||||
debug!("servers in remote_leave_room: {servers:?}");
|
||||
|
||||
@@ -29,11 +29,7 @@ pub(crate) async fn send_message_event_route(
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_deref();
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
// Forbid m.room.encrypted if encryption is disabled
|
||||
if MessageLikeEventType::RoomEncrypted == body.event_type && !services().globals.allow_encryption() {
|
||||
|
||||
@@ -353,7 +353,7 @@ pub async fn update_avatar_url(
|
||||
|
||||
pub async fn update_all_rooms(all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: OwnedUserId) {
|
||||
for (pdu_builder, room_id) in all_joined_rooms {
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
let state_lock = services().rooms.state.mutex.lock(room_id).await;
|
||||
if let Err(e) = services()
|
||||
.rooms
|
||||
.timeline
|
||||
|
||||
@@ -15,11 +15,7 @@ pub(crate) async fn redact_event_route(body: Ruma<redact_event::v3::Request>) ->
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let body = body.body;
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
let event_id = services()
|
||||
.rooms
|
||||
|
||||
+35
-64
@@ -90,7 +90,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
||||
}
|
||||
|
||||
let _short_id = services().rooms.short.get_or_create_shortroomid(&room_id)?;
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(&room_id).await;
|
||||
let state_lock = services().rooms.state.mutex.lock(&room_id).await;
|
||||
|
||||
let alias: Option<OwnedRoomAliasId> = if let Some(alias) = &body.room_alias_name {
|
||||
Some(room_alias_check(alias, &body.appservice_info).await?)
|
||||
@@ -118,6 +118,8 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
||||
|
||||
let content = match &body.creation_content {
|
||||
Some(content) => {
|
||||
use RoomVersionId::*;
|
||||
|
||||
let mut content = content
|
||||
.deserialize_as::<CanonicalJsonObject>()
|
||||
.map_err(|e| {
|
||||
@@ -125,16 +127,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
||||
Error::bad_database("Failed to deserialise content as canonical JSON.")
|
||||
})?;
|
||||
match room_version {
|
||||
RoomVersionId::V1
|
||||
| RoomVersionId::V2
|
||||
| RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
| RoomVersionId::V7
|
||||
| RoomVersionId::V8
|
||||
| RoomVersionId::V9
|
||||
| RoomVersionId::V10 => {
|
||||
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||
content.insert(
|
||||
"creator".into(),
|
||||
json!(&sender_user).try_into().map_err(|e| {
|
||||
@@ -143,7 +136,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
||||
})?,
|
||||
);
|
||||
},
|
||||
RoomVersionId::V11 => {}, // V11 removed the "creator" key
|
||||
V11 => {}, // V11 removed the "creator" key
|
||||
_ => {
|
||||
warn!("Unexpected or unsupported room version {room_version}");
|
||||
return Err(Error::BadRequest(
|
||||
@@ -152,7 +145,6 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
||||
));
|
||||
},
|
||||
}
|
||||
|
||||
content.insert(
|
||||
"room_version".into(),
|
||||
json!(room_version.as_str())
|
||||
@@ -162,18 +154,11 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
||||
content
|
||||
},
|
||||
None => {
|
||||
use RoomVersionId::*;
|
||||
|
||||
let content = match room_version {
|
||||
RoomVersionId::V1
|
||||
| RoomVersionId::V2
|
||||
| RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
| RoomVersionId::V7
|
||||
| RoomVersionId::V8
|
||||
| RoomVersionId::V9
|
||||
| RoomVersionId::V10 => RoomCreateEventContent::new_v1(sender_user.clone()),
|
||||
RoomVersionId::V11 => RoomCreateEventContent::new_v11(),
|
||||
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => RoomCreateEventContent::new_v1(sender_user.clone()),
|
||||
V11 => RoomCreateEventContent::new_v11(),
|
||||
_ => {
|
||||
warn!("Unexpected or unsupported room version {room_version}");
|
||||
return Err(Error::BadRequest(
|
||||
@@ -573,11 +558,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
||||
.short
|
||||
.get_or_create_shortroomid(&replacement_room)?;
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
// Send a m.room.tombstone event to the old room to indicate that it is not
|
||||
// intended to be used any further Fail if the sender does not have the required
|
||||
@@ -605,11 +586,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
||||
|
||||
// Change lock to replacement room
|
||||
drop(state_lock);
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&replacement_room)
|
||||
.await;
|
||||
let state_lock = services().rooms.state.mutex.lock(&replacement_room).await;
|
||||
|
||||
// Get the old room creation event
|
||||
let mut create_event_content = serde_json::from_str::<CanonicalJsonObject>(
|
||||
@@ -631,36 +608,30 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
||||
|
||||
// Send a m.room.create event containing a predecessor field and the applicable
|
||||
// room_version
|
||||
match body.new_version {
|
||||
RoomVersionId::V1
|
||||
| RoomVersionId::V2
|
||||
| RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
| RoomVersionId::V7
|
||||
| RoomVersionId::V8
|
||||
| RoomVersionId::V9
|
||||
| RoomVersionId::V10 => {
|
||||
create_event_content.insert(
|
||||
"creator".into(),
|
||||
json!(&sender_user).try_into().map_err(|e| {
|
||||
info!("Error forming creation event: {e}");
|
||||
Error::BadRequest(ErrorKind::BadJson, "Error forming creation event")
|
||||
})?,
|
||||
);
|
||||
},
|
||||
RoomVersionId::V11 => {
|
||||
// "creator" key no longer exists in V11 rooms
|
||||
create_event_content.remove("creator");
|
||||
},
|
||||
_ => {
|
||||
warn!("Unexpected or unsupported room version {}", body.new_version);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::BadJson,
|
||||
"Unexpected or unsupported room version found",
|
||||
));
|
||||
},
|
||||
{
|
||||
use RoomVersionId::*;
|
||||
match body.new_version {
|
||||
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||
create_event_content.insert(
|
||||
"creator".into(),
|
||||
json!(&sender_user).try_into().map_err(|e| {
|
||||
info!("Error forming creation event: {e}");
|
||||
Error::BadRequest(ErrorKind::BadJson, "Error forming creation event")
|
||||
})?,
|
||||
);
|
||||
},
|
||||
V11 => {
|
||||
// "creator" key no longer exists in V11 rooms
|
||||
create_event_content.remove("creator");
|
||||
},
|
||||
_ => {
|
||||
warn!("Unexpected or unsupported room version {}", body.new_version);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::BadJson,
|
||||
"Unexpected or unsupported room version found",
|
||||
));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
create_event_content.insert(
|
||||
|
||||
@@ -47,7 +47,7 @@ pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>)
|
||||
&body.room_id,
|
||||
limit.try_into().unwrap_or(10),
|
||||
key.map_or(vec![], |token| token.short_room_ids),
|
||||
max_depth.try_into().unwrap_or(3),
|
||||
max_depth.into(),
|
||||
body.suggested_only,
|
||||
)
|
||||
.await
|
||||
|
||||
+12
-14
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use conduit::{error, warn};
|
||||
use conduit::{debug_info, error};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
@@ -36,18 +36,16 @@ pub(crate) async fn send_state_event_for_key_route(
|
||||
) -> Result<send_state_event::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let event_id = send_state_event_for_key_helper(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&body.event_type,
|
||||
&body.body.body,
|
||||
body.state_key.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let event_id = (*event_id).to_owned();
|
||||
Ok(send_state_event::v3::Response {
|
||||
event_id,
|
||||
event_id: send_state_event_for_key_helper(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&body.event_type,
|
||||
&body.body.body,
|
||||
body.state_key.clone(),
|
||||
)
|
||||
.await?
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -128,7 +126,7 @@ pub(crate) async fn get_state_events_for_key_route(
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, &body.event_type, &body.state_key)?
|
||||
.ok_or_else(|| {
|
||||
warn!("State event {:?} not found in room {:?}", &body.event_type, &body.room_id);
|
||||
debug_info!("State event {:?} not found in room {:?}", &body.event_type, &body.room_id);
|
||||
Error::BadRequest(ErrorKind::NotFound, "State event not found.")
|
||||
})?;
|
||||
if body
|
||||
@@ -172,7 +170,7 @@ async fn send_state_event_for_key_helper(
|
||||
sender: &UserId, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>, state_key: String,
|
||||
) -> Result<Arc<EventId>> {
|
||||
allowed_to_send_state_event(room_id, event_type, json).await?;
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
let state_lock = services().rooms.state.mutex.lock(room_id).await;
|
||||
let event_id = services()
|
||||
.rooms
|
||||
.timeline
|
||||
|
||||
+30
-35
@@ -1,10 +1,15 @@
|
||||
use std::{
|
||||
cmp,
|
||||
cmp::Ordering,
|
||||
collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use conduit::PduCount;
|
||||
use conduit::{
|
||||
error,
|
||||
utils::math::{ruma_from_u64, ruma_from_usize, usize_from_ruma, usize_from_u64_truncated},
|
||||
Err, PduCount,
|
||||
};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
filter::{FilterDefinition, LazyLoadOptions},
|
||||
@@ -27,7 +32,7 @@ use ruma::{
|
||||
serde::Raw,
|
||||
uint, DeviceId, EventId, OwnedUserId, RoomId, UInt, UserId,
|
||||
};
|
||||
use tracing::{error, Instrument as _, Span};
|
||||
use tracing::{Instrument as _, Span};
|
||||
|
||||
use crate::{service::pdu::EventHash, services, utils, Error, PduEvent, Result, Ruma, RumaResponse};
|
||||
|
||||
@@ -194,7 +199,7 @@ pub(crate) async fn sync_events_route(
|
||||
let (room_id, invite_state_events) = result?;
|
||||
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let insert_lock = services().globals.roomid_mutex_insert.lock(&room_id).await;
|
||||
let insert_lock = services().rooms.timeline.mutex_insert.lock(&room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let invite_count = services()
|
||||
@@ -298,15 +303,9 @@ pub(crate) async fn sync_events_route(
|
||||
{
|
||||
// Hang a few seconds so requests are not spammed
|
||||
// Stop hanging if new info arrives
|
||||
let mut duration = body.timeout.unwrap_or_default();
|
||||
if duration.as_secs() > 30 {
|
||||
duration = Duration::from_secs(30);
|
||||
}
|
||||
|
||||
#[allow(clippy::let_underscore_must_use)]
|
||||
{
|
||||
_ = tokio::time::timeout(duration, watcher).await;
|
||||
}
|
||||
let default = Duration::from_secs(30);
|
||||
let duration = cmp::min(body.timeout.unwrap_or(default), default);
|
||||
_ = tokio::time::timeout(duration, watcher).await;
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
@@ -318,7 +317,7 @@ async fn handle_left_room(
|
||||
next_batch_string: &str, full_state: bool, lazy_load_enabled: bool,
|
||||
) -> Result<()> {
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let insert_lock = services().globals.roomid_mutex_insert.lock(room_id).await;
|
||||
let insert_lock = services().rooms.timeline.mutex_insert.lock(room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let left_count = services()
|
||||
@@ -520,7 +519,7 @@ async fn load_joined_room(
|
||||
) -> Result<JoinedRoom> {
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
// This will make sure the we have all events until next_batch
|
||||
let insert_lock = services().globals.roomid_mutex_insert.lock(room_id).await;
|
||||
let insert_lock = services().rooms.timeline.mutex_insert.lock(room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let (timeline_pdus, limited) = load_timeline(sender_user, room_id, sincecount, 10)?;
|
||||
@@ -546,8 +545,7 @@ async fn load_joined_room(
|
||||
// Database queries:
|
||||
|
||||
let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else {
|
||||
error!("Room {} has no state", room_id);
|
||||
return Err(Error::BadDatabase("Room has no state"));
|
||||
return Err!(Database(error!("Room {room_id} has no state")));
|
||||
};
|
||||
|
||||
let since_shortstatehash = services()
|
||||
@@ -975,8 +973,8 @@ async fn load_joined_room(
|
||||
},
|
||||
summary: RoomSummary {
|
||||
heroes,
|
||||
joined_member_count: joined_member_count.map(|n| (n as u32).into()),
|
||||
invited_member_count: invited_member_count.map(|n| (n as u32).into()),
|
||||
joined_member_count: joined_member_count.map(ruma_from_u64),
|
||||
invited_member_count: invited_member_count.map(ruma_from_u64),
|
||||
},
|
||||
unread_notifications: UnreadNotificationsCount {
|
||||
highlight_count,
|
||||
@@ -1026,7 +1024,7 @@ fn load_timeline(
|
||||
// Take the last events for the timeline
|
||||
timeline_pdus = non_timeline_pdus
|
||||
.by_ref()
|
||||
.take(limit as usize)
|
||||
.take(usize_from_u64_truncated(limit))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.rev()
|
||||
@@ -1300,7 +1298,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
r.0,
|
||||
UInt::try_from(all_joined_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
|
||||
);
|
||||
let room_ids = all_joined_rooms[(u64::from(r.0) as usize)..=(u64::from(r.1) as usize)].to_vec();
|
||||
let room_ids = all_joined_rooms[usize_from_ruma(r.0)..=usize_from_ruma(r.1)].to_vec();
|
||||
new_known_rooms.extend(room_ids.iter().cloned());
|
||||
for room_id in &room_ids {
|
||||
let todo_room = todo_rooms
|
||||
@@ -1333,7 +1331,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
count: UInt::from(all_joined_rooms.len() as u32),
|
||||
count: ruma_from_usize(all_joined_rooms.len()),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1529,20 +1527,22 @@ pub(crate) async fn sync_events_v4_route(
|
||||
prev_batch,
|
||||
limited,
|
||||
joined_count: Some(
|
||||
(services()
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(room_id)?
|
||||
.unwrap_or(0) as u32)
|
||||
.into(),
|
||||
.unwrap_or(0)
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| uint!(0)),
|
||||
),
|
||||
invited_count: Some(
|
||||
(services()
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_invited_count(room_id)?
|
||||
.unwrap_or(0) as u32)
|
||||
.into(),
|
||||
.unwrap_or(0)
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| uint!(0)),
|
||||
),
|
||||
num_live: None, // Count events in timeline greater than global sync counter
|
||||
timestamp: None,
|
||||
@@ -1557,14 +1557,9 @@ pub(crate) async fn sync_events_v4_route(
|
||||
{
|
||||
// Hang a few seconds so requests are not spammed
|
||||
// Stop hanging if new info arrives
|
||||
let mut duration = body.timeout.unwrap_or(Duration::from_secs(30));
|
||||
if duration.as_secs() > 30 {
|
||||
duration = Duration::from_secs(30);
|
||||
}
|
||||
#[allow(clippy::let_underscore_must_use)]
|
||||
{
|
||||
_ = tokio::time::timeout(duration, watcher).await;
|
||||
}
|
||||
let default = Duration::from_secs(30);
|
||||
let duration = cmp::min(body.timeout.unwrap_or(default), default);
|
||||
_ = tokio::time::timeout(duration, watcher).await;
|
||||
}
|
||||
|
||||
Ok(sync_events::v4::Response {
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use ruma::api::client::thirdparty::get_protocols;
|
||||
|
||||
use crate::{Result, Ruma};
|
||||
use crate::{Result, Ruma, RumaResponse};
|
||||
|
||||
/// # `GET /_matrix/client/r0/thirdparty/protocols`
|
||||
///
|
||||
@@ -15,3 +15,13 @@ pub(crate) async fn get_protocols_route(
|
||||
protocols: BTreeMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/unstable/thirdparty/protocols`
|
||||
///
|
||||
/// Same as `get_protocols_route`, except for some reason Element Android legacy
|
||||
/// calls this
|
||||
pub(crate) async fn get_protocols_route_unstable(
|
||||
body: Ruma<get_protocols::v3::Request>,
|
||||
) -> Result<RumaResponse<get_protocols::v3::Response>> {
|
||||
get_protocols_route(body).await.map(RumaResponse)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::{warn, RumaResponse};
|
||||
use conduit::warn;
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, membership::mutual_rooms, room::get_summary},
|
||||
events::room::member::MembershipState,
|
||||
OwnedRoomId,
|
||||
};
|
||||
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma, RumaResponse};
|
||||
|
||||
/// # `GET /_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`
|
||||
///
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ extern crate conduit_service as service;
|
||||
pub(crate) use conduit::{debug_info, debug_warn, utils, Error, Result};
|
||||
pub(crate) use service::{pdu::PduEvent, services, user_is_local};
|
||||
|
||||
pub(crate) use crate::router::{Ruma, RumaResponse};
|
||||
pub(crate) use self::router::{Ruma, RumaResponse};
|
||||
|
||||
conduit::mod_ctor! {}
|
||||
conduit::mod_dtor! {}
|
||||
|
||||
@@ -6,6 +6,7 @@ use axum_extra::{
|
||||
typed_header::TypedHeaderRejectionReason,
|
||||
TypedHeader,
|
||||
};
|
||||
use conduit::Err;
|
||||
use http::uri::PathAndQuery;
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, AuthScheme, Metadata},
|
||||
@@ -183,7 +184,7 @@ fn auth_appservice(request: &Request, info: Box<RegistrationInfo>) -> Result<Aut
|
||||
|
||||
async fn auth_server(request: &mut Request, json_body: &Option<CanonicalJsonValue>) -> Result<Auth> {
|
||||
if !services().globals.allow_federation() {
|
||||
return Err(Error::bad_config("Federation is disabled."));
|
||||
return Err!(Config("allow_federation", "Federation is disabled."));
|
||||
}
|
||||
|
||||
let TypedHeader(Authorization(x_matrix)) = request
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
mod auth;
|
||||
mod handler;
|
||||
mod request;
|
||||
mod response;
|
||||
|
||||
use std::{mem, ops::Deref};
|
||||
|
||||
use axum::{async_trait, body::Body, extract::FromRequest};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
pub(super) use conduit::error::RumaResponse;
|
||||
use conduit::{debug, debug_warn, trace, warn};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, IncomingRequest},
|
||||
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
||||
};
|
||||
|
||||
pub(super) use self::handler::RouterExt;
|
||||
use self::{auth::Auth, request::Request};
|
||||
pub(super) use self::{handler::RouterExt, response::RumaResponse};
|
||||
use crate::{service::appservice::RegistrationInfo, services, Error, Result};
|
||||
|
||||
/// Extractor for Ruma request structs
|
||||
|
||||
@@ -29,12 +29,7 @@ pub(super) async fn from(request: hyper::Request<axum::body::Body>) -> Result<Re
|
||||
let query = serde_html_form::from_str(parts.uri.query().unwrap_or_default())
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::Unknown, "Failed to read query parameters"))?;
|
||||
|
||||
let max_body_size = services()
|
||||
.globals
|
||||
.config
|
||||
.max_request_size
|
||||
.try_into()
|
||||
.expect("failed to convert max request size");
|
||||
let max_body_size = services().globals.config.max_request_size;
|
||||
|
||||
let body = axum::body::to_bytes(body, max_body_size)
|
||||
.await
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use bytes::BytesMut;
|
||||
use conduit::{error, Error};
|
||||
use http::StatusCode;
|
||||
use http_body_util::Full;
|
||||
use ruma::api::{client::uiaa::UiaaResponse, OutgoingResponse};
|
||||
|
||||
pub(crate) struct RumaResponse<T>(pub(crate) T);
|
||||
|
||||
impl From<Error> for RumaResponse<UiaaResponse> {
|
||||
fn from(t: Error) -> Self { Self(t.into()) }
|
||||
}
|
||||
|
||||
impl<T: OutgoingResponse> IntoResponse for RumaResponse<T> {
|
||||
fn into_response(self) -> Response {
|
||||
self.0
|
||||
.try_into_http_response::<BytesMut>()
|
||||
.inspect_err(|e| error!("response error: {e}"))
|
||||
.map_or_else(
|
||||
|_| StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
||||
|r| r.map(BytesMut::freeze).map(Full::new).into_response(),
|
||||
)
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -3,7 +3,7 @@ use axum::{
|
||||
routing::{any, get, post},
|
||||
Router,
|
||||
};
|
||||
use conduit::{Error, Server};
|
||||
use conduit::{err, Error, Server};
|
||||
use http::Uri;
|
||||
use ruma::api::client::error::ErrorKind;
|
||||
|
||||
@@ -94,6 +94,8 @@ pub fn build(router: Router, server: &Server) -> Router {
|
||||
.ruma_route(client::search_users_route)
|
||||
.ruma_route(client::get_member_events_route)
|
||||
.ruma_route(client::get_protocols_route)
|
||||
.route("/_matrix/client/unstable/thirdparty/protocols",
|
||||
get(client::get_protocols_route_unstable))
|
||||
.ruma_route(client::send_message_event_route)
|
||||
.ruma_route(client::send_state_event_for_key_route)
|
||||
.ruma_route(client::get_state_events_route)
|
||||
@@ -234,4 +236,4 @@ async fn initial_sync(_uri: Uri) -> impl IntoResponse {
|
||||
Error::BadRequest(ErrorKind::GuestAccessForbidden, "Guest access not implemented")
|
||||
}
|
||||
|
||||
async fn federation_disabled() -> impl IntoResponse { Error::bad_config("Federation is disabled.") }
|
||||
async fn federation_disabled() -> impl IntoResponse { err!(Config("allow_federation", "Federation is disabled.")) }
|
||||
|
||||
+26
-37
@@ -7,7 +7,7 @@ use ruma::{
|
||||
},
|
||||
StateEventType, TimelineEventType,
|
||||
},
|
||||
RoomId, RoomVersionId, UserId,
|
||||
CanonicalJsonObject, RoomId, RoomVersionId, UserId,
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
use tracing::warn;
|
||||
@@ -71,11 +71,7 @@ pub(crate) async fn create_join_event_template_route(
|
||||
|
||||
let room_version_id = services().rooms.state.get_room_version(&body.room_id)?;
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
let join_authorized_via_users_server = if (services()
|
||||
.rooms
|
||||
@@ -148,27 +144,7 @@ pub(crate) async fn create_join_event_template_route(
|
||||
drop(state_lock);
|
||||
|
||||
// room v3 and above removed the "event_id" field from remote PDU format
|
||||
match room_version_id {
|
||||
RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
| RoomVersionId::V7
|
||||
| RoomVersionId::V8
|
||||
| RoomVersionId::V9
|
||||
| RoomVersionId::V10
|
||||
| RoomVersionId::V11 => {
|
||||
pdu_json.remove("event_id");
|
||||
},
|
||||
_ => {
|
||||
warn!("Unexpected or unsupported room version {room_version_id}");
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::BadJson,
|
||||
"Unexpected or unsupported room version found",
|
||||
));
|
||||
},
|
||||
};
|
||||
maybe_strip_event_id(&mut pdu_json, &room_version_id)?;
|
||||
|
||||
Ok(prepare_join_event::v1::Response {
|
||||
room_version: Some(room_version_id),
|
||||
@@ -183,6 +159,8 @@ pub(crate) async fn create_join_event_template_route(
|
||||
pub(crate) fn user_can_perform_restricted_join(
|
||||
user_id: &UserId, room_id: &RoomId, room_version_id: &RoomVersionId,
|
||||
) -> Result<bool> {
|
||||
use RoomVersionId::*;
|
||||
|
||||
let join_rules_event =
|
||||
services()
|
||||
.rooms
|
||||
@@ -202,16 +180,7 @@ pub(crate) fn user_can_perform_restricted_join(
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
if matches!(
|
||||
room_version_id,
|
||||
RoomVersionId::V1
|
||||
| RoomVersionId::V2
|
||||
| RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
| RoomVersionId::V7
|
||||
) {
|
||||
if matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6 | V7) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@@ -243,3 +212,23 @@ pub(crate) fn user_can_perform_restricted_join(
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_strip_event_id(pdu_json: &mut CanonicalJsonObject, room_version_id: &RoomVersionId) -> Result<()> {
|
||||
use RoomVersionId::*;
|
||||
|
||||
match room_version_id {
|
||||
V1 | V2 => {},
|
||||
V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 | V11 => {
|
||||
pdu_json.remove("event_id");
|
||||
},
|
||||
_ => {
|
||||
warn!("Unexpected or unsupported room version {room_version_id}");
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::BadJson,
|
||||
"Unexpected or unsupported room version found",
|
||||
));
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
use conduit::{Error, Result};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::membership::prepare_leave_event},
|
||||
events::{
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
TimelineEventType,
|
||||
},
|
||||
RoomVersionId,
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
|
||||
use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma};
|
||||
use super::make_join::maybe_strip_event_id;
|
||||
use crate::{service::pdu::PduBuilder, services, Ruma};
|
||||
|
||||
/// # `PUT /_matrix/federation/v1/make_leave/{roomId}/{eventId}`
|
||||
///
|
||||
@@ -35,11 +36,7 @@ pub(crate) async fn create_leave_event_template_route(
|
||||
.acl_check(origin, &body.room_id)?;
|
||||
|
||||
let room_version_id = services().rooms.state.get_room_version(&body.room_id)?;
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
let state_lock = services().rooms.state.mutex.lock(&body.room_id).await;
|
||||
let content = to_raw_value(&RoomMemberEventContent {
|
||||
avatar_url: None,
|
||||
blurhash: None,
|
||||
@@ -68,26 +65,7 @@ pub(crate) async fn create_leave_event_template_route(
|
||||
drop(state_lock);
|
||||
|
||||
// room v3 and above removed the "event_id" field from remote PDU format
|
||||
match room_version_id {
|
||||
RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
| RoomVersionId::V7
|
||||
| RoomVersionId::V8
|
||||
| RoomVersionId::V9
|
||||
| RoomVersionId::V10
|
||||
| RoomVersionId::V11 => {
|
||||
pdu_json.remove("event_id");
|
||||
},
|
||||
_ => {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::BadJson,
|
||||
"Unexpected or unsupported room version found",
|
||||
));
|
||||
},
|
||||
};
|
||||
maybe_strip_event_id(&mut pdu_json, &room_version_id)?;
|
||||
|
||||
Ok(prepare_leave_event::v1::Response {
|
||||
room_version: Some(room_version_id),
|
||||
|
||||
@@ -84,7 +84,7 @@ pub(crate) async fn send_transaction_message_route(
|
||||
Ok(send_transaction_message::v1::Response {
|
||||
pdus: resolved_map
|
||||
.into_iter()
|
||||
.map(|(e, r)| (e, r.map_err(|e| e.sanitized_error())))
|
||||
.map(|(e, r)| (e, r.map_err(|e| e.sanitized_string())))
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
@@ -127,8 +127,9 @@ async fn handle_pdus(
|
||||
for (event_id, value, room_id) in parsed_pdus {
|
||||
let pdu_start_time = Instant::now();
|
||||
let mutex_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
.rooms
|
||||
.event_handler
|
||||
.mutex_federation
|
||||
.lock(&room_id)
|
||||
.await;
|
||||
resolved_map.insert(
|
||||
|
||||
@@ -156,8 +156,9 @@ async fn create_join_event(
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "origin is not a server name."))?;
|
||||
|
||||
let mutex_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
.rooms
|
||||
.event_handler
|
||||
.mutex_federation
|
||||
.lock(room_id)
|
||||
.await;
|
||||
let pdu_id: Vec<u8> = services()
|
||||
|
||||
@@ -152,8 +152,9 @@ async fn create_leave_event(origin: &ServerName, room_id: &RoomId, pdu: &RawJson
|
||||
.await?;
|
||||
|
||||
let mutex_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
.rooms
|
||||
.event_handler
|
||||
.mutex_federation
|
||||
.lock(room_id)
|
||||
.await;
|
||||
let pdu_id: Vec<u8> = services()
|
||||
|
||||
@@ -53,7 +53,9 @@ sha256_media = []
|
||||
argon2.workspace = true
|
||||
axum.workspace = true
|
||||
bytes.workspace = true
|
||||
checked_ops.workspace = true
|
||||
chrono.workspace = true
|
||||
const-str.workspace = true
|
||||
either.workspace = true
|
||||
figment.workspace = true
|
||||
http-body-util.workspace = true
|
||||
@@ -80,6 +82,7 @@ tikv-jemalloc-ctl.workspace = true
|
||||
tikv-jemalloc-sys.optional = true
|
||||
tikv-jemalloc-sys.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-metrics.workspace = true
|
||||
tracing-core.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! Default allocator with no special features
|
||||
|
||||
/// Always returns the empty string
|
||||
/// Always returns None
|
||||
#[must_use]
|
||||
pub fn memory_stats() -> String { String::default() }
|
||||
pub fn memory_stats() -> Option<String> { None }
|
||||
|
||||
/// Always returns the empty string
|
||||
/// Always returns None
|
||||
#[must_use]
|
||||
pub fn memory_usage() -> String { String::default() }
|
||||
pub fn memory_usage() -> Option<String> { None }
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
static HMALLOC: hardened_malloc_rs::HardenedMalloc = hardened_malloc_rs::HardenedMalloc;
|
||||
|
||||
#[must_use]
|
||||
pub fn memory_usage() -> String {
|
||||
String::default() //TODO: get usage
|
||||
}
|
||||
//TODO: get usage
|
||||
pub fn memory_usage() -> Option<string> { None }
|
||||
|
||||
#[must_use]
|
||||
pub fn memory_stats() -> String { "Extended statistics are not available from hardened_malloc.".to_owned() }
|
||||
pub fn memory_stats() -> Option<String> {
|
||||
Some("Extended statistics are not available from hardened_malloc.".to_owned())
|
||||
}
|
||||
|
||||
+22
-13
@@ -10,22 +10,31 @@ use tikv_jemallocator as jemalloc;
|
||||
static JEMALLOC: jemalloc::Jemalloc = jemalloc::Jemalloc;
|
||||
|
||||
#[must_use]
|
||||
pub fn memory_usage() -> String {
|
||||
pub fn memory_usage() -> Option<String> {
|
||||
use mallctl::stats;
|
||||
let allocated = stats::allocated::read().unwrap_or_default() as f64 / 1024.0 / 1024.0;
|
||||
let active = stats::active::read().unwrap_or_default() as f64 / 1024.0 / 1024.0;
|
||||
let mapped = stats::mapped::read().unwrap_or_default() as f64 / 1024.0 / 1024.0;
|
||||
let metadata = stats::metadata::read().unwrap_or_default() as f64 / 1024.0 / 1024.0;
|
||||
let resident = stats::resident::read().unwrap_or_default() as f64 / 1024.0 / 1024.0;
|
||||
let retained = stats::retained::read().unwrap_or_default() as f64 / 1024.0 / 1024.0;
|
||||
format!(
|
||||
"allocated: {allocated:.2} MiB\n active: {active:.2} MiB\n mapped: {mapped:.2} MiB\n metadata: {metadata:.2} \
|
||||
MiB\n resident: {resident:.2} MiB\n retained: {retained:.2} MiB\n "
|
||||
)
|
||||
|
||||
let mibs = |input: Result<usize, mallctl::Error>| {
|
||||
let input = input.unwrap_or_default();
|
||||
let kibs = input / 1024;
|
||||
let kibs = u32::try_from(kibs).unwrap_or_default();
|
||||
let kibs = f64::from(kibs);
|
||||
kibs / 1024.0
|
||||
};
|
||||
|
||||
let allocated = mibs(stats::allocated::read());
|
||||
let active = mibs(stats::active::read());
|
||||
let mapped = mibs(stats::mapped::read());
|
||||
let metadata = mibs(stats::metadata::read());
|
||||
let resident = mibs(stats::resident::read());
|
||||
let retained = mibs(stats::retained::read());
|
||||
Some(format!(
|
||||
"allocated: {allocated:.2} MiB\nactive: {active:.2} MiB\nmapped: {mapped:.2} MiB\nmetadata: {metadata:.2} \
|
||||
MiB\nresident: {resident:.2} MiB\nretained: {retained:.2} MiB\n"
|
||||
))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn memory_stats() -> String {
|
||||
pub fn memory_stats() -> Option<String> {
|
||||
const MAX_LENGTH: usize = 65536 - 4096;
|
||||
|
||||
let opts_s = "d";
|
||||
@@ -42,7 +51,7 @@ pub fn memory_stats() -> String {
|
||||
unsafe { ffi::malloc_stats_print(Some(malloc_stats_cb), opaque, opts_p) };
|
||||
|
||||
str.truncate(MAX_LENGTH);
|
||||
format!("<pre><code>{str}</code></pre>")
|
||||
Some(format!("<pre><code>{str}</code></pre>"))
|
||||
}
|
||||
|
||||
extern "C" fn malloc_stats_cb(opaque: *mut c_void, msg: *const c_char) {
|
||||
|
||||
+162
-95
@@ -1,110 +1,126 @@
|
||||
#[cfg(unix)]
|
||||
use std::path::Path; // not unix specific, just only for UNIX sockets stuff and *nix container checks
|
||||
use figment::Figment;
|
||||
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
use crate::{error::Error, Config};
|
||||
|
||||
pub fn check(config: &Config) -> Result<(), Error> {
|
||||
#[cfg(feature = "rocksdb")]
|
||||
warn!(
|
||||
"Note the rocksdb feature was deleted from conduwuit, sqlite was deleted and RocksDB is the only supported \
|
||||
backend now. Please update your build script to remove this feature."
|
||||
);
|
||||
#[cfg(feature = "sha256_media")]
|
||||
warn!(
|
||||
"Note the sha256_media feature was deleted from conduwuit, it is now fully integrated in a \
|
||||
forwards-compatible way. Please update your build script to remove this feature."
|
||||
);
|
||||
|
||||
config.warn_deprecated();
|
||||
config.warn_unknown_key();
|
||||
|
||||
if config.sentry && config.sentry_endpoint.is_none() {
|
||||
return Err(Error::bad_config("Sentry cannot be enabled without an endpoint set"));
|
||||
}
|
||||
|
||||
if cfg!(feature = "hardened_malloc") && cfg!(feature = "jemalloc") {
|
||||
warn!("hardened_malloc and jemalloc are both enabled, this causes jemalloc to be used.");
|
||||
}
|
||||
|
||||
if config.unix_socket_path.is_some() && !cfg!(unix) {
|
||||
return Err(Error::bad_config(
|
||||
"UNIX socket support is only available on *nix platforms. Please remove \"unix_socket_path\" from your \
|
||||
config.",
|
||||
));
|
||||
}
|
||||
|
||||
config.get_bind_addrs().iter().for_each(|addr| {
|
||||
if addr.ip().is_loopback() && cfg!(unix) {
|
||||
debug!("Found loopback listening address {addr}, running checks if we're in a container.",);
|
||||
|
||||
#[cfg(unix)]
|
||||
if Path::new("/proc/vz").exists() /* Guest */ && !Path::new("/proc/bz").exists()
|
||||
/* Host */
|
||||
{
|
||||
error!(
|
||||
"You are detected using OpenVZ with a loopback/localhost listening address of {addr}. If you are \
|
||||
using OpenVZ for containers and you use NAT-based networking to communicate with the host and \
|
||||
guest, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, you can \
|
||||
ignore.",
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if Path::new("/.dockerenv").exists() {
|
||||
error!(
|
||||
"You are detected using Docker with a loopback/localhost listening address of {addr}. If you are \
|
||||
using a reverse proxy on the host and require communication to conduwuit in the Docker container \
|
||||
via NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is \
|
||||
expected, you can ignore.",
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if Path::new("/run/.containerenv").exists() {
|
||||
error!(
|
||||
"You are detected using Podman with a loopback/localhost listening address of {addr}. If you are \
|
||||
using a reverse proxy on the host and require communication to conduwuit in the Podman container \
|
||||
via NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is \
|
||||
expected, you can ignore.",
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// rocksdb does not allow max_log_files to be 0
|
||||
if config.rocksdb_max_log_files == 0 {
|
||||
return Err(Error::bad_config(
|
||||
"When using RocksDB, rocksdb_max_log_files cannot be 0. Please set a value at least 1.",
|
||||
));
|
||||
}
|
||||
|
||||
// yeah, unless the user built a debug build hopefully for local testing only
|
||||
if config.server_name == "your.server.name" && !cfg!(debug_assertions) {
|
||||
return Err(Error::bad_config(
|
||||
"You must specify a valid server name for production usage of conduwuit.",
|
||||
));
|
||||
}
|
||||
use super::DEPRECATED_KEYS;
|
||||
use crate::{debug, debug_info, error, info, warn, Config, Err, Result};
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn check(config: &Config) -> Result<()> {
|
||||
if cfg!(debug_assertions) {
|
||||
info!("Note: conduwuit was built without optimisations (i.e. debug build)");
|
||||
}
|
||||
|
||||
// prevents catching this in `--all-features`
|
||||
if cfg!(all(feature = "rocksdb", not(feature = "sha256_media"))) {
|
||||
warn!(
|
||||
"Note the rocksdb feature was deleted from conduwuit. SQLite support was removed and RocksDB is the only \
|
||||
supported backend now. Please update your build script to remove this feature."
|
||||
);
|
||||
}
|
||||
|
||||
// prevents catching this in `--all-features`
|
||||
if cfg!(all(feature = "sha256_media", not(feature = "rocksdb"))) {
|
||||
warn!(
|
||||
"Note the sha256_media feature was deleted from conduwuit, it is now fully integrated in a \
|
||||
forwards-compatible way. Please update your build script to remove this feature."
|
||||
);
|
||||
}
|
||||
|
||||
warn_deprecated(config);
|
||||
warn_unknown_key(config);
|
||||
|
||||
if config.sentry && config.sentry_endpoint.is_none() {
|
||||
return Err!(Config("sentry_endpoint", "Sentry cannot be enabled without an endpoint set"));
|
||||
}
|
||||
|
||||
if cfg!(all(feature = "hardened_malloc", feature = "jemalloc")) {
|
||||
warn!(
|
||||
"hardened_malloc and jemalloc are both enabled, this causes jemalloc to be used. If using --all-features, \
|
||||
this is harmless."
|
||||
);
|
||||
}
|
||||
|
||||
if cfg!(not(unix)) && config.unix_socket_path.is_some() {
|
||||
return Err!(Config(
|
||||
"unix_socket_path",
|
||||
"UNIX socket support is only available on *nix platforms. Please remove 'unix_socket_path' from your \
|
||||
config."
|
||||
));
|
||||
}
|
||||
|
||||
if cfg!(unix) && config.unix_socket_path.is_none() {
|
||||
config.get_bind_addrs().iter().for_each(|addr| {
|
||||
use std::path::Path;
|
||||
|
||||
if addr.ip().is_loopback() {
|
||||
debug_info!("Found loopback listening address {addr}, running checks if we're in a container.");
|
||||
|
||||
if Path::new("/proc/vz").exists() /* Guest */ && !Path::new("/proc/bz").exists()
|
||||
/* Host */
|
||||
{
|
||||
error!(
|
||||
"You are detected using OpenVZ with a loopback/localhost listening address of {addr}. If you \
|
||||
are using OpenVZ for containers and you use NAT-based networking to communicate with the \
|
||||
host and guest, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \
|
||||
you can ignore.",
|
||||
);
|
||||
}
|
||||
|
||||
if Path::new("/.dockerenv").exists() {
|
||||
error!(
|
||||
"You are detected using Docker with a loopback/localhost listening address of {addr}. If you \
|
||||
are using a reverse proxy on the host and require communication to conduwuit in the Docker \
|
||||
container via NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". \
|
||||
If this is expected, you can ignore.",
|
||||
);
|
||||
}
|
||||
|
||||
if Path::new("/run/.containerenv").exists() {
|
||||
error!(
|
||||
"You are detected using Podman with a loopback/localhost listening address of {addr}. If you \
|
||||
are using a reverse proxy on the host and require communication to conduwuit in the Podman \
|
||||
container via NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". \
|
||||
If this is expected, you can ignore.",
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// rocksdb does not allow max_log_files to be 0
|
||||
if config.rocksdb_max_log_files == 0 {
|
||||
return Err!(Config(
|
||||
"max_log_files",
|
||||
"rocksdb_max_log_files cannot be 0. Please set a value at least 1."
|
||||
));
|
||||
}
|
||||
|
||||
// yeah, unless the user built a debug build hopefully for local testing only
|
||||
if cfg!(not(debug_assertions)) && config.server_name == "your.server.name" {
|
||||
return Err!(Config(
|
||||
"server_name",
|
||||
"You must specify a valid server name for production usage of conduwuit."
|
||||
));
|
||||
}
|
||||
|
||||
// check if the user specified a registration token as `""`
|
||||
if config.registration_token == Some(String::new()) {
|
||||
return Err(Error::bad_config("Registration token was specified but is empty (\"\")"));
|
||||
return Err!(Config(
|
||||
"registration_token",
|
||||
"Registration token was specified but is empty (\"\")"
|
||||
));
|
||||
}
|
||||
|
||||
if config.max_request_size < 5_120_000 {
|
||||
return Err(Error::bad_config("Max request size is less than 5MB. Please increase it."));
|
||||
return Err!(Config(
|
||||
"max_request_size",
|
||||
"Max request size is less than 5MB. Please increase it."
|
||||
));
|
||||
}
|
||||
|
||||
// check if user specified valid IP CIDR ranges on startup
|
||||
for cidr in &config.ip_range_denylist {
|
||||
if let Err(e) = ipaddress::IPAddress::parse(cidr) {
|
||||
error!("Error parsing specified IP CIDR range from string: {e}");
|
||||
return Err(Error::bad_config("Error parsing specified IP CIDR ranges from strings"));
|
||||
return Err!(Config("ip_range_denylist", "Parsing specified IP CIDR range from string: {e}."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,13 +128,14 @@ pub fn check(config: &Config) -> Result<(), Error> {
|
||||
&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
&& config.registration_token.is_none()
|
||||
{
|
||||
return Err(Error::bad_config(
|
||||
return Err!(Config(
|
||||
"registration_token",
|
||||
"!! You have `allow_registration` enabled without a token configured in your config which means you are \
|
||||
allowing ANYONE to register on your conduwuit instance without any 2nd-step (e.g. registration token).\n
|
||||
If this is not the intended behaviour, please set a registration token with the `registration_token` config option.\n
|
||||
For security and safety reasons, conduwuit will shut down. If you are extra sure this is the desired behaviour you \
|
||||
want, please set the following config option to true:
|
||||
`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`",
|
||||
`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -135,8 +152,9 @@ For security and safety reasons, conduwuit will shut down. If you are extra sure
|
||||
}
|
||||
|
||||
if config.allow_outgoing_presence && !config.allow_local_presence {
|
||||
return Err(Error::bad_config(
|
||||
"Outgoing presence requires allowing local presence. Please enable \"allow_local_presence\".",
|
||||
return Err!(Config(
|
||||
"allow_local_presence",
|
||||
"Outgoing presence requires allowing local presence. Please enable 'allow_local_presence'."
|
||||
));
|
||||
}
|
||||
|
||||
@@ -173,3 +191,52 @@ For security and safety reasons, conduwuit will shut down. If you are extra sure
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Iterates over all the keys in the config file and warns if there is a
|
||||
/// deprecated key specified
|
||||
fn warn_deprecated(config: &Config) {
|
||||
debug!("Checking for deprecated config keys");
|
||||
let mut was_deprecated = false;
|
||||
for key in config
|
||||
.catchall
|
||||
.keys()
|
||||
.filter(|key| DEPRECATED_KEYS.iter().any(|s| s == key))
|
||||
{
|
||||
warn!("Config parameter \"{}\" is deprecated, ignoring.", key);
|
||||
was_deprecated = true;
|
||||
}
|
||||
|
||||
if was_deprecated {
|
||||
warn!(
|
||||
"Read conduwuit config documentation at https://conduwuit.puppyirl.gay/configuration.html and check your \
|
||||
configuration if any new configuration parameters should be adjusted"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// iterates over all the catchall keys (unknown config options) and warns
|
||||
/// if there are any.
|
||||
fn warn_unknown_key(config: &Config) {
|
||||
debug!("Checking for unknown config keys");
|
||||
for key in config
|
||||
.catchall
|
||||
.keys()
|
||||
.filter(|key| "config".to_owned().ne(key.to_owned()) /* "config" is expected */)
|
||||
{
|
||||
warn!("Config parameter \"{}\" is unknown to conduwuit, ignoring.", key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the presence of the `address` and `unix_socket_path` keys in the
|
||||
/// raw_config, exiting the process if both keys were detected.
|
||||
pub(super) fn is_dual_listening(raw_config: &Figment) -> Result<()> {
|
||||
let contains_address = raw_config.contains("address");
|
||||
let contains_unix_socket = raw_config.contains("unix_socket_path");
|
||||
if contains_address && contains_unix_socket {
|
||||
return Err!(
|
||||
"TOML keys \"address\" and \"unix_socket_path\" were both defined. Please specify only one option."
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+396
-435
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fmt::{self, Write as _},
|
||||
fmt,
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
path::PathBuf,
|
||||
};
|
||||
@@ -19,30 +19,15 @@ use ruma::{
|
||||
api::client::discovery::discover_support::ContactRole, OwnedRoomId, OwnedServerName, OwnedUserId, RoomVersionId,
|
||||
};
|
||||
use serde::{de::IgnoredAny, Deserialize};
|
||||
use tracing::{debug, error, warn};
|
||||
use url::Url;
|
||||
|
||||
pub use self::check::check;
|
||||
use self::proxy::ProxyConfig;
|
||||
use crate::error::Error;
|
||||
use crate::{error::Error, Err, Result};
|
||||
|
||||
pub mod check;
|
||||
pub mod proxy;
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[serde(transparent)]
|
||||
struct ListeningPort {
|
||||
#[serde(with = "either::serde_untagged")]
|
||||
ports: Either<u16, Vec<u16>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[serde(transparent)]
|
||||
struct ListeningAddr {
|
||||
#[serde(with = "either::serde_untagged")]
|
||||
addrs: Either<IpAddr, Vec<IpAddr>>,
|
||||
}
|
||||
|
||||
/// all the config options for conduwuit
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
@@ -114,7 +99,7 @@ pub struct Config {
|
||||
pub ip_lookup_strategy: u8,
|
||||
|
||||
#[serde(default = "default_max_request_size")]
|
||||
pub max_request_size: u32,
|
||||
pub max_request_size: usize,
|
||||
#[serde(default = "default_max_fetch_prev_events")]
|
||||
pub max_fetch_prev_events: u16,
|
||||
|
||||
@@ -181,16 +166,14 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub well_known: WellKnownConfig,
|
||||
#[serde(default)]
|
||||
#[cfg(feature = "perf_measurements")]
|
||||
pub allow_jaeger: bool,
|
||||
#[serde(default = "default_jaeger_filter")]
|
||||
pub jaeger_filter: String,
|
||||
#[serde(default)]
|
||||
#[cfg(feature = "perf_measurements")]
|
||||
pub tracing_flame: bool,
|
||||
#[serde(default = "default_tracing_flame_filter")]
|
||||
#[cfg(feature = "perf_measurements")]
|
||||
pub tracing_flame_filter: String,
|
||||
#[serde(default = "default_tracing_flame_output_path")]
|
||||
#[cfg(feature = "perf_measurements")]
|
||||
pub tracing_flame_output_path: String,
|
||||
#[serde(default)]
|
||||
pub proxy: ProxyConfig,
|
||||
@@ -356,6 +339,14 @@ pub struct Config {
|
||||
pub sentry_send_server_name: bool,
|
||||
#[serde(default = "default_sentry_traces_sample_rate")]
|
||||
pub sentry_traces_sample_rate: f32,
|
||||
#[serde(default)]
|
||||
pub sentry_attach_stacktrace: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub sentry_send_panic: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub sentry_send_error: bool,
|
||||
#[serde(default = "default_sentry_filter")]
|
||||
pub sentry_filter: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub tokio_console: bool,
|
||||
@@ -386,6 +377,20 @@ pub struct WellKnownConfig {
|
||||
pub support_mxid: Option<OwnedUserId>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[serde(transparent)]
|
||||
struct ListeningPort {
|
||||
#[serde(with = "either::serde_untagged")]
|
||||
ports: Either<u16, Vec<u16>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[serde(transparent)]
|
||||
struct ListeningAddr {
|
||||
#[serde(with = "either::serde_untagged")]
|
||||
addrs: Either<IpAddr, Vec<IpAddr>>,
|
||||
}
|
||||
|
||||
const DEPRECATED_KEYS: &[&str] = &[
|
||||
"cache_capacity",
|
||||
"max_concurrent_requests",
|
||||
@@ -399,7 +404,7 @@ const DEPRECATED_KEYS: &[&str] = &[
|
||||
|
||||
impl Config {
|
||||
/// Initialize config
|
||||
pub fn new(path: Option<PathBuf>) -> Result<Self, Error> {
|
||||
pub fn new(path: Option<PathBuf>) -> Result<Self> {
|
||||
let raw_config = if let Some(config_file_env) = Env::var("CONDUIT_CONFIG") {
|
||||
Figment::new()
|
||||
.merge(Toml::file(config_file_env).nested())
|
||||
@@ -422,69 +427,16 @@ impl Config {
|
||||
};
|
||||
|
||||
let config = match raw_config.extract::<Self>() {
|
||||
Err(e) => return Err(Error::BadConfig(format!("{e}"))),
|
||||
Err(e) => return Err!("There was a problem with your configuration file: {e}"),
|
||||
Ok(config) => config,
|
||||
};
|
||||
|
||||
// don't start if we're listening on both UNIX sockets and TCP at same time
|
||||
if Self::is_dual_listening(&raw_config) {
|
||||
return Err(Error::bad_config("dual listening on UNIX and TCP sockets not allowed."));
|
||||
};
|
||||
check::is_dual_listening(&raw_config)?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Iterates over all the keys in the config file and warns if there is a
|
||||
/// deprecated key specified
|
||||
pub(crate) fn warn_deprecated(&self) {
|
||||
debug!("Checking for deprecated config keys");
|
||||
let mut was_deprecated = false;
|
||||
for key in self
|
||||
.catchall
|
||||
.keys()
|
||||
.filter(|key| DEPRECATED_KEYS.iter().any(|s| s == key))
|
||||
{
|
||||
warn!("Config parameter \"{}\" is deprecated, ignoring.", key);
|
||||
was_deprecated = true;
|
||||
}
|
||||
|
||||
if was_deprecated {
|
||||
warn!(
|
||||
"Read conduwuit config documentation at https://conduwuit.puppyirl.gay/configuration.html and check \
|
||||
your configuration if any new configuration parameters should be adjusted"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// iterates over all the catchall keys (unknown config options) and warns
|
||||
/// if there are any.
|
||||
pub(crate) fn warn_unknown_key(&self) {
|
||||
debug!("Checking for unknown config keys");
|
||||
for key in self
|
||||
.catchall
|
||||
.keys()
|
||||
.filter(|key| "config".to_owned().ne(key.to_owned()) /* "config" is expected */)
|
||||
{
|
||||
warn!("Config parameter \"{}\" is unknown to conduwuit, ignoring.", key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the presence of the `address` and `unix_socket_path` keys in the
|
||||
/// raw_config, exiting the process if both keys were detected.
|
||||
fn is_dual_listening(raw_config: &Figment) -> bool {
|
||||
let check_address = raw_config.find_value("address");
|
||||
let check_unix_socket = raw_config.find_value("unix_socket_path");
|
||||
|
||||
// are the check_address and check_unix_socket keys both Ok (specified) at the
|
||||
// same time?
|
||||
if check_address.is_ok() && check_unix_socket.is_ok() {
|
||||
error!("TOML keys \"address\" and \"unix_socket_path\" were both defined. Please specify only one option.");
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_bind_addrs(&self) -> Vec<SocketAddr> {
|
||||
let mut addrs = Vec::new();
|
||||
@@ -516,361 +468,358 @@ impl Config {
|
||||
|
||||
impl fmt::Display for Config {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Prepare a list of config values to show
|
||||
let lines = [
|
||||
("Server name", self.server_name.host()),
|
||||
("Database backend", &self.database_backend),
|
||||
("Database path", &self.database_path.to_string_lossy()),
|
||||
(
|
||||
"Database backup path",
|
||||
self.database_backup_path
|
||||
.as_ref()
|
||||
.map_or("", |path| path.to_str().unwrap_or("")),
|
||||
),
|
||||
("Database backups to keep", &self.database_backups_to_keep.to_string()),
|
||||
("Database cache capacity (MB)", &self.db_cache_capacity_mb.to_string()),
|
||||
("Cache capacity modifier", &self.conduit_cache_capacity_modifier.to_string()),
|
||||
("PDU cache capacity", &self.pdu_cache_capacity.to_string()),
|
||||
("Auth chain cache capacity", &self.auth_chain_cache_capacity.to_string()),
|
||||
("Short eventid cache capacity", &self.shorteventid_cache_capacity.to_string()),
|
||||
("Eventid short cache capacity", &self.eventidshort_cache_capacity.to_string()),
|
||||
("Short statekey cache capacity", &self.shortstatekey_cache_capacity.to_string()),
|
||||
("Statekey short cache capacity", &self.statekeyshort_cache_capacity.to_string()),
|
||||
(
|
||||
"Server visibility cache capacity",
|
||||
&self.server_visibility_cache_capacity.to_string(),
|
||||
),
|
||||
(
|
||||
"User visibility cache capacity",
|
||||
&self.user_visibility_cache_capacity.to_string(),
|
||||
),
|
||||
("Stateinfo cache capacity", &self.stateinfo_cache_capacity.to_string()),
|
||||
(
|
||||
"Roomid space hierarchy cache capacity",
|
||||
&self.roomid_spacehierarchy_cache_capacity.to_string(),
|
||||
),
|
||||
("DNS cache entry limit", &self.dns_cache_entries.to_string()),
|
||||
("DNS minimum TTL", &self.dns_min_ttl.to_string()),
|
||||
("DNS minimum NXDOMAIN TTL", &self.dns_min_ttl_nxdomain.to_string()),
|
||||
("DNS attempts", &self.dns_attempts.to_string()),
|
||||
("DNS timeout", &self.dns_timeout.to_string()),
|
||||
("DNS fallback to TCP", &self.dns_tcp_fallback.to_string()),
|
||||
("DNS query over TCP only", &self.query_over_tcp_only.to_string()),
|
||||
("Query all nameservers", &self.query_all_nameservers.to_string()),
|
||||
("Maximum request size (bytes)", &self.max_request_size.to_string()),
|
||||
("Sender retry backoff limit", &self.sender_retry_backoff_limit.to_string()),
|
||||
("Request connect timeout", &self.request_conn_timeout.to_string()),
|
||||
("Request timeout", &self.request_timeout.to_string()),
|
||||
("Request total timeout", &self.request_total_timeout.to_string()),
|
||||
("Idle connections per host", &self.request_idle_per_host.to_string()),
|
||||
("Request pool idle timeout", &self.request_idle_timeout.to_string()),
|
||||
("Well_known connect timeout", &self.well_known_conn_timeout.to_string()),
|
||||
("Well_known timeout", &self.well_known_timeout.to_string()),
|
||||
("Federation timeout", &self.federation_timeout.to_string()),
|
||||
("Federation pool idle per host", &self.federation_idle_per_host.to_string()),
|
||||
("Federation pool idle timeout", &self.federation_idle_timeout.to_string()),
|
||||
("Sender timeout", &self.sender_timeout.to_string()),
|
||||
("Sender pool idle timeout", &self.sender_idle_timeout.to_string()),
|
||||
("Appservice timeout", &self.appservice_timeout.to_string()),
|
||||
("Appservice pool idle timeout", &self.appservice_idle_timeout.to_string()),
|
||||
("Pusher pool idle timeout", &self.pusher_idle_timeout.to_string()),
|
||||
("Allow registration", &self.allow_registration.to_string()),
|
||||
(
|
||||
"Registration token",
|
||||
if self.registration_token.is_some() {
|
||||
"set"
|
||||
} else {
|
||||
"not set (open registration!)"
|
||||
},
|
||||
),
|
||||
(
|
||||
"Allow guest registration (inherently false if allow registration is false)",
|
||||
&self.allow_guest_registration.to_string(),
|
||||
),
|
||||
(
|
||||
"Log guest registrations in admin room",
|
||||
&self.log_guest_registrations.to_string(),
|
||||
),
|
||||
(
|
||||
"Allow guests to auto join rooms",
|
||||
&self.allow_guests_auto_join_rooms.to_string(),
|
||||
),
|
||||
("New user display name suffix", &self.new_user_displayname_suffix),
|
||||
("Allow encryption", &self.allow_encryption.to_string()),
|
||||
("Allow federation", &self.allow_federation.to_string()),
|
||||
(
|
||||
"Allow incoming federated presence requests (updates)",
|
||||
&self.allow_incoming_presence.to_string(),
|
||||
),
|
||||
(
|
||||
"Allow outgoing federated presence requests (updates)",
|
||||
&self.allow_outgoing_presence.to_string(),
|
||||
),
|
||||
(
|
||||
"Allow local presence requests (updates)",
|
||||
&self.allow_local_presence.to_string(),
|
||||
),
|
||||
(
|
||||
"Allow incoming remote read receipts",
|
||||
&self.allow_incoming_read_receipts.to_string(),
|
||||
),
|
||||
(
|
||||
"Allow outgoing remote read receipts",
|
||||
&self.allow_outgoing_read_receipts.to_string(),
|
||||
),
|
||||
(
|
||||
"Block non-admin room invites (local and remote, admins can still send and receive invites)",
|
||||
&self.block_non_admin_invites.to_string(),
|
||||
),
|
||||
("Enable admin escape commands", &self.admin_escape_commands.to_string()),
|
||||
("Allow outgoing federated typing", &self.allow_outgoing_typing.to_string()),
|
||||
("Allow incoming federated typing", &self.allow_incoming_typing.to_string()),
|
||||
(
|
||||
"Incoming federated typing timeout",
|
||||
&self.typing_federation_timeout_s.to_string(),
|
||||
),
|
||||
("Client typing timeout minimum", &self.typing_client_timeout_min_s.to_string()),
|
||||
("Client typing timeout maxmimum", &self.typing_client_timeout_max_s.to_string()),
|
||||
("Allow device name federation", &self.allow_device_name_federation.to_string()),
|
||||
(
|
||||
"Allow incoming profile lookup federation requests",
|
||||
&self.allow_profile_lookup_federation_requests.to_string(),
|
||||
),
|
||||
(
|
||||
"Auto deactivate banned room join attempts",
|
||||
&self.auto_deactivate_banned_room_attempts.to_string(),
|
||||
),
|
||||
("Notification push path", &self.notification_push_path),
|
||||
("Allow room creation", &self.allow_room_creation.to_string()),
|
||||
(
|
||||
"Allow public room directory over federation",
|
||||
&self.allow_public_room_directory_over_federation.to_string(),
|
||||
),
|
||||
(
|
||||
"Allow public room directory without authentication",
|
||||
&self.allow_public_room_directory_without_auth.to_string(),
|
||||
),
|
||||
(
|
||||
"Lockdown public room directory (only allow admins to publish)",
|
||||
&self.lockdown_public_room_directory.to_string(),
|
||||
),
|
||||
(
|
||||
"JWT secret",
|
||||
match self.jwt_secret {
|
||||
Some(_) => "set",
|
||||
None => "not set",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Trusted key servers",
|
||||
&self
|
||||
.trusted_servers
|
||||
.iter()
|
||||
.map(|server| server.host())
|
||||
.join(", "),
|
||||
),
|
||||
(
|
||||
"Query Trusted Key Servers First",
|
||||
&self.query_trusted_key_servers_first.to_string(),
|
||||
),
|
||||
("OpenID Token TTL", &self.openid_token_ttl.to_string()),
|
||||
(
|
||||
"TURN username",
|
||||
if self.turn_username.is_empty() {
|
||||
"not set"
|
||||
} else {
|
||||
&self.turn_username
|
||||
},
|
||||
),
|
||||
("TURN password", {
|
||||
if self.turn_password.is_empty() {
|
||||
"not set"
|
||||
} else {
|
||||
"set"
|
||||
}
|
||||
}),
|
||||
("TURN secret", {
|
||||
if self.turn_secret.is_empty() {
|
||||
"not set"
|
||||
} else {
|
||||
"set"
|
||||
}
|
||||
}),
|
||||
("Turn TTL", &self.turn_ttl.to_string()),
|
||||
("Turn URIs", {
|
||||
let mut lst = vec![];
|
||||
for item in self.turn_uris.iter().cloned().enumerate() {
|
||||
let (_, uri): (usize, String) = item;
|
||||
lst.push(uri);
|
||||
}
|
||||
&lst.join(", ")
|
||||
}),
|
||||
("Auto Join Rooms", {
|
||||
let mut lst = vec![];
|
||||
for room in &self.auto_join_rooms {
|
||||
lst.push(room);
|
||||
}
|
||||
&lst.into_iter().join(", ")
|
||||
}),
|
||||
#[cfg(feature = "zstd_compression")]
|
||||
("Zstd HTTP Compression", &self.zstd_compression.to_string()),
|
||||
#[cfg(feature = "gzip_compression")]
|
||||
("Gzip HTTP Compression", &self.gzip_compression.to_string()),
|
||||
#[cfg(feature = "brotli_compression")]
|
||||
("Brotli HTTP Compression", &self.brotli_compression.to_string()),
|
||||
("RocksDB database LOG level", &self.rocksdb_log_level),
|
||||
("RocksDB database LOG to stderr", &self.rocksdb_log_stderr.to_string()),
|
||||
("RocksDB database LOG time-to-roll", &self.rocksdb_log_time_to_roll.to_string()),
|
||||
("RocksDB Max LOG Files", &self.rocksdb_max_log_files.to_string()),
|
||||
(
|
||||
"RocksDB database max LOG file size",
|
||||
&self.rocksdb_max_log_file_size.to_string(),
|
||||
),
|
||||
(
|
||||
"RocksDB database optimize for spinning disks",
|
||||
&self.rocksdb_optimize_for_spinning_disks.to_string(),
|
||||
),
|
||||
("RocksDB Direct-IO", &self.rocksdb_direct_io.to_string()),
|
||||
("RocksDB Parallelism Threads", &self.rocksdb_parallelism_threads.to_string()),
|
||||
("RocksDB Compression Algorithm", &self.rocksdb_compression_algo),
|
||||
("RocksDB Compression Level", &self.rocksdb_compression_level.to_string()),
|
||||
(
|
||||
"RocksDB Bottommost Compression Level",
|
||||
&self.rocksdb_bottommost_compression_level.to_string(),
|
||||
),
|
||||
(
|
||||
"RocksDB Bottommost Level Compression",
|
||||
&self.rocksdb_bottommost_compression.to_string(),
|
||||
),
|
||||
("RocksDB Recovery Mode", &self.rocksdb_recovery_mode.to_string()),
|
||||
("RocksDB Repair Mode", &self.rocksdb_repair.to_string()),
|
||||
("RocksDB Read-only Mode", &self.rocksdb_read_only.to_string()),
|
||||
(
|
||||
"RocksDB Compaction Idle Priority",
|
||||
&self.rocksdb_compaction_prio_idle.to_string(),
|
||||
),
|
||||
(
|
||||
"RocksDB Compaction Idle IOPriority",
|
||||
&self.rocksdb_compaction_ioprio_idle.to_string(),
|
||||
),
|
||||
("Media integrity checks on startup", &self.media_startup_check.to_string()),
|
||||
("Media compatibility filesystem links", &self.media_compat_file_link.to_string()),
|
||||
("Prevent Media Downloads From", {
|
||||
let mut lst = vec![];
|
||||
for domain in &self.prevent_media_downloads_from {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
}),
|
||||
("Forbidden Remote Server Names (\"Global\" ACLs)", {
|
||||
let mut lst = vec![];
|
||||
for domain in &self.forbidden_remote_server_names {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
}),
|
||||
("Forbidden Remote Room Directory Server Names", {
|
||||
let mut lst = vec![];
|
||||
for domain in &self.forbidden_remote_room_directory_server_names {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
}),
|
||||
("Outbound Request IP Range Denylist", {
|
||||
let mut lst = vec![];
|
||||
for item in self.ip_range_denylist.iter().cloned().enumerate() {
|
||||
let (_, ip): (usize, String) = item;
|
||||
lst.push(ip);
|
||||
}
|
||||
&lst.join(", ")
|
||||
}),
|
||||
("Forbidden usernames", {
|
||||
&self.forbidden_usernames.patterns().iter().join(", ")
|
||||
}),
|
||||
("Forbidden room aliases", {
|
||||
&self.forbidden_alias_names.patterns().iter().join(", ")
|
||||
}),
|
||||
(
|
||||
"URL preview domain contains allowlist",
|
||||
&self.url_preview_domain_contains_allowlist.join(", "),
|
||||
),
|
||||
(
|
||||
"URL preview domain explicit allowlist",
|
||||
&self.url_preview_domain_explicit_allowlist.join(", "),
|
||||
),
|
||||
(
|
||||
"URL preview domain explicit denylist",
|
||||
&self.url_preview_domain_explicit_denylist.join(", "),
|
||||
),
|
||||
(
|
||||
"URL preview URL contains allowlist",
|
||||
&self.url_preview_url_contains_allowlist.join(", "),
|
||||
),
|
||||
("URL preview maximum spider size", &self.url_preview_max_spider_size.to_string()),
|
||||
("URL preview check root domain", &self.url_preview_check_root_domain.to_string()),
|
||||
(
|
||||
"Allow check for updates / announcements check",
|
||||
&self.allow_check_for_updates.to_string(),
|
||||
),
|
||||
("Enable netburst on startup", &self.startup_netburst.to_string()),
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
("Sentry.io reporting and tracing", &self.sentry.to_string()),
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
("Sentry.io send server_name in logs", &self.sentry_send_server_name.to_string()),
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
("Sentry.io tracing sample rate", &self.sentry_traces_sample_rate.to_string()),
|
||||
(
|
||||
"Well-known server name",
|
||||
self.well_known
|
||||
.server
|
||||
.as_ref()
|
||||
.map_or("", |server| server.as_str()),
|
||||
),
|
||||
(
|
||||
"Well-known client URL",
|
||||
self.well_known
|
||||
.client
|
||||
.as_ref()
|
||||
.map_or("", |url| url.as_str()),
|
||||
),
|
||||
(
|
||||
"Well-known support email",
|
||||
self.well_known
|
||||
.support_email
|
||||
.as_ref()
|
||||
.map_or("", |str| str.as_ref()),
|
||||
),
|
||||
(
|
||||
"Well-known support Matrix ID",
|
||||
self.well_known
|
||||
.support_mxid
|
||||
.as_ref()
|
||||
.map_or("", |mxid| mxid.as_str()),
|
||||
),
|
||||
(
|
||||
"Well-known support role",
|
||||
self.well_known
|
||||
.support_role
|
||||
.as_ref()
|
||||
.map_or("", |role| role.as_str()),
|
||||
),
|
||||
(
|
||||
"Well-known support page/URL",
|
||||
self.well_known
|
||||
.support_page
|
||||
.as_ref()
|
||||
.map_or("", |url| url.as_str()),
|
||||
),
|
||||
("Enable the tokio-console", &self.tokio_console.to_string()),
|
||||
];
|
||||
writeln!(f, "Active config values:\n\n").expect("wrote line to formatter stream");
|
||||
let mut line = |key: &str, val: &str| {
|
||||
writeln!(f, "{key}: {val}").expect("wrote line to formatter stream");
|
||||
};
|
||||
|
||||
let mut msg: String = "Active config values:\n\n".to_owned();
|
||||
line("Server name", self.server_name.host());
|
||||
line("Database backend", &self.database_backend);
|
||||
line("Database path", &self.database_path.to_string_lossy());
|
||||
line(
|
||||
"Database backup path",
|
||||
self.database_backup_path
|
||||
.as_ref()
|
||||
.map_or("", |path| path.to_str().unwrap_or("")),
|
||||
);
|
||||
line("Database backups to keep", &self.database_backups_to_keep.to_string());
|
||||
line("Database cache capacity (MB)", &self.db_cache_capacity_mb.to_string());
|
||||
line("Cache capacity modifier", &self.conduit_cache_capacity_modifier.to_string());
|
||||
line("PDU cache capacity", &self.pdu_cache_capacity.to_string());
|
||||
line("Auth chain cache capacity", &self.auth_chain_cache_capacity.to_string());
|
||||
line("Short eventid cache capacity", &self.shorteventid_cache_capacity.to_string());
|
||||
line("Eventid short cache capacity", &self.eventidshort_cache_capacity.to_string());
|
||||
line("Short statekey cache capacity", &self.shortstatekey_cache_capacity.to_string());
|
||||
line("Statekey short cache capacity", &self.statekeyshort_cache_capacity.to_string());
|
||||
line(
|
||||
"Server visibility cache capacity",
|
||||
&self.server_visibility_cache_capacity.to_string(),
|
||||
);
|
||||
line(
|
||||
"User visibility cache capacity",
|
||||
&self.user_visibility_cache_capacity.to_string(),
|
||||
);
|
||||
line("Stateinfo cache capacity", &self.stateinfo_cache_capacity.to_string());
|
||||
line(
|
||||
"Roomid space hierarchy cache capacity",
|
||||
&self.roomid_spacehierarchy_cache_capacity.to_string(),
|
||||
);
|
||||
line("DNS cache entry limit", &self.dns_cache_entries.to_string());
|
||||
line("DNS minimum TTL", &self.dns_min_ttl.to_string());
|
||||
line("DNS minimum NXDOMAIN TTL", &self.dns_min_ttl_nxdomain.to_string());
|
||||
line("DNS attempts", &self.dns_attempts.to_string());
|
||||
line("DNS timeout", &self.dns_timeout.to_string());
|
||||
line("DNS fallback to TCP", &self.dns_tcp_fallback.to_string());
|
||||
line("DNS query over TCP only", &self.query_over_tcp_only.to_string());
|
||||
line("Query all nameservers", &self.query_all_nameservers.to_string());
|
||||
line("Maximum request size (bytes)", &self.max_request_size.to_string());
|
||||
line("Sender retry backoff limit", &self.sender_retry_backoff_limit.to_string());
|
||||
line("Request connect timeout", &self.request_conn_timeout.to_string());
|
||||
line("Request timeout", &self.request_timeout.to_string());
|
||||
line("Request total timeout", &self.request_total_timeout.to_string());
|
||||
line("Idle connections per host", &self.request_idle_per_host.to_string());
|
||||
line("Request pool idle timeout", &self.request_idle_timeout.to_string());
|
||||
line("Well_known connect timeout", &self.well_known_conn_timeout.to_string());
|
||||
line("Well_known timeout", &self.well_known_timeout.to_string());
|
||||
line("Federation timeout", &self.federation_timeout.to_string());
|
||||
line("Federation pool idle per host", &self.federation_idle_per_host.to_string());
|
||||
line("Federation pool idle timeout", &self.federation_idle_timeout.to_string());
|
||||
line("Sender timeout", &self.sender_timeout.to_string());
|
||||
line("Sender pool idle timeout", &self.sender_idle_timeout.to_string());
|
||||
line("Appservice timeout", &self.appservice_timeout.to_string());
|
||||
line("Appservice pool idle timeout", &self.appservice_idle_timeout.to_string());
|
||||
line("Pusher pool idle timeout", &self.pusher_idle_timeout.to_string());
|
||||
line("Allow registration", &self.allow_registration.to_string());
|
||||
line(
|
||||
"Registration token",
|
||||
if self.registration_token.is_some() {
|
||||
"set"
|
||||
} else {
|
||||
"not set (open registration!)"
|
||||
},
|
||||
);
|
||||
line(
|
||||
"Allow guest registration (inherently false if allow registration is false)",
|
||||
&self.allow_guest_registration.to_string(),
|
||||
);
|
||||
line(
|
||||
"Log guest registrations in admin room",
|
||||
&self.log_guest_registrations.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow guests to auto join rooms",
|
||||
&self.allow_guests_auto_join_rooms.to_string(),
|
||||
);
|
||||
line("New user display name suffix", &self.new_user_displayname_suffix);
|
||||
line("Allow encryption", &self.allow_encryption.to_string());
|
||||
line("Allow federation", &self.allow_federation.to_string());
|
||||
line(
|
||||
"Allow incoming federated presence requests (updates)",
|
||||
&self.allow_incoming_presence.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow outgoing federated presence requests (updates)",
|
||||
&self.allow_outgoing_presence.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow local presence requests (updates)",
|
||||
&self.allow_local_presence.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow incoming remote read receipts",
|
||||
&self.allow_incoming_read_receipts.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow outgoing remote read receipts",
|
||||
&self.allow_outgoing_read_receipts.to_string(),
|
||||
);
|
||||
line(
|
||||
"Block non-admin room invites (local and remote, admins can still send and receive invites)",
|
||||
&self.block_non_admin_invites.to_string(),
|
||||
);
|
||||
line("Enable admin escape commands", &self.admin_escape_commands.to_string());
|
||||
line("Allow outgoing federated typing", &self.allow_outgoing_typing.to_string());
|
||||
line("Allow incoming federated typing", &self.allow_incoming_typing.to_string());
|
||||
line(
|
||||
"Incoming federated typing timeout",
|
||||
&self.typing_federation_timeout_s.to_string(),
|
||||
);
|
||||
line("Client typing timeout minimum", &self.typing_client_timeout_min_s.to_string());
|
||||
line("Client typing timeout maxmimum", &self.typing_client_timeout_max_s.to_string());
|
||||
line("Allow device name federation", &self.allow_device_name_federation.to_string());
|
||||
line(
|
||||
"Allow incoming profile lookup federation requests",
|
||||
&self.allow_profile_lookup_federation_requests.to_string(),
|
||||
);
|
||||
line(
|
||||
"Auto deactivate banned room join attempts",
|
||||
&self.auto_deactivate_banned_room_attempts.to_string(),
|
||||
);
|
||||
line("Notification push path", &self.notification_push_path);
|
||||
line("Allow room creation", &self.allow_room_creation.to_string());
|
||||
line(
|
||||
"Allow public room directory over federation",
|
||||
&self.allow_public_room_directory_over_federation.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow public room directory without authentication",
|
||||
&self.allow_public_room_directory_without_auth.to_string(),
|
||||
);
|
||||
line(
|
||||
"Lockdown public room directory (only allow admins to publish)",
|
||||
&self.lockdown_public_room_directory.to_string(),
|
||||
);
|
||||
line(
|
||||
"JWT secret",
|
||||
match self.jwt_secret {
|
||||
Some(_) => "set",
|
||||
None => "not set",
|
||||
},
|
||||
);
|
||||
line(
|
||||
"Trusted key servers",
|
||||
&self
|
||||
.trusted_servers
|
||||
.iter()
|
||||
.map(|server| server.host())
|
||||
.join(", "),
|
||||
);
|
||||
line(
|
||||
"Query Trusted Key Servers First",
|
||||
&self.query_trusted_key_servers_first.to_string(),
|
||||
);
|
||||
line("OpenID Token TTL", &self.openid_token_ttl.to_string());
|
||||
line(
|
||||
"TURN username",
|
||||
if self.turn_username.is_empty() {
|
||||
"not set"
|
||||
} else {
|
||||
&self.turn_username
|
||||
},
|
||||
);
|
||||
line("TURN password", {
|
||||
if self.turn_password.is_empty() {
|
||||
"not set"
|
||||
} else {
|
||||
"set"
|
||||
}
|
||||
});
|
||||
line("TURN secret", {
|
||||
if self.turn_secret.is_empty() {
|
||||
"not set"
|
||||
} else {
|
||||
"set"
|
||||
}
|
||||
});
|
||||
line("Turn TTL", &self.turn_ttl.to_string());
|
||||
line("Turn URIs", {
|
||||
let mut lst = vec![];
|
||||
for item in self.turn_uris.iter().cloned().enumerate() {
|
||||
let (_, uri): (usize, String) = item;
|
||||
lst.push(uri);
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Auto Join Rooms", {
|
||||
let mut lst = vec![];
|
||||
for room in &self.auto_join_rooms {
|
||||
lst.push(room);
|
||||
}
|
||||
&lst.into_iter().join(", ")
|
||||
});
|
||||
line("Zstd HTTP Compression", &self.zstd_compression.to_string());
|
||||
line("Gzip HTTP Compression", &self.gzip_compression.to_string());
|
||||
line("Brotli HTTP Compression", &self.brotli_compression.to_string());
|
||||
line("RocksDB database LOG level", &self.rocksdb_log_level);
|
||||
line("RocksDB database LOG to stderr", &self.rocksdb_log_stderr.to_string());
|
||||
line("RocksDB database LOG time-to-roll", &self.rocksdb_log_time_to_roll.to_string());
|
||||
line("RocksDB Max LOG Files", &self.rocksdb_max_log_files.to_string());
|
||||
line(
|
||||
"RocksDB database max LOG file size",
|
||||
&self.rocksdb_max_log_file_size.to_string(),
|
||||
);
|
||||
line(
|
||||
"RocksDB database optimize for spinning disks",
|
||||
&self.rocksdb_optimize_for_spinning_disks.to_string(),
|
||||
);
|
||||
line("RocksDB Direct-IO", &self.rocksdb_direct_io.to_string());
|
||||
line("RocksDB Parallelism Threads", &self.rocksdb_parallelism_threads.to_string());
|
||||
line("RocksDB Compression Algorithm", &self.rocksdb_compression_algo);
|
||||
line("RocksDB Compression Level", &self.rocksdb_compression_level.to_string());
|
||||
line(
|
||||
"RocksDB Bottommost Compression Level",
|
||||
&self.rocksdb_bottommost_compression_level.to_string(),
|
||||
);
|
||||
line(
|
||||
"RocksDB Bottommost Level Compression",
|
||||
&self.rocksdb_bottommost_compression.to_string(),
|
||||
);
|
||||
line("RocksDB Recovery Mode", &self.rocksdb_recovery_mode.to_string());
|
||||
line("RocksDB Repair Mode", &self.rocksdb_repair.to_string());
|
||||
line("RocksDB Read-only Mode", &self.rocksdb_read_only.to_string());
|
||||
line(
|
||||
"RocksDB Compaction Idle Priority",
|
||||
&self.rocksdb_compaction_prio_idle.to_string(),
|
||||
);
|
||||
line(
|
||||
"RocksDB Compaction Idle IOPriority",
|
||||
&self.rocksdb_compaction_ioprio_idle.to_string(),
|
||||
);
|
||||
line("Media integrity checks on startup", &self.media_startup_check.to_string());
|
||||
line("Media compatibility filesystem links", &self.media_compat_file_link.to_string());
|
||||
line("Prevent Media Downloads From", {
|
||||
let mut lst = vec![];
|
||||
for domain in &self.prevent_media_downloads_from {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Forbidden Remote Server Names (\"Global\" ACLs)", {
|
||||
let mut lst = vec![];
|
||||
for domain in &self.forbidden_remote_server_names {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Forbidden Remote Room Directory Server Names", {
|
||||
let mut lst = vec![];
|
||||
for domain in &self.forbidden_remote_room_directory_server_names {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Outbound Request IP Range Denylist", {
|
||||
let mut lst = vec![];
|
||||
for item in self.ip_range_denylist.iter().cloned().enumerate() {
|
||||
let (_, ip): (usize, String) = item;
|
||||
lst.push(ip);
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Forbidden usernames", {
|
||||
&self.forbidden_usernames.patterns().iter().join(", ")
|
||||
});
|
||||
line("Forbidden room aliases", {
|
||||
&self.forbidden_alias_names.patterns().iter().join(", ")
|
||||
});
|
||||
line(
|
||||
"URL preview domain contains allowlist",
|
||||
&self.url_preview_domain_contains_allowlist.join(", "),
|
||||
);
|
||||
line(
|
||||
"URL preview domain explicit allowlist",
|
||||
&self.url_preview_domain_explicit_allowlist.join(", "),
|
||||
);
|
||||
line(
|
||||
"URL preview domain explicit denylist",
|
||||
&self.url_preview_domain_explicit_denylist.join(", "),
|
||||
);
|
||||
line(
|
||||
"URL preview URL contains allowlist",
|
||||
&self.url_preview_url_contains_allowlist.join(", "),
|
||||
);
|
||||
line("URL preview maximum spider size", &self.url_preview_max_spider_size.to_string());
|
||||
line("URL preview check root domain", &self.url_preview_check_root_domain.to_string());
|
||||
line(
|
||||
"Allow check for updates / announcements check",
|
||||
&self.allow_check_for_updates.to_string(),
|
||||
);
|
||||
line("Enable netburst on startup", &self.startup_netburst.to_string());
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
line("Sentry.io reporting and tracing", &self.sentry.to_string());
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
line("Sentry.io send server_name in logs", &self.sentry_send_server_name.to_string());
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
line("Sentry.io tracing sample rate", &self.sentry_traces_sample_rate.to_string());
|
||||
line("Sentry.io attach stacktrace", &self.sentry_attach_stacktrace.to_string());
|
||||
line("Sentry.io send panics", &self.sentry_send_panic.to_string());
|
||||
line("Sentry.io send errors", &self.sentry_send_error.to_string());
|
||||
line("Sentry.io tracing filter", &self.sentry_filter);
|
||||
line(
|
||||
"Well-known server name",
|
||||
self.well_known
|
||||
.server
|
||||
.as_ref()
|
||||
.map_or("", |server| server.as_str()),
|
||||
);
|
||||
line(
|
||||
"Well-known client URL",
|
||||
self.well_known
|
||||
.client
|
||||
.as_ref()
|
||||
.map_or("", |url| url.as_str()),
|
||||
);
|
||||
line(
|
||||
"Well-known support email",
|
||||
self.well_known
|
||||
.support_email
|
||||
.as_ref()
|
||||
.map_or("", |str| str.as_ref()),
|
||||
);
|
||||
line(
|
||||
"Well-known support Matrix ID",
|
||||
self.well_known
|
||||
.support_mxid
|
||||
.as_ref()
|
||||
.map_or("", |mxid| mxid.as_str()),
|
||||
);
|
||||
line(
|
||||
"Well-known support role",
|
||||
self.well_known
|
||||
.support_role
|
||||
.as_ref()
|
||||
.map_or("", |role| role.as_str()),
|
||||
);
|
||||
line(
|
||||
"Well-known support page/URL",
|
||||
self.well_known
|
||||
.support_page
|
||||
.as_ref()
|
||||
.map_or("", |url| url.as_str()),
|
||||
);
|
||||
line("Enable the tokio-console", &self.tokio_console.to_string());
|
||||
|
||||
for line in lines.into_iter().enumerate() {
|
||||
writeln!(msg, "{}: {}", line.1 .0, line.1 .1).expect("should be able to write to string buffer");
|
||||
}
|
||||
|
||||
write!(f, "{msg}")
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -930,7 +879,7 @@ fn default_dns_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_ip_lookup_strategy() -> u8 { 5 }
|
||||
|
||||
fn default_max_request_size() -> u32 {
|
||||
fn default_max_request_size() -> usize {
|
||||
20 * 1024 * 1024 // Default to 20 MB
|
||||
}
|
||||
|
||||
@@ -968,10 +917,20 @@ fn default_pusher_idle_timeout() -> u64 { 15 }
|
||||
|
||||
fn default_max_fetch_prev_events() -> u16 { 100_u16 }
|
||||
|
||||
#[cfg(feature = "perf_measurements")]
|
||||
fn default_tracing_flame_filter() -> String { "trace,h2=off".to_owned() }
|
||||
fn default_tracing_flame_filter() -> String {
|
||||
cfg!(debug_assertions)
|
||||
.then_some("trace,h2=off")
|
||||
.unwrap_or("info")
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
fn default_jaeger_filter() -> String {
|
||||
cfg!(debug_assertions)
|
||||
.then_some("trace,h2=off")
|
||||
.unwrap_or("info")
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
#[cfg(feature = "perf_measurements")]
|
||||
fn default_tracing_flame_output_path() -> String { "./tracing.folded".to_owned() }
|
||||
|
||||
fn default_trusted_servers() -> Vec<OwnedServerName> { vec![OwnedServerName::try_from("matrix.org").unwrap()] }
|
||||
@@ -1070,4 +1029,6 @@ fn default_sentry_endpoint() -> Option<Url> {
|
||||
|
||||
fn default_sentry_traces_sample_rate() -> f32 { 0.15 }
|
||||
|
||||
fn default_sentry_filter() -> String { "info".to_owned() }
|
||||
|
||||
fn default_startup_netburst_keep() -> i64 { 50 }
|
||||
|
||||
@@ -127,6 +127,7 @@ impl WildCardedDomain {
|
||||
impl std::str::FromStr for WildCardedDomain {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
#[allow(clippy::string_slice)]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// maybe do some domain validation?
|
||||
Ok(if s.starts_with("*.") {
|
||||
|
||||
+9
-8
@@ -1,6 +1,4 @@
|
||||
#![allow(dead_code)] // this is a developer's toolbox
|
||||
|
||||
use std::panic;
|
||||
use std::{any::Any, panic};
|
||||
|
||||
/// Export all of the ancillary tools from here as well.
|
||||
pub use crate::utils::debug::*;
|
||||
@@ -14,9 +12,9 @@ pub use crate::utils::debug::*;
|
||||
macro_rules! debug_event {
|
||||
( $level:expr, $($x:tt)+ ) => {
|
||||
if cfg!(debug_assertions) && cfg!(not(feature = "dev_release_log_level")) {
|
||||
::tracing::event!( $level, $($x)+ );
|
||||
::tracing::event!( $level, $($x)+ )
|
||||
} else {
|
||||
::tracing::debug!( $($x)+ );
|
||||
::tracing::debug!( $($x)+ )
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +25,7 @@ macro_rules! debug_event {
|
||||
#[macro_export]
|
||||
macro_rules! debug_error {
|
||||
( $($x:tt)+ ) => {
|
||||
$crate::debug_event!(::tracing::Level::ERROR, $($x)+ );
|
||||
$crate::debug_event!(::tracing::Level::ERROR, $($x)+ )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +35,7 @@ macro_rules! debug_error {
|
||||
#[macro_export]
|
||||
macro_rules! debug_warn {
|
||||
( $($x:tt)+ ) => {
|
||||
$crate::debug_event!(::tracing::Level::WARN, $($x)+ );
|
||||
$crate::debug_event!(::tracing::Level::WARN, $($x)+ )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +45,7 @@ macro_rules! debug_warn {
|
||||
#[macro_export]
|
||||
macro_rules! debug_info {
|
||||
( $($x:tt)+ ) => {
|
||||
$crate::debug_event!(::tracing::Level::INFO, $($x)+ );
|
||||
$crate::debug_event!(::tracing::Level::INFO, $($x)+ )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,3 +77,6 @@ pub fn trap() {
|
||||
std::arch::asm!("int3");
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn panic_str(p: &Box<dyn Any + Send>) -> &'static str { p.downcast_ref::<&str>().copied().unwrap_or_default() }
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
use std::{convert::Infallible, fmt};
|
||||
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use bytes::BytesMut;
|
||||
use http::StatusCode;
|
||||
use http_body_util::Full;
|
||||
use ruma::{
|
||||
api::{
|
||||
client::{
|
||||
error::ErrorKind::{
|
||||
Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, NotFound, ThreepidAuthFailed,
|
||||
ThreepidDenied, TooLarge, Unauthorized, Unknown, UnknownToken, Unrecognized, UserDeactivated,
|
||||
WrongRoomKeysVersion,
|
||||
},
|
||||
uiaa::{UiaaInfo, UiaaResponse},
|
||||
},
|
||||
OutgoingResponse,
|
||||
},
|
||||
OwnedServerName,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use tracing::error;
|
||||
|
||||
#[derive(Error)]
|
||||
pub enum Error {
|
||||
// std
|
||||
#[error("{0}")]
|
||||
Fmt(#[from] fmt::Error),
|
||||
#[error("I/O error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("{0}")]
|
||||
Utf8Error(#[from] std::str::Utf8Error),
|
||||
#[error("{0}")]
|
||||
FromUtf8Error(#[from] std::string::FromUtf8Error),
|
||||
#[error("{0}")]
|
||||
TryFromSliceError(#[from] std::array::TryFromSliceError),
|
||||
|
||||
// third-party
|
||||
#[error("Regex error: {0}")]
|
||||
Regex(#[from] regex::Error),
|
||||
#[error("Tracing filter error: {0}")]
|
||||
TracingFilter(#[from] tracing_subscriber::filter::ParseError),
|
||||
#[error("Image error: {0}")]
|
||||
Image(#[from] image::error::ImageError),
|
||||
#[error("Request error: {0}")]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
#[error("{0}")]
|
||||
Extension(#[from] axum::extract::rejection::ExtensionRejection),
|
||||
#[error("{0}")]
|
||||
Path(#[from] axum::extract::rejection::PathRejection),
|
||||
|
||||
// ruma
|
||||
#[error("{0}")]
|
||||
Mxid(#[from] ruma::IdParseError),
|
||||
#[error("{0}: {1}")]
|
||||
BadRequest(ruma::api::client::error::ErrorKind, &'static str),
|
||||
#[error("from {0}: {1}")]
|
||||
Redaction(OwnedServerName, ruma::canonical_json::RedactionError),
|
||||
#[error("Remote server {0} responded with: {1}")]
|
||||
Federation(OwnedServerName, ruma::api::client::error::Error),
|
||||
#[error("{0} in {1}")]
|
||||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||
|
||||
// conduwuit
|
||||
#[error("There was a problem with your configuration: {0}")]
|
||||
BadConfig(String),
|
||||
#[error("{0}")]
|
||||
BadDatabase(&'static str),
|
||||
#[error("{0}")]
|
||||
Database(String),
|
||||
#[error("{0}")]
|
||||
BadServerResponse(&'static str),
|
||||
#[error("{0}")]
|
||||
Conflict(&'static str), // This is only needed for when a room alias already exists
|
||||
#[error("uiaa")]
|
||||
Uiaa(UiaaInfo),
|
||||
|
||||
// unique / untyped
|
||||
#[error("{0}")]
|
||||
Err(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn bad_database(message: &'static str) -> Self {
|
||||
error!("BadDatabase: {}", message);
|
||||
Self::BadDatabase(message)
|
||||
}
|
||||
|
||||
pub fn bad_config(message: &str) -> Self {
|
||||
error!("BadConfig: {}", message);
|
||||
Self::BadConfig(message.to_owned())
|
||||
}
|
||||
|
||||
/// Returns the Matrix error code / error kind
|
||||
#[inline]
|
||||
pub fn error_code(&self) -> ruma::api::client::error::ErrorKind {
|
||||
if let Self::Federation(_, error) = self {
|
||||
return error.error_kind().unwrap_or_else(|| &Unknown).clone();
|
||||
}
|
||||
|
||||
match self {
|
||||
Self::BadRequest(kind, _) => kind.clone(),
|
||||
_ => Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sanitizes public-facing errors that can leak sensitive information.
|
||||
pub fn sanitized_error(&self) -> String {
|
||||
match self {
|
||||
Self::Database {
|
||||
..
|
||||
} => String::from("Database error occurred."),
|
||||
Self::Io {
|
||||
..
|
||||
} => String::from("I/O error occurred."),
|
||||
_ => self.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Infallible> for Error {
|
||||
fn from(i: Infallible) -> Self { match i {} }
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self}") }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RumaResponse<T>(pub T);
|
||||
|
||||
impl<T> From<T> for RumaResponse<T> {
|
||||
fn from(t: T) -> Self { Self(t) }
|
||||
}
|
||||
|
||||
impl From<Error> for RumaResponse<UiaaResponse> {
|
||||
fn from(t: Error) -> Self { t.to_response() }
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn to_response(&self) -> RumaResponse<UiaaResponse> {
|
||||
use ruma::api::client::error::{Error as RumaError, ErrorBody};
|
||||
|
||||
if let Self::Uiaa(uiaainfo) = self {
|
||||
return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone()));
|
||||
}
|
||||
|
||||
if let Self::Federation(origin, error) = self {
|
||||
let mut error = error.clone();
|
||||
error.body = ErrorBody::Standard {
|
||||
kind: error.error_kind().unwrap_or_else(|| &Unknown).clone(),
|
||||
message: format!("Answer from {origin}: {error}"),
|
||||
};
|
||||
return RumaResponse(UiaaResponse::MatrixError(error));
|
||||
}
|
||||
|
||||
let message = format!("{self}");
|
||||
let (kind, status_code) = match self {
|
||||
Self::BadRequest(kind, _) => (
|
||||
kind.clone(),
|
||||
match kind {
|
||||
WrongRoomKeysVersion {
|
||||
..
|
||||
}
|
||||
| Forbidden {
|
||||
..
|
||||
}
|
||||
| GuestAccessForbidden
|
||||
| ThreepidAuthFailed
|
||||
| UserDeactivated
|
||||
| ThreepidDenied => StatusCode::FORBIDDEN,
|
||||
Unauthorized
|
||||
| UnknownToken {
|
||||
..
|
||||
}
|
||||
| MissingToken => StatusCode::UNAUTHORIZED,
|
||||
NotFound | Unrecognized => StatusCode::NOT_FOUND,
|
||||
LimitExceeded {
|
||||
..
|
||||
} => StatusCode::TOO_MANY_REQUESTS,
|
||||
TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
_ => StatusCode::BAD_REQUEST,
|
||||
},
|
||||
),
|
||||
Self::Conflict(_) => (Unknown, StatusCode::CONFLICT),
|
||||
_ => (Unknown, StatusCode::INTERNAL_SERVER_ERROR),
|
||||
};
|
||||
|
||||
RumaResponse(UiaaResponse::MatrixError(RumaError {
|
||||
body: ErrorBody::Standard {
|
||||
kind,
|
||||
message,
|
||||
},
|
||||
status_code,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::axum::response::IntoResponse for Error {
|
||||
fn into_response(self) -> ::axum::response::Response { self.to_response().into_response() }
|
||||
}
|
||||
|
||||
impl<T: OutgoingResponse> IntoResponse for RumaResponse<T> {
|
||||
fn into_response(self) -> Response {
|
||||
match self.0.try_into_http_response::<BytesMut>() {
|
||||
Ok(res) => res.map(BytesMut::freeze).map(Full::new).into_response(),
|
||||
Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//! Error construction macros
|
||||
//!
|
||||
//! These are specialized macros specific to this project's patterns for
|
||||
//! throwing Errors; they make Error construction succinct and reduce clutter.
|
||||
//! They are developed from folding existing patterns into the macro while
|
||||
//! fixing several anti-patterns in the codebase.
|
||||
//!
|
||||
//! - The primary macros `Err!` and `err!` are provided. `Err!` simply wraps
|
||||
//! `err!` in the Result variant to reduce `Err(err!(...))` boilerplate, thus
|
||||
//! `err!` can be used in any case.
|
||||
//!
|
||||
//! 1. The macro makes the general Error construction easy: `return
|
||||
//! Err!("something went wrong")` replaces the prior `return
|
||||
//! Err(Error::Err("something went wrong".to_owned()))`.
|
||||
//!
|
||||
//! 2. The macro integrates format strings automatically: `return
|
||||
//! Err!("something bad: {msg}")` replaces the prior `return
|
||||
//! Err(Error::Err(format!("something bad: {msg}")))`.
|
||||
//!
|
||||
//! 3. The macro scopes variants of Error: `return Err!(Database("problem with
|
||||
//! bad database."))` replaces the prior `return Err(Error::Database("problem
|
||||
//! with bad database."))`.
|
||||
//!
|
||||
//! 4. The macro matches and scopes some special-case sub-variants, for example
|
||||
//! with ruma ErrorKind: `return Err!(Request(MissingToken("you must provide
|
||||
//! an access token")))`.
|
||||
//!
|
||||
//! 5. The macro fixes the anti-pattern of repeating messages in an error! log
|
||||
//! and then again in an Error construction, often slightly different due to
|
||||
//! the Error variant not supporting a format string. Instead `return
|
||||
//! Err(Database(error!("problem with db: {msg}")))` logs the error at the
|
||||
//! callsite and then returns the error with the same string. Caller has the
|
||||
//! option of replacing `error!` with `debug_error!`.
|
||||
#[macro_export]
|
||||
macro_rules! Err {
|
||||
($($args:tt)*) => {
|
||||
Err($crate::err!($($args)*))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! err {
|
||||
(Config($item:literal, $($args:expr),*)) => {{
|
||||
$crate::error!(config = %$item, $($args),*);
|
||||
$crate::error::Error::Config($item, $crate::format_maybe!($($args),*))
|
||||
}};
|
||||
|
||||
(Request(Forbidden($level:ident!($($args:expr),*)))) => {{
|
||||
$crate::$level!($($args),*);
|
||||
$crate::error::Error::Request(
|
||||
::ruma::api::client::error::ErrorKind::forbidden(),
|
||||
$crate::format_maybe!($($args),*)
|
||||
)
|
||||
}};
|
||||
|
||||
(Request(Forbidden($($args:expr),*))) => {
|
||||
$crate::error::Error::Request(
|
||||
::ruma::api::client::error::ErrorKind::forbidden(),
|
||||
$crate::format_maybe!($($args),*)
|
||||
)
|
||||
};
|
||||
|
||||
(Request($variant:ident($level:ident!($($args:expr),*)))) => {{
|
||||
$crate::$level!($($args),*);
|
||||
$crate::error::Error::Request(
|
||||
::ruma::api::client::error::ErrorKind::$variant,
|
||||
$crate::format_maybe!($($args),*)
|
||||
)
|
||||
}};
|
||||
|
||||
(Request($variant:ident($($args:expr),*))) => {
|
||||
$crate::error::Error::Request(
|
||||
::ruma::api::client::error::ErrorKind::$variant,
|
||||
$crate::format_maybe!($($args),*)
|
||||
)
|
||||
};
|
||||
|
||||
($variant:ident($level:ident!($($args:expr),*))) => {{
|
||||
$crate::$level!($($args),*);
|
||||
$crate::error::Error::$variant($crate::format_maybe!($($args),*))
|
||||
}};
|
||||
|
||||
($variant:ident($($args:expr),*)) => {
|
||||
$crate::error::Error::$variant($crate::format_maybe!($($args),*))
|
||||
};
|
||||
|
||||
($level:ident!($($args:expr),*)) => {{
|
||||
$crate::$level!($($args),*);
|
||||
$crate::error::Error::Err($crate::format_maybe!($($args),*))
|
||||
}};
|
||||
|
||||
($($args:expr),*) => {
|
||||
$crate::error::Error::Err($crate::format_maybe!($($args),*))
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
use std::{convert::Infallible, fmt};
|
||||
|
||||
use super::Error;
|
||||
use crate::{debug_error, error};
|
||||
|
||||
#[inline]
|
||||
pub fn else_log<T, E>(error: E) -> Result<T, Infallible>
|
||||
where
|
||||
T: Default,
|
||||
Error: From<E>,
|
||||
{
|
||||
Ok(default_log(error))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn else_debug_log<T, E>(error: E) -> Result<T, Infallible>
|
||||
where
|
||||
T: Default,
|
||||
Error: From<E>,
|
||||
{
|
||||
Ok(default_debug_log(error))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn default_log<T, E>(error: E) -> T
|
||||
where
|
||||
T: Default,
|
||||
Error: From<E>,
|
||||
{
|
||||
let error = Error::from(error);
|
||||
inspect_log(&error);
|
||||
T::default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn default_debug_log<T, E>(error: E) -> T
|
||||
where
|
||||
T: Default,
|
||||
Error: From<E>,
|
||||
{
|
||||
let error = Error::from(error);
|
||||
inspect_debug_log(&error);
|
||||
T::default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn map_log<E>(error: E) -> Error
|
||||
where
|
||||
Error: From<E>,
|
||||
{
|
||||
let error = Error::from(error);
|
||||
inspect_log(&error);
|
||||
error
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn map_debug_log<E>(error: E) -> Error
|
||||
where
|
||||
Error: From<E>,
|
||||
{
|
||||
let error = Error::from(error);
|
||||
inspect_debug_log(&error);
|
||||
error
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inspect_log<E: fmt::Display>(error: &E) {
|
||||
error!("{error}");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inspect_debug_log<E: fmt::Debug>(error: &E) {
|
||||
debug_error!("{error:?}");
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
mod err;
|
||||
mod log;
|
||||
mod panic;
|
||||
mod response;
|
||||
|
||||
use std::{any::Any, borrow::Cow, convert::Infallible, fmt};
|
||||
|
||||
pub use log::*;
|
||||
|
||||
use crate::error;
|
||||
|
||||
#[derive(thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("PANIC!")]
|
||||
PanicAny(Box<dyn Any + Send>),
|
||||
#[error("PANIC! {0}")]
|
||||
Panic(&'static str, Box<dyn Any + Send + 'static>),
|
||||
|
||||
// std
|
||||
#[error("{0}")]
|
||||
Fmt(#[from] fmt::Error),
|
||||
#[error("I/O error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("{0}")]
|
||||
Utf8Error(#[from] std::str::Utf8Error),
|
||||
#[error("{0}")]
|
||||
FromUtf8Error(#[from] std::string::FromUtf8Error),
|
||||
#[error("{0}")]
|
||||
TryFromSliceError(#[from] std::array::TryFromSliceError),
|
||||
#[error("{0}")]
|
||||
TryFromIntError(#[from] std::num::TryFromIntError),
|
||||
#[error("{0}")]
|
||||
ParseIntError(#[from] std::num::ParseIntError),
|
||||
#[error("{0}")]
|
||||
ParseFloatError(#[from] std::num::ParseFloatError),
|
||||
|
||||
// third-party
|
||||
#[error("Join error: {0}")]
|
||||
JoinError(#[from] tokio::task::JoinError),
|
||||
#[error("Regex error: {0}")]
|
||||
Regex(#[from] regex::Error),
|
||||
#[error("Tracing filter error: {0}")]
|
||||
TracingFilter(#[from] tracing_subscriber::filter::ParseError),
|
||||
#[error("Tracing reload error: {0}")]
|
||||
TracingReload(#[from] tracing_subscriber::reload::Error),
|
||||
#[error("Image error: {0}")]
|
||||
Image(#[from] image::error::ImageError),
|
||||
#[error("Request error: {0}")]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
#[error("{0}")]
|
||||
Extension(#[from] axum::extract::rejection::ExtensionRejection),
|
||||
#[error("{0}")]
|
||||
Path(#[from] axum::extract::rejection::PathRejection),
|
||||
#[error("{0}")]
|
||||
Http(#[from] http::Error),
|
||||
#[error("{0}")]
|
||||
HttpHeader(#[from] http::header::InvalidHeaderValue),
|
||||
|
||||
// ruma
|
||||
#[error("{0}")]
|
||||
IntoHttpError(#[from] ruma::api::error::IntoHttpError),
|
||||
#[error("{0}")]
|
||||
RumaError(#[from] ruma::api::client::error::Error),
|
||||
#[error("uiaa")]
|
||||
Uiaa(ruma::api::client::uiaa::UiaaInfo),
|
||||
#[error("{0}")]
|
||||
Mxid(#[from] ruma::IdParseError),
|
||||
#[error("{0}: {1}")]
|
||||
BadRequest(ruma::api::client::error::ErrorKind, &'static str), //TODO: remove
|
||||
#[error("{0}: {1}")]
|
||||
Request(ruma::api::client::error::ErrorKind, Cow<'static, str>),
|
||||
#[error("from {0}: {1}")]
|
||||
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
|
||||
#[error("Remote server {0} responded with: {1}")]
|
||||
Federation(ruma::OwnedServerName, ruma::api::client::error::Error),
|
||||
#[error("{0} in {1}")]
|
||||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||
|
||||
// conduwuit
|
||||
#[error("Arithmetic operation failed: {0}")]
|
||||
Arithmetic(&'static str),
|
||||
#[error("There was a problem with the '{0}' directive in your configuration: {1}")]
|
||||
Config(&'static str, Cow<'static, str>),
|
||||
#[error("{0}")]
|
||||
Database(Cow<'static, str>),
|
||||
#[error("{0}")]
|
||||
BadServerResponse(&'static str),
|
||||
#[error("{0}")]
|
||||
Conflict(&'static str), // This is only needed for when a room alias already exists
|
||||
|
||||
// unique / untyped
|
||||
#[error("{0}")]
|
||||
Err(Cow<'static, str>),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn bad_database(message: &'static str) -> Self { crate::err!(Database(error!("{message}"))) }
|
||||
|
||||
/// Sanitizes public-facing errors that can leak sensitive information.
|
||||
pub fn sanitized_string(&self) -> String {
|
||||
match self {
|
||||
Self::Database(..) => String::from("Database error occurred."),
|
||||
Self::Io(..) => String::from("I/O error occurred."),
|
||||
_ => self.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn message(&self) -> String {
|
||||
match self {
|
||||
Self::Federation(ref origin, ref error) => format!("Answer from {origin}: {error}"),
|
||||
Self::RumaError(ref error) => response::ruma_error_message(error),
|
||||
_ => format!("{self}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the Matrix error code / error kind
|
||||
#[inline]
|
||||
pub fn kind(&self) -> ruma::api::client::error::ErrorKind {
|
||||
use ruma::api::client::error::ErrorKind::Unknown;
|
||||
|
||||
match self {
|
||||
Self::Federation(_, error) => response::ruma_error_kind(error).clone(),
|
||||
Self::BadRequest(kind, _) | Self::Request(kind, _) => kind.clone(),
|
||||
_ => Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status_code(&self) -> http::StatusCode {
|
||||
match self {
|
||||
Self::Federation(_, ref error) | Self::RumaError(ref error) => error.status_code,
|
||||
Self::BadRequest(ref kind, _) | Self::Request(ref kind, _) => response::bad_request_code(kind),
|
||||
Self::Conflict(_) => http::StatusCode::CONFLICT,
|
||||
_ => http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self}") }
|
||||
}
|
||||
|
||||
#[allow(clippy::fallible_impl_from)]
|
||||
impl From<Infallible> for Error {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn from(_e: Infallible) -> Self {
|
||||
panic!("infallible error should never exist");
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub fn infallible(_e: &Infallible) {
|
||||
panic!("infallible error should never exist");
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use std::{
|
||||
any::Any,
|
||||
panic::{panic_any, RefUnwindSafe, UnwindSafe},
|
||||
};
|
||||
|
||||
use super::Error;
|
||||
use crate::debug;
|
||||
|
||||
impl UnwindSafe for Error {}
|
||||
impl RefUnwindSafe for Error {}
|
||||
|
||||
impl Error {
|
||||
pub fn panic(self) -> ! { panic_any(self.into_panic()) }
|
||||
|
||||
#[must_use]
|
||||
pub fn from_panic(e: Box<dyn Any + Send>) -> Self { Self::Panic(debug::panic_str(&e), e) }
|
||||
|
||||
pub fn into_panic(self) -> Box<dyn Any + Send + 'static> {
|
||||
match self {
|
||||
Self::Panic(_, e) | Self::PanicAny(e) => e,
|
||||
Self::JoinError(e) => e.into_panic(),
|
||||
_ => Box::new(self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the panic message string.
|
||||
pub fn panic_str(self) -> Option<&'static str> {
|
||||
self.is_panic()
|
||||
.then_some(debug::panic_str(&self.into_panic()))
|
||||
}
|
||||
|
||||
/// Check if the Error is trafficking a panic object.
|
||||
#[inline]
|
||||
pub fn is_panic(&self) -> bool {
|
||||
match &self {
|
||||
Self::Panic(..) | Self::PanicAny(..) => true,
|
||||
Self::JoinError(e) => e.is_panic(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use bytes::BytesMut;
|
||||
use http::StatusCode;
|
||||
use http_body_util::Full;
|
||||
use ruma::api::{client::uiaa::UiaaResponse, OutgoingResponse};
|
||||
|
||||
use super::Error;
|
||||
use crate::error;
|
||||
|
||||
impl axum::response::IntoResponse for Error {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
let response: UiaaResponse = self.into();
|
||||
response
|
||||
.try_into_http_response::<BytesMut>()
|
||||
.inspect_err(|e| error!("error response error: {e}"))
|
||||
.map_or_else(
|
||||
|_| StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
||||
|r| r.map(BytesMut::freeze).map(Full::new).into_response(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for UiaaResponse {
|
||||
fn from(error: Error) -> Self {
|
||||
if let Error::Uiaa(uiaainfo) = error {
|
||||
return Self::AuthResponse(uiaainfo);
|
||||
}
|
||||
|
||||
let body = ruma::api::client::error::ErrorBody::Standard {
|
||||
kind: error.kind(),
|
||||
message: error.message(),
|
||||
};
|
||||
|
||||
Self::MatrixError(ruma::api::client::error::Error {
|
||||
status_code: error.status_code(),
|
||||
body,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn bad_request_code(kind: &ruma::api::client::error::ErrorKind) -> StatusCode {
|
||||
use ruma::api::client::error::ErrorKind::*;
|
||||
|
||||
match kind {
|
||||
GuestAccessForbidden
|
||||
| ThreepidAuthFailed
|
||||
| UserDeactivated
|
||||
| ThreepidDenied
|
||||
| WrongRoomKeysVersion {
|
||||
..
|
||||
}
|
||||
| Forbidden {
|
||||
..
|
||||
} => StatusCode::FORBIDDEN,
|
||||
|
||||
UnknownToken {
|
||||
..
|
||||
}
|
||||
| MissingToken
|
||||
| Unauthorized => StatusCode::UNAUTHORIZED,
|
||||
|
||||
LimitExceeded {
|
||||
..
|
||||
} => StatusCode::TOO_MANY_REQUESTS,
|
||||
|
||||
TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
|
||||
NotFound | Unrecognized => StatusCode::NOT_FOUND,
|
||||
|
||||
_ => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn ruma_error_message(error: &ruma::api::client::error::Error) -> String {
|
||||
if let ruma::api::client::error::ErrorBody::Standard {
|
||||
message,
|
||||
..
|
||||
} = &error.body
|
||||
{
|
||||
return message.to_string();
|
||||
}
|
||||
|
||||
format!("{error}")
|
||||
}
|
||||
|
||||
pub(super) fn ruma_error_kind(e: &ruma::api::client::error::Error) -> &ruma::api::client::error::ErrorKind {
|
||||
e.error_kind()
|
||||
.unwrap_or(&ruma::api::client::error::ErrorKind::Unknown)
|
||||
}
|
||||
+5
-5
@@ -29,25 +29,25 @@ pub struct Log {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
( $($x:tt)+ ) => { ::tracing::error!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::error!( $($x)+ ) }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
( $($x:tt)+ ) => { ::tracing::warn!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::warn!( $($x)+ ) }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
( $($x:tt)+ ) => { ::tracing::info!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::info!( $($x)+ ) }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
( $($x:tt)+ ) => { ::tracing::debug!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::debug!( $($x)+ ) }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace {
|
||||
( $($x:tt)+ ) => { ::tracing::trace!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::trace!( $($x)+ ) }
|
||||
}
|
||||
|
||||
+46
-20
@@ -1,7 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use tracing_subscriber::{reload, EnvFilter};
|
||||
|
||||
use crate::{error, Result};
|
||||
|
||||
/// We need to store a reload::Handle value, but can't name it's type explicitly
|
||||
/// because the S type parameter depends on the subscriber's previous layers. In
|
||||
/// our case, this includes unnameable 'impl Trait' types.
|
||||
@@ -17,39 +22,60 @@ use tracing_subscriber::{reload, EnvFilter};
|
||||
///
|
||||
/// [1]: <https://github.com/tokio-rs/tracing/pull/1035/commits/8a87ea52425098d3ef8f56d92358c2f6c144a28f>
|
||||
pub trait ReloadHandle<L> {
|
||||
fn current(&self) -> Option<L>;
|
||||
|
||||
fn reload(&self, new_value: L) -> Result<(), reload::Error>;
|
||||
}
|
||||
|
||||
impl<L, S> ReloadHandle<L> for reload::Handle<L, S> {
|
||||
impl<L: Clone, S> ReloadHandle<L> for reload::Handle<L, S> {
|
||||
fn current(&self) -> Option<L> { Self::clone_current(self) }
|
||||
|
||||
fn reload(&self, new_value: L) -> Result<(), reload::Error> { Self::reload(self, new_value) }
|
||||
}
|
||||
|
||||
struct LogLevelReloadHandlesInner {
|
||||
handles: Vec<Box<dyn ReloadHandle<EnvFilter> + Send + Sync>>,
|
||||
}
|
||||
|
||||
/// Wrapper to allow reloading the filter on several several
|
||||
/// [`tracing_subscriber::reload::Handle`]s at once, with the same value.
|
||||
#[derive(Clone)]
|
||||
pub struct LogLevelReloadHandles {
|
||||
inner: Arc<LogLevelReloadHandlesInner>,
|
||||
handles: Arc<Mutex<HandleMap>>,
|
||||
}
|
||||
|
||||
type HandleMap = HashMap<String, Handle>;
|
||||
type Handle = Box<dyn ReloadHandle<EnvFilter> + Send + Sync>;
|
||||
|
||||
impl LogLevelReloadHandles {
|
||||
#[must_use]
|
||||
pub fn new(handles: Vec<Box<dyn ReloadHandle<EnvFilter> + Send + Sync>>) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(LogLevelReloadHandlesInner {
|
||||
handles,
|
||||
}),
|
||||
}
|
||||
pub fn add(&self, name: &str, handle: Handle) {
|
||||
self.handles
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.insert(name.into(), handle);
|
||||
}
|
||||
|
||||
pub fn reload(&self, new_value: &EnvFilter) -> Result<(), reload::Error> {
|
||||
for handle in &self.inner.handles {
|
||||
handle.reload(new_value.clone())?;
|
||||
}
|
||||
pub fn reload(&self, new_value: &EnvFilter, names: Option<&[&str]>) -> Result<()> {
|
||||
self.handles
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.iter()
|
||||
.filter(|(name, _)| names.map_or(false, |names| names.contains(&name.as_str())))
|
||||
.for_each(|(_, handle)| {
|
||||
_ = handle.reload(new_value.clone()).or_else(error::else_log);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn current(&self, name: &str) -> Option<EnvFilter> {
|
||||
self.handles
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.get(name)
|
||||
.map(|handle| handle.current())?
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LogLevelReloadHandles {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
handles: Arc::new(HandleMap::new().into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,21 @@ pub struct Suppress {
|
||||
|
||||
impl Suppress {
|
||||
pub fn new(server: &Arc<Server>) -> Self {
|
||||
let handle = "console";
|
||||
let config = &server.config.log;
|
||||
Self::from_filters(server, EnvFilter::try_new(config).unwrap_or_default(), &EnvFilter::default())
|
||||
}
|
||||
let suppress = EnvFilter::default();
|
||||
let restore = server
|
||||
.log
|
||||
.reload
|
||||
.current(handle)
|
||||
.unwrap_or_else(|| EnvFilter::try_new(config).unwrap_or_default());
|
||||
|
||||
fn from_filters(server: &Arc<Server>, restore: EnvFilter, suppress: &EnvFilter) -> Self {
|
||||
server
|
||||
.log
|
||||
.reload
|
||||
.reload(suppress)
|
||||
.reload(&suppress, Some(&[handle]))
|
||||
.expect("log filter reloaded");
|
||||
|
||||
Self {
|
||||
server: server.clone(),
|
||||
restore,
|
||||
@@ -32,7 +37,7 @@ impl Drop for Suppress {
|
||||
self.server
|
||||
.log
|
||||
.reload
|
||||
.reload(&self.restore)
|
||||
.reload(&self.restore, Some(&["console"]))
|
||||
.expect("log filter reloaded");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
use std::sync::atomic::AtomicU32;
|
||||
|
||||
use tokio::runtime;
|
||||
use tokio_metrics::TaskMonitor;
|
||||
#[cfg(tokio_unstable)]
|
||||
use tokio_metrics::{RuntimeIntervals, RuntimeMonitor};
|
||||
|
||||
pub struct Metrics {
|
||||
_runtime: Option<runtime::Handle>,
|
||||
|
||||
runtime_metrics: Option<runtime::RuntimeMetrics>,
|
||||
|
||||
task_monitor: Option<TaskMonitor>,
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
_runtime_monitor: Option<RuntimeMonitor>,
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
runtime_intervals: std::sync::Mutex<Option<RuntimeIntervals>>,
|
||||
|
||||
// TODO: move stats
|
||||
pub requests_spawn_active: AtomicU32,
|
||||
pub requests_spawn_finished: AtomicU32,
|
||||
pub requests_handle_active: AtomicU32,
|
||||
pub requests_handle_finished: AtomicU32,
|
||||
pub requests_panic: AtomicU32,
|
||||
}
|
||||
|
||||
impl Metrics {
|
||||
#[must_use]
|
||||
pub fn new(runtime: Option<runtime::Handle>) -> Self {
|
||||
#[cfg(tokio_unstable)]
|
||||
let runtime_monitor = runtime.as_ref().map(RuntimeMonitor::new);
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
let runtime_intervals = runtime_monitor.as_ref().map(RuntimeMonitor::intervals);
|
||||
|
||||
Self {
|
||||
_runtime: runtime.clone(),
|
||||
|
||||
runtime_metrics: runtime.as_ref().map(runtime::Handle::metrics),
|
||||
|
||||
task_monitor: runtime.map(|_| TaskMonitor::new()),
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
_runtime_monitor: runtime_monitor,
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
runtime_intervals: std::sync::Mutex::new(runtime_intervals),
|
||||
|
||||
requests_spawn_active: AtomicU32::new(0),
|
||||
requests_spawn_finished: AtomicU32::new(0),
|
||||
requests_handle_active: AtomicU32::new(0),
|
||||
requests_handle_finished: AtomicU32::new(0),
|
||||
requests_panic: AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
pub fn runtime_interval(&self) -> Option<tokio_metrics::RuntimeMetrics> {
|
||||
self.runtime_intervals
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.as_mut()
|
||||
.map(Iterator::next)
|
||||
.expect("next interval")
|
||||
}
|
||||
|
||||
pub fn task_root(&self) -> Option<&TaskMonitor> { self.task_monitor.as_ref() }
|
||||
|
||||
pub fn runtime_metrics(&self) -> Option<&runtime::RuntimeMetrics> { self.runtime_metrics.as_ref() }
|
||||
}
|
||||
+2
-1
@@ -3,6 +3,7 @@ pub mod config;
|
||||
pub mod debug;
|
||||
pub mod error;
|
||||
pub mod log;
|
||||
pub mod metrics;
|
||||
pub mod mods;
|
||||
pub mod pdu;
|
||||
pub mod server;
|
||||
@@ -10,7 +11,7 @@ pub mod utils;
|
||||
pub mod version;
|
||||
|
||||
pub use config::Config;
|
||||
pub use error::{Error, RumaResponse};
|
||||
pub use error::Error;
|
||||
pub use pdu::{PduBuilder, PduCount, PduEvent};
|
||||
pub use server::Server;
|
||||
pub use version::version;
|
||||
|
||||
+10
-10
@@ -64,7 +64,7 @@ pub struct PduEvent {
|
||||
}
|
||||
|
||||
impl PduEvent {
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn redact(&mut self, room_version_id: RoomVersionId, reason: &Self) -> crate::Result<()> {
|
||||
self.unsigned = None;
|
||||
|
||||
@@ -158,7 +158,7 @@ impl PduEvent {
|
||||
(self.redacts.clone(), self.content.clone())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn to_sync_room_event(&self) -> Raw<AnySyncTimelineEvent> {
|
||||
let (redacts, content) = self.copy_redacts();
|
||||
let mut json = json!({
|
||||
@@ -183,7 +183,7 @@ impl PduEvent {
|
||||
}
|
||||
|
||||
/// This only works for events that are also AnyRoomEvents.
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn to_any_event(&self) -> Raw<AnyEphemeralRoomEvent> {
|
||||
let (redacts, content) = self.copy_redacts();
|
||||
let mut json = json!({
|
||||
@@ -208,7 +208,7 @@ impl PduEvent {
|
||||
serde_json::from_value(json).expect("Raw::from_value always works")
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn to_room_event(&self) -> Raw<AnyTimelineEvent> {
|
||||
let (redacts, content) = self.copy_redacts();
|
||||
let mut json = json!({
|
||||
@@ -233,7 +233,7 @@ impl PduEvent {
|
||||
serde_json::from_value(json).expect("Raw::from_value always works")
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn to_message_like_event(&self) -> Raw<AnyMessageLikeEvent> {
|
||||
let (redacts, content) = self.copy_redacts();
|
||||
let mut json = json!({
|
||||
@@ -258,7 +258,7 @@ impl PduEvent {
|
||||
serde_json::from_value(json).expect("Raw::from_value always works")
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn to_state_event(&self) -> Raw<AnyStateEvent> {
|
||||
let mut json = json!({
|
||||
"content": self.content,
|
||||
@@ -277,7 +277,7 @@ impl PduEvent {
|
||||
serde_json::from_value(json).expect("Raw::from_value always works")
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn to_sync_state_event(&self) -> Raw<AnySyncStateEvent> {
|
||||
let mut json = json!({
|
||||
"content": self.content,
|
||||
@@ -295,7 +295,7 @@ impl PduEvent {
|
||||
serde_json::from_value(json).expect("Raw::from_value always works")
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn to_stripped_state_event(&self) -> Raw<AnyStrippedStateEvent> {
|
||||
let json = json!({
|
||||
"content": self.content,
|
||||
@@ -307,7 +307,7 @@ impl PduEvent {
|
||||
serde_json::from_value(json).expect("Raw::from_value always works")
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn to_stripped_spacechild_state_event(&self) -> Raw<HierarchySpaceChildEvent> {
|
||||
let json = json!({
|
||||
"content": self.content,
|
||||
@@ -320,7 +320,7 @@ impl PduEvent {
|
||||
serde_json::from_value(json).expect("Raw::from_value always works")
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn to_member_event(&self) -> Raw<StateEvent<RoomMemberEventContent>> {
|
||||
let mut json = json!({
|
||||
"content": self.content,
|
||||
|
||||
+20
-23
@@ -1,11 +1,11 @@
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, AtomicU32, Ordering},
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
use tokio::{runtime, sync::broadcast};
|
||||
|
||||
use crate::{config::Config, log, Error, Result};
|
||||
use crate::{config::Config, log::Log, metrics::Metrics, Err, Result};
|
||||
|
||||
/// Server runtime state; public portion
|
||||
pub struct Server {
|
||||
@@ -33,71 +33,68 @@ pub struct Server {
|
||||
pub signal: broadcast::Sender<&'static str>,
|
||||
|
||||
/// Logging subsystem state
|
||||
pub log: log::Log,
|
||||
pub log: Log,
|
||||
|
||||
/// TODO: move stats
|
||||
pub requests_spawn_active: AtomicU32,
|
||||
pub requests_spawn_finished: AtomicU32,
|
||||
pub requests_handle_active: AtomicU32,
|
||||
pub requests_handle_finished: AtomicU32,
|
||||
pub requests_panic: AtomicU32,
|
||||
/// Metrics subsystem state
|
||||
pub metrics: Metrics,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
#[must_use]
|
||||
pub fn new(config: Config, runtime: Option<runtime::Handle>, log: log::Log) -> Self {
|
||||
pub fn new(config: Config, runtime: Option<runtime::Handle>, log: Log) -> Self {
|
||||
Self {
|
||||
config,
|
||||
started: SystemTime::now(),
|
||||
stopping: AtomicBool::new(false),
|
||||
reloading: AtomicBool::new(false),
|
||||
restarting: AtomicBool::new(false),
|
||||
runtime,
|
||||
runtime: runtime.clone(),
|
||||
signal: broadcast::channel::<&'static str>(1).0,
|
||||
log,
|
||||
requests_spawn_active: AtomicU32::new(0),
|
||||
requests_spawn_finished: AtomicU32::new(0),
|
||||
requests_handle_active: AtomicU32::new(0),
|
||||
requests_handle_finished: AtomicU32::new(0),
|
||||
requests_panic: AtomicU32::new(0),
|
||||
metrics: Metrics::new(runtime),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reload(&self) -> Result<()> {
|
||||
if cfg!(not(conduit_mods)) {
|
||||
return Err(Error::Err("Reloading not enabled".into()));
|
||||
return Err!("Reloading not enabled");
|
||||
}
|
||||
|
||||
if self.reloading.swap(true, Ordering::AcqRel) {
|
||||
return Err(Error::Err("Reloading already in progress".into()));
|
||||
return Err!("Reloading already in progress");
|
||||
}
|
||||
|
||||
if self.stopping.swap(true, Ordering::AcqRel) {
|
||||
return Err(Error::Err("Shutdown already in progress".into()));
|
||||
return Err!("Shutdown already in progress");
|
||||
}
|
||||
|
||||
self.signal("SIGINT")
|
||||
self.signal("SIGINT").inspect_err(|_| {
|
||||
self.stopping.store(false, Ordering::Release);
|
||||
self.reloading.store(false, Ordering::Release);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn restart(&self) -> Result<()> {
|
||||
if self.restarting.swap(true, Ordering::AcqRel) {
|
||||
return Err(Error::Err("Restart already in progress".into()));
|
||||
return Err!("Restart already in progress");
|
||||
}
|
||||
|
||||
self.shutdown()
|
||||
.inspect_err(|_| self.restarting.store(false, Ordering::Release))
|
||||
}
|
||||
|
||||
pub fn shutdown(&self) -> Result<()> {
|
||||
if self.stopping.swap(true, Ordering::AcqRel) {
|
||||
return Err(Error::Err("Shutdown already in progress".into()));
|
||||
return Err!("Shutdown already in progress");
|
||||
}
|
||||
|
||||
self.signal("SIGTERM")
|
||||
.inspect_err(|_| self.stopping.store(false, Ordering::Release))
|
||||
}
|
||||
|
||||
pub fn signal(&self, sig: &'static str) -> Result<()> {
|
||||
if let Err(e) = self.signal.send(sig) {
|
||||
return Err(Error::Err(format!("Failed to send signal: {e}")));
|
||||
return Err!("Failed to send signal: {e}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -66,7 +66,7 @@ pub fn content_disposition_type(content_type: &Option<String>) -> &'static str {
|
||||
|
||||
/// sanitises the file name for the Content-Disposition using
|
||||
/// `sanitize_filename` crate
|
||||
#[tracing::instrument]
|
||||
#[tracing::instrument(level = "debug")]
|
||||
pub fn sanitise_filename(filename: String) -> String {
|
||||
let options = sanitize_filename::Options {
|
||||
truncate: false,
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
#[macro_export]
|
||||
macro_rules! defer {
|
||||
($body:block) => {
|
||||
struct _Defer_<F>
|
||||
where
|
||||
F: FnMut(),
|
||||
{
|
||||
struct _Defer_<F: FnMut()> {
|
||||
closure: F,
|
||||
}
|
||||
|
||||
impl<F> Drop for _Defer_<F>
|
||||
where
|
||||
F: FnMut(),
|
||||
{
|
||||
impl<F: FnMut()> Drop for _Defer_<F> {
|
||||
fn drop(&mut self) { (self.closure)(); }
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use argon2::{
|
||||
PasswordVerifier, Version,
|
||||
};
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::{err, Error, Result};
|
||||
|
||||
const M_COST: u32 = Params::DEFAULT_M_COST; // memory size in 1 KiB blocks
|
||||
const T_COST: u32 = Params::DEFAULT_T_COST; // nr of iterations
|
||||
@@ -44,7 +44,7 @@ pub(super) fn verify_password(password: &str, password_hash: &str) -> Result<()>
|
||||
.map_err(map_err)
|
||||
}
|
||||
|
||||
fn map_err(e: password_hash::Error) -> Error { Error::Err(e.to_string()) }
|
||||
fn map_err(e: password_hash::Error) -> Error { err!("{e}") }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ring::{digest, digest::SHA256};
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument(skip_all, level = "debug")]
|
||||
pub(super) fn hash(keys: &[&[u8]]) -> Vec<u8> {
|
||||
// We only hash the pdu's event ids, not the whole pdu
|
||||
let bytes = keys.join(&0xFF);
|
||||
|
||||
@@ -6,6 +6,7 @@ pub struct Escape<'a>(pub &'a str);
|
||||
|
||||
/// Copied from librustdoc:
|
||||
/// * <https://github.com/rust-lang/rust/blob/cbaeec14f90b59a91a6b0f17fc046c66fa811892/src/librustdoc/html/escape.rs>
|
||||
#[allow(clippy::string_slice)]
|
||||
impl fmt::Display for Escape<'_> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Because the internet is always right, turns out there's not that many
|
||||
@@ -26,7 +27,7 @@ impl fmt::Display for Escape<'_> {
|
||||
fmt.write_str(s)?;
|
||||
// NOTE: we only expect single byte characters here - which is fine as long as
|
||||
// we only match single byte characters
|
||||
last = i + 1;
|
||||
last = i.saturating_add(1);
|
||||
}
|
||||
|
||||
if last < s.len() {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
use std::{cmp, time::Duration};
|
||||
|
||||
pub use checked_ops::checked_ops;
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// Checked arithmetic expression. Returns a Result<R, Error::Arithmetic>
|
||||
#[macro_export]
|
||||
macro_rules! checked {
|
||||
($($input:tt)*) => {
|
||||
$crate::utils::math::checked_ops!($($input)*)
|
||||
.ok_or_else(|| $crate::Error::Arithmetic("operation overflowed or result invalid"))
|
||||
}
|
||||
}
|
||||
|
||||
/// in release-mode. Use for performance when the expression is obviously safe.
|
||||
/// The check remains in debug-mode for regression analysis.
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[macro_export]
|
||||
macro_rules! validated {
|
||||
($($input:tt)*) => {
|
||||
//#[allow(clippy::arithmetic_side_effects)] {
|
||||
//Some($($input)*)
|
||||
// .ok_or_else(|| $crate::Error::Arithmetic("this error should never been seen"))
|
||||
//}
|
||||
|
||||
//NOTE: remove me when stmt_expr_attributes is stable
|
||||
$crate::checked!($($input)*)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[macro_export]
|
||||
macro_rules! validated {
|
||||
($($input:tt)*) => { $crate::checked!($($input)*) }
|
||||
}
|
||||
|
||||
/// Returns false if the exponential backoff has expired based on the inputs
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn continue_exponential_backoff_secs(min: u64, max: u64, elapsed: Duration, tries: u32) -> bool {
|
||||
let min = Duration::from_secs(min);
|
||||
let max = Duration::from_secs(max);
|
||||
continue_exponential_backoff(min, max, elapsed, tries)
|
||||
}
|
||||
|
||||
/// Returns false if the exponential backoff has expired based on the inputs
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn continue_exponential_backoff(min: Duration, max: Duration, elapsed: Duration, tries: u32) -> bool {
|
||||
let min = min.saturating_mul(tries).saturating_mul(tries);
|
||||
let min = cmp::min(min, max);
|
||||
elapsed < min
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::as_conversions)]
|
||||
pub fn usize_from_f64(val: f64) -> Result<usize, Error> {
|
||||
if val < 0.0 {
|
||||
return Err(Error::Arithmetic("Converting negative float to unsigned integer"));
|
||||
}
|
||||
|
||||
//SAFETY: <https://doc.rust-lang.org/std/primitive.f64.html#method.to_int_unchecked>
|
||||
Ok(unsafe { val.to_int_unchecked::<usize>() })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn usize_from_ruma(val: ruma::UInt) -> usize {
|
||||
usize::try_from(val).expect("failed conversion from ruma::UInt to usize")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ruma_from_u64(val: u64) -> ruma::UInt {
|
||||
ruma::UInt::try_from(val).expect("failed conversion from u64 to ruma::UInt")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ruma_from_usize(val: usize) -> ruma::UInt {
|
||||
ruma::UInt::try_from(val).expect("failed conversion from usize to ruma::UInt")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
|
||||
pub fn usize_from_u64_truncated(val: u64) -> usize { val as usize }
|
||||
+2
-14
@@ -5,6 +5,7 @@ pub mod defer;
|
||||
pub mod hash;
|
||||
pub mod html;
|
||||
pub mod json;
|
||||
pub mod math;
|
||||
pub mod mutex_map;
|
||||
pub mod rand;
|
||||
pub mod string;
|
||||
@@ -19,27 +20,14 @@ pub use debug::slice_truncated as debug_slice_truncated;
|
||||
pub use hash::calculate_hash;
|
||||
pub use html::Escape as HtmlEscape;
|
||||
pub use json::{deserialize_from_str, to_canonical_object};
|
||||
pub use mutex_map::MutexMap;
|
||||
pub use mutex_map::{Guard as MutexMapGuard, MutexMap};
|
||||
pub use rand::string as random_string;
|
||||
pub use string::{str_from_bytes, string_from_bytes};
|
||||
pub use sys::available_parallelism;
|
||||
pub use time::now_millis as millis_since_unix_epoch;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
pub fn clamp<T: Ord>(val: T, min: T, max: T) -> T { cmp::min(cmp::max(val, min), max) }
|
||||
|
||||
/// Boilerplate for wraps which are typed to never error.
|
||||
///
|
||||
/// * <https://doc.rust-lang.org/std/convert/enum.Infallible.html>
|
||||
#[must_use]
|
||||
pub fn unwrap_infallible<T>(result: Result<T, std::convert::Infallible>) -> T {
|
||||
match result {
|
||||
Ok(val) => val,
|
||||
Err(err) => match err {},
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn generate_keypair() -> Vec<u8> {
|
||||
let mut value = rand::string(8).as_bytes().to_vec();
|
||||
|
||||
+38
-15
@@ -1,20 +1,22 @@
|
||||
use std::{hash::Hash, sync::Arc};
|
||||
use std::{fmt::Debug, hash::Hash, sync::Arc};
|
||||
|
||||
type Value<Val> = tokio::sync::Mutex<Val>;
|
||||
type ArcMutex<Val> = Arc<Value<Val>>;
|
||||
type HashMap<Key, Val> = std::collections::HashMap<Key, ArcMutex<Val>>;
|
||||
type MapMutex<Key, Val> = std::sync::Mutex<HashMap<Key, Val>>;
|
||||
type Map<Key, Val> = MapMutex<Key, Val>;
|
||||
use tokio::sync::OwnedMutexGuard as Omg;
|
||||
|
||||
/// Map of Mutexes
|
||||
pub struct MutexMap<Key, Val> {
|
||||
map: Map<Key, Val>,
|
||||
}
|
||||
|
||||
pub struct Guard<Val> {
|
||||
_guard: tokio::sync::OwnedMutexGuard<Val>,
|
||||
pub struct Guard<Key, Val> {
|
||||
map: Map<Key, Val>,
|
||||
val: Omg<Val>,
|
||||
}
|
||||
|
||||
type Map<Key, Val> = Arc<MapMutex<Key, Val>>;
|
||||
type MapMutex<Key, Val> = std::sync::Mutex<HashMap<Key, Val>>;
|
||||
type HashMap<Key, Val> = std::collections::HashMap<Key, Value<Val>>;
|
||||
type Value<Val> = Arc<tokio::sync::Mutex<Val>>;
|
||||
|
||||
impl<Key, Val> MutexMap<Key, Val>
|
||||
where
|
||||
Key: Send + Hash + Eq + Clone,
|
||||
@@ -23,28 +25,38 @@ where
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: Map::<Key, Val>::new(HashMap::<Key, Val>::new()),
|
||||
map: Map::new(MapMutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn lock<K>(&self, k: &K) -> Guard<Val>
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub async fn lock<K>(&self, k: &K) -> Guard<Key, Val>
|
||||
where
|
||||
K: ?Sized + Send + Sync,
|
||||
K: ?Sized + Send + Sync + Debug,
|
||||
Key: for<'a> From<&'a K>,
|
||||
{
|
||||
let val = self
|
||||
.map
|
||||
.lock()
|
||||
.expect("map mutex locked")
|
||||
.expect("locked")
|
||||
.entry(k.into())
|
||||
.or_default()
|
||||
.clone();
|
||||
|
||||
let guard = val.lock_owned().await;
|
||||
Guard::<Val> {
|
||||
_guard: guard,
|
||||
Guard::<Key, Val> {
|
||||
map: Arc::clone(&self.map),
|
||||
val: val.lock_owned().await,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn contains(&self, k: &Key) -> bool { self.map.lock().expect("locked").contains_key(k) }
|
||||
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool { self.map.lock().expect("locked").is_empty() }
|
||||
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize { self.map.lock().expect("locked").len() }
|
||||
}
|
||||
|
||||
impl<Key, Val> Default for MutexMap<Key, Val>
|
||||
@@ -54,3 +66,14 @@ where
|
||||
{
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
||||
impl<Key, Val> Drop for Guard<Key, Val> {
|
||||
fn drop(&mut self) {
|
||||
if Arc::strong_count(Omg::mutex(&self.val)) <= 2 {
|
||||
self.map
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.retain(|_, val| !Arc::ptr_eq(val, Omg::mutex(&self.val)) || Arc::strong_count(val) > 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@ pub fn string(length: usize) -> String {
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn timepoint_secs(range: Range<u64>) -> SystemTime { SystemTime::now() + secs(range) }
|
||||
pub fn timepoint_secs(range: Range<u64>) -> SystemTime {
|
||||
SystemTime::now()
|
||||
.checked_add(secs(range))
|
||||
.expect("range does not overflow SystemTime")
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn secs(range: Range<u64>) -> Duration {
|
||||
|
||||
@@ -2,6 +2,50 @@ use crate::Result;
|
||||
|
||||
pub const EMPTY: &str = "";
|
||||
|
||||
/// Constant expression to bypass format! if the argument is a string literal
|
||||
/// but not a format string. If the literal is a format string then String is
|
||||
/// returned otherwise the input (i.e. &'static str) is returned. If multiple
|
||||
/// arguments are provided the first is assumed to be a format string.
|
||||
#[macro_export]
|
||||
macro_rules! format_maybe {
|
||||
($s:literal) => {
|
||||
if $crate::is_format!($s) { std::format!($s).into() } else { $s.into() }
|
||||
};
|
||||
|
||||
($($args:expr),*) => {
|
||||
std::format!($($args),*).into()
|
||||
};
|
||||
}
|
||||
|
||||
/// Constant expression to decide if a literal is a format string. Note: could
|
||||
/// use some improvement.
|
||||
#[macro_export]
|
||||
macro_rules! is_format {
|
||||
($s:literal) => {
|
||||
::const_str::contains!($s, "{") && ::const_str::contains!($s, "}")
|
||||
};
|
||||
}
|
||||
|
||||
/// Find the common prefix from a collection of strings and return a slice
|
||||
/// ```
|
||||
/// use conduit_core::utils::string::common_prefix;
|
||||
/// let input = ["conduwuit", "conduit", "construct"];
|
||||
/// common_prefix(&input) == "con";
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[allow(clippy::string_slice)]
|
||||
pub fn common_prefix<'a>(choice: &'a [&str]) -> &'a str {
|
||||
choice.first().map_or(EMPTY, move |best| {
|
||||
choice.iter().skip(1).fold(*best, |best, choice| {
|
||||
&best[0..choice
|
||||
.char_indices()
|
||||
.zip(best.char_indices())
|
||||
.take_while(|&(a, b)| a == b)
|
||||
.count()]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn split_once_infallible<'a>(input: &'a str, delim: &'_ str) -> (&'a str, &'a str) {
|
||||
|
||||
@@ -35,3 +35,102 @@ fn increment_wrap() {
|
||||
let res = u64::from_be_bytes(bytes);
|
||||
assert_eq!(res, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn common_prefix() {
|
||||
use utils::string;
|
||||
|
||||
let input = ["conduwuit", "conduit", "construct"];
|
||||
let output = string::common_prefix(&input);
|
||||
assert_eq!(output, "con");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn common_prefix_empty() {
|
||||
use utils::string;
|
||||
|
||||
let input = ["abcdefg", "hijklmn", "opqrstu"];
|
||||
let output = string::common_prefix(&input);
|
||||
assert_eq!(output, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn common_prefix_none() {
|
||||
use utils::string;
|
||||
|
||||
let input = [];
|
||||
let output = string::common_prefix(&input);
|
||||
assert_eq!(output, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_add() {
|
||||
use crate::checked;
|
||||
|
||||
let a = 1234;
|
||||
let res = checked!(a + 1).unwrap();
|
||||
assert_eq!(res, 1235);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "overflow")]
|
||||
fn checked_add_overflow() {
|
||||
use crate::checked;
|
||||
|
||||
let a = u64::MAX;
|
||||
let res = checked!(a + 1).expect("overflow");
|
||||
assert_eq!(res, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mutex_map_cleanup() {
|
||||
use crate::utils::MutexMap;
|
||||
|
||||
let map = MutexMap::<String, ()>::new();
|
||||
|
||||
let lock = map.lock("foo").await;
|
||||
assert!(!map.is_empty(), "map must not be empty");
|
||||
|
||||
drop(lock);
|
||||
assert!(map.is_empty(), "map must be empty");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mutex_map_contend() {
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::Barrier;
|
||||
|
||||
use crate::utils::MutexMap;
|
||||
|
||||
let map = Arc::new(MutexMap::<String, ()>::new());
|
||||
let seq = Arc::new([Barrier::new(2), Barrier::new(2)]);
|
||||
let str = "foo".to_owned();
|
||||
|
||||
let seq_ = seq.clone();
|
||||
let map_ = map.clone();
|
||||
let str_ = str.clone();
|
||||
let join_a = tokio::spawn(async move {
|
||||
let _lock = map_.lock(&str_).await;
|
||||
assert!(!map_.is_empty(), "A0 must not be empty");
|
||||
seq_[0].wait().await;
|
||||
assert!(map_.contains(&str_), "A1 must contain key");
|
||||
});
|
||||
|
||||
let seq_ = seq.clone();
|
||||
let map_ = map.clone();
|
||||
let str_ = str.clone();
|
||||
let join_b = tokio::spawn(async move {
|
||||
let _lock = map_.lock(&str_).await;
|
||||
assert!(!map_.is_empty(), "B0 must not be empty");
|
||||
seq_[1].wait().await;
|
||||
assert!(map_.contains(&str_), "B1 must contain key");
|
||||
});
|
||||
|
||||
seq[0].wait().await;
|
||||
assert!(map.contains(&str), "Must contain key");
|
||||
seq[1].wait().await;
|
||||
|
||||
tokio::try_join!(join_b, join_a).expect("joined");
|
||||
assert!(map.is_empty(), "Must be empty");
|
||||
}
|
||||
|
||||
+79
-2
@@ -1,8 +1,8 @@
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(clippy::as_conversions)]
|
||||
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
|
||||
pub fn now_millis() -> u64 {
|
||||
UNIX_EPOCH
|
||||
.elapsed()
|
||||
@@ -26,3 +26,80 @@ pub fn format(ts: SystemTime, str: &str) -> String {
|
||||
let dt: DateTime<Utc> = ts.into();
|
||||
dt.format(str).to_string()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::as_conversions, clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
pub fn pretty(d: Duration) -> String {
|
||||
use Unit::*;
|
||||
|
||||
let fmt = |w, f, u| format!("{w}.{f} {u}");
|
||||
let gen64 = |w, f, u| fmt(w, (f * 100.0) as u32, u);
|
||||
let gen128 = |w, f, u| gen64(u64::try_from(w).expect("u128 to u64"), f, u);
|
||||
match whole_and_frac(d) {
|
||||
(Days(whole), frac) => gen64(whole, frac, "days"),
|
||||
(Hours(whole), frac) => gen64(whole, frac, "hours"),
|
||||
(Mins(whole), frac) => gen64(whole, frac, "minutes"),
|
||||
(Secs(whole), frac) => gen64(whole, frac, "seconds"),
|
||||
(Millis(whole), frac) => gen128(whole, frac, "milliseconds"),
|
||||
(Micros(whole), frac) => gen128(whole, frac, "microseconds"),
|
||||
(Nanos(whole), frac) => gen128(whole, frac, "nanoseconds"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a pair of (whole part, frac part) from a duration where. The whole
|
||||
/// part is the largest Unit containing a non-zero value, the frac part is a
|
||||
/// rational remainder left over.
|
||||
#[must_use]
|
||||
#[allow(clippy::as_conversions, clippy::cast_precision_loss)]
|
||||
pub fn whole_and_frac(d: Duration) -> (Unit, f64) {
|
||||
use Unit::*;
|
||||
|
||||
let whole = whole_unit(d);
|
||||
(
|
||||
whole,
|
||||
match whole {
|
||||
Days(_) => (d.as_secs() % 86_400) as f64 / 86_400.0,
|
||||
Hours(_) => (d.as_secs() % 3_600) as f64 / 3_600.0,
|
||||
Mins(_) => (d.as_secs() % 60) as f64 / 60.0,
|
||||
Secs(_) => f64::from(d.subsec_millis()) / 1000.0,
|
||||
Millis(_) => f64::from(d.subsec_micros()) / 1000.0,
|
||||
Micros(_) => f64::from(d.subsec_nanos()) / 1000.0,
|
||||
Nanos(_) => 0.0,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the largest Unit which represents the duration. The value is
|
||||
/// rounded-down, but never zero.
|
||||
#[must_use]
|
||||
pub fn whole_unit(d: Duration) -> Unit {
|
||||
use Unit::*;
|
||||
|
||||
match d.as_secs() {
|
||||
86_400.. => Days(d.as_secs() / 86_400),
|
||||
3_600..=86_399 => Hours(d.as_secs() / 3_600),
|
||||
60..=3_599 => Mins(d.as_secs() / 60),
|
||||
|
||||
_ => match d.as_micros() {
|
||||
1_000_000.. => Secs(d.as_secs()),
|
||||
1_000..=999_999 => Millis(d.subsec_millis().into()),
|
||||
|
||||
_ => match d.as_nanos() {
|
||||
1_000.. => Micros(d.subsec_micros().into()),
|
||||
|
||||
_ => Nanos(d.subsec_nanos().into()),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
||||
pub enum Unit {
|
||||
Days(u64),
|
||||
Hours(u64),
|
||||
Mins(u64),
|
||||
Secs(u64),
|
||||
Millis(u128),
|
||||
Micros(u128),
|
||||
Nanos(u128),
|
||||
}
|
||||
|
||||
+7
-1
@@ -27,5 +27,11 @@ fn init_user_agent() -> String { format!("{}/{}", name(), version()) }
|
||||
fn init_version() -> String {
|
||||
option_env!("CONDUWUIT_VERSION_EXTRA")
|
||||
.or(option_env!("CONDUIT_VERSION_EXTRA"))
|
||||
.map_or(SEMANTIC.to_owned(), |extra| format!("{SEMANTIC} ({extra})"))
|
||||
.map_or(SEMANTIC.to_owned(), |extra| {
|
||||
if extra.is_empty() {
|
||||
SEMANTIC.to_owned()
|
||||
} else {
|
||||
format!("{SEMANTIC} ({extra})")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ zstd_compression = [
|
||||
|
||||
[dependencies]
|
||||
conduit-core.workspace = true
|
||||
const-str.workspace = true
|
||||
log.workspace = true
|
||||
ruma.workspace = true
|
||||
rust-rocksdb.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{cork::Cork, maps, maps::Maps, Engine, Map};
|
||||
|
||||
pub struct Database {
|
||||
pub db: Arc<Engine>,
|
||||
pub map: Maps,
|
||||
map: Maps,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
|
||||
+22
-21
@@ -30,6 +30,7 @@ pub struct Engine {
|
||||
pub(crate) type Db = DBWithThreadMode<MultiThreaded>;
|
||||
|
||||
impl Engine {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn open(server: &Arc<Server>) -> Result<Arc<Self>> {
|
||||
let config = &server.config;
|
||||
let cache_capacity_bytes = config.db_cache_capacity_mb * 1024.0 * 1024.0;
|
||||
@@ -51,7 +52,7 @@ impl Engine {
|
||||
if config.rocksdb_repair {
|
||||
warn!("Starting database repair. This may take a long time...");
|
||||
if let Err(e) = Db::repair(&db_opts, &config.database_path) {
|
||||
error!("Repair failed: {:?}", e);
|
||||
error!("Repair failed: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,9 +77,9 @@ impl Engine {
|
||||
|
||||
let db = res.or_else(or_else)?;
|
||||
info!(
|
||||
"Opened database at sequence number {} in {:?}",
|
||||
db.latest_sequence_number(),
|
||||
load_time.elapsed()
|
||||
sequence = %db.latest_sequence_number(),
|
||||
time = ?load_time.elapsed(),
|
||||
"Opened database."
|
||||
);
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
@@ -93,15 +94,16 @@ impl Engine {
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub(crate) fn open_cf(&self, name: &str) -> Result<Arc<BoundColumnFamily<'_>>> {
|
||||
let mut cfs = self.cfs.lock().expect("locked");
|
||||
if !cfs.contains(name) {
|
||||
debug!("Creating new column family in database: {}", name);
|
||||
debug!("Creating new column family in database: {name}");
|
||||
|
||||
let mut col_cache = self.col_cache.write().expect("locked");
|
||||
let opts = cf_options(&self.server.config, name, self.opts.clone(), &mut col_cache);
|
||||
if let Err(e) = self.db.create_cf(name, &opts) {
|
||||
error!("Failed to create new column family: {e}");
|
||||
error!(?name, "Failed to create new column family: {e}");
|
||||
return or_else(e);
|
||||
}
|
||||
|
||||
@@ -134,34 +136,34 @@ impl Engine {
|
||||
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
pub fn memory_usage(&self) -> Result<String> {
|
||||
let mut res = String::new();
|
||||
let stats = get_memory_usage_stats(Some(&[&self.db]), Some(&[&self.row_cache])).or_else(or_else)?;
|
||||
let mibs = |input| f64::from(u32::try_from(input / 1024).unwrap_or(0)) / 1024.0;
|
||||
writeln!(
|
||||
res,
|
||||
"Memory buffers: {:.2} MiB\nPending write: {:.2} MiB\nTable readers: {:.2} MiB\nRow cache: {:.2} MiB",
|
||||
stats.mem_table_total as f64 / 1024.0 / 1024.0,
|
||||
stats.mem_table_unflushed as f64 / 1024.0 / 1024.0,
|
||||
stats.mem_table_readers_total as f64 / 1024.0 / 1024.0,
|
||||
self.row_cache.get_usage() as f64 / 1024.0 / 1024.0,
|
||||
)
|
||||
.expect("should be able to write to string buffer");
|
||||
mibs(stats.mem_table_total),
|
||||
mibs(stats.mem_table_unflushed),
|
||||
mibs(stats.mem_table_readers_total),
|
||||
mibs(u64::try_from(self.row_cache.get_usage())?),
|
||||
)?;
|
||||
|
||||
for (name, cache) in &*self.col_cache.read().expect("locked") {
|
||||
writeln!(res, "{} cache: {:.2} MiB", name, cache.get_usage() as f64 / 1024.0 / 1024.0,)
|
||||
.expect("should be able to write to string buffer");
|
||||
writeln!(res, "{name} cache: {:.2} MiB", mibs(u64::try_from(cache.get_usage())?))?;
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub fn cleanup(&self) -> Result<()> {
|
||||
debug!("Running flush_opt");
|
||||
let flushoptions = rocksdb::FlushOptions::default();
|
||||
result(DBCommon::flush_opt(&self.db, &flushoptions))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn backup(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = &self.server.config;
|
||||
let path = config.database_backup_path.as_ref();
|
||||
@@ -214,8 +216,7 @@ impl Engine {
|
||||
rfc2822_from_seconds(info.timestamp),
|
||||
info.size,
|
||||
info.num_files,
|
||||
)
|
||||
.expect("should be able to write to string buffer");
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
@@ -226,16 +227,16 @@ impl Engine {
|
||||
Err(e) => Ok(String::from(e)),
|
||||
Ok(files) => {
|
||||
let mut res = String::new();
|
||||
writeln!(res, "| lev | sst | keys | dels | size | column |").expect("written to string buffer");
|
||||
writeln!(res, "| ---: | :--- | ---: | ---: | ---: | :--- |").expect("written to string buffer");
|
||||
writeln!(res, "| lev | sst | keys | dels | size | column |")?;
|
||||
writeln!(res, "| ---: | :--- | ---: | ---: | ---: | :--- |")?;
|
||||
for file in files {
|
||||
writeln!(
|
||||
res,
|
||||
"| {} | {:<13} | {:7}+ | {:4}- | {:9} | {} |",
|
||||
file.level, file.name, file.num_entries, file.num_deletions, file.size, file.column_family_name,
|
||||
)
|
||||
.expect("should be able to writeln to string buffer");
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
},
|
||||
}
|
||||
|
||||
+1
-1
@@ -233,7 +233,7 @@ fn open(db: &Arc<Engine>, name: &str) -> Result<Arc<ColumnFamily>> {
|
||||
// closing the database (dropping `Engine`). Since `Arc<Engine>` is a sibling
|
||||
// member along with this handle in `Map`, that is prevented.
|
||||
Ok(unsafe {
|
||||
Arc::decrement_strong_count(cf_ptr);
|
||||
Arc::increment_strong_count(cf_ptr);
|
||||
Arc::from_raw(cf_ptr)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ pub type Maps = BTreeMap<String, Arc<Map>>;
|
||||
|
||||
pub(crate) fn open(db: &Arc<Engine>) -> Result<Maps> { open_list(db, MAPS) }
|
||||
|
||||
#[tracing::instrument(skip_all, level = "debug")]
|
||||
pub(crate) fn open_list(db: &Arc<Engine>, maps: &[&str]) -> Result<Maps> {
|
||||
Ok(maps
|
||||
.iter()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use conduit::Result;
|
||||
use conduit::{err, Result};
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn result<T>(r: std::result::Result<T, rocksdb::Error>) -> Result<T, conduit::Error> {
|
||||
@@ -10,4 +10,7 @@ pub(crate) fn and_then<T>(t: T) -> Result<T, conduit::Error> { Ok(t) }
|
||||
|
||||
pub(crate) fn or_else<T>(e: rocksdb::Error) -> Result<T, conduit::Error> { Err(map_err(e)) }
|
||||
|
||||
pub(crate) fn map_err(e: rocksdb::Error) -> conduit::Error { conduit::Error::Database(e.into_string()) }
|
||||
pub(crate) fn map_err(e: rocksdb::Error) -> conduit::Error {
|
||||
let string = e.into_string();
|
||||
err!(Database(error!("{string}")))
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user