mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-04-14 08:05:53 +00:00
Compare commits
68 Commits
ginger/rem
...
aranje/ill
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
164b59a30c | ||
|
|
96fba8fe12 | ||
|
|
565d29448b | ||
|
|
36988b6d73 | ||
|
|
e7b1de330b | ||
|
|
240964ecfd | ||
|
|
67e67d63e3 | ||
|
|
4740e98909 | ||
|
|
5c13f77899 | ||
|
|
e39a95cbea | ||
|
|
307b4c6342 | ||
|
|
deafc4bcf0 | ||
|
|
ecfb105b44 | ||
|
|
cb1a24317e | ||
|
|
0de239a382 | ||
|
|
f49be74edc | ||
|
|
482afdbd4d | ||
|
|
9ba500a141 | ||
|
|
2325e8fa4c | ||
|
|
6906d63013 | ||
|
|
16de2a2cc0 | ||
|
|
108a4fe336 | ||
|
|
83396db5de | ||
|
|
839138c02e | ||
|
|
e03c90c2ac | ||
|
|
379ef5014c | ||
|
|
2ab177f100 | ||
|
|
a818f51396 | ||
|
|
09bfe79a44 | ||
|
|
d041adadc8 | ||
|
|
189ed1c394 | ||
|
|
36c32938ae | ||
|
|
915643c965 | ||
|
|
4063b2c7da | ||
|
|
943bd81ce9 | ||
|
|
2942d9133e | ||
|
|
18a7a85fe4 | ||
|
|
0fdb1be938 | ||
|
|
867a3ac376 | ||
|
|
7a6eff091a | ||
|
|
c278663f65 | ||
|
|
c822c945e7 | ||
|
|
6eb3dc1f9d | ||
|
|
789ec71b75 | ||
|
|
1cfa3ff10b | ||
|
|
02cf6b5695 | ||
|
|
4cc4893376 | ||
|
|
7643b64f60 | ||
|
|
3d9fd34012 | ||
|
|
630963d6e1 | ||
|
|
36da6f5bf3 | ||
|
|
462ef63945 | ||
|
|
46bcfe5605 | ||
|
|
16321cf467 | ||
|
|
4d59e07006 | ||
|
|
ec5f50c68e | ||
|
|
db1b08532e | ||
|
|
d8f67e3b46 | ||
|
|
2124fcf325 | ||
|
|
38b4065270 | ||
|
|
2e62ca93a8 | ||
|
|
b7a6c819b7 | ||
|
|
eccc878ee9 | ||
|
|
8b762cf2e6 | ||
|
|
1ce9ae2cbf | ||
|
|
6a3370005e | ||
|
|
675cfb964a | ||
|
|
09312791a7 |
2
.envrc
2
.envrc
@@ -2,7 +2,7 @@
|
||||
|
||||
dotenv_if_exists
|
||||
|
||||
if [ -f /etc/os-release ] && grep -q '^ID=nixos' /etc/os-release; then
|
||||
if command -v nix >/dev/null 2>&1; then
|
||||
use flake ".#${DIRENV_DEVSHELL:-default}"
|
||||
fi
|
||||
|
||||
|
||||
@@ -149,37 +149,6 @@ runs:
|
||||
- name: Setup sccache
|
||||
uses: https://git.tomfos.tr/tom/sccache-action@v1
|
||||
|
||||
- name: Cache dependencies
|
||||
id: deps-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
target/**/.fingerprint
|
||||
target/**/deps
|
||||
target/**/*.d
|
||||
target/**/.cargo-lock
|
||||
target/**/CACHEDIR.TAG
|
||||
target/**/.rustc_info.json
|
||||
/timelord/
|
||||
# Dependencies cache - based on Cargo.lock, survives source code changes
|
||||
key: >-
|
||||
continuwuity-deps-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
continuwuity-deps-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-
|
||||
|
||||
- name: Cache incremental compilation
|
||||
id: incremental-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
target/**/incremental
|
||||
# Incremental cache - based on source code changes
|
||||
key: >-
|
||||
continuwuity-incremental-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }}-${{ hashFiles('**/*.rs', '**/Cargo.toml') }}
|
||||
restore-keys: |
|
||||
continuwuity-incremental-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }}-
|
||||
continuwuity-incremental-${{ steps.runner-os.outputs.slug }}-${{ steps.runner-os.outputs.arch }}-${{ steps.rust-setup.outputs.version }}${{ inputs.cache-key-suffix && format('-{0}', inputs.cache-key-suffix) || '' }}-
|
||||
|
||||
- name: End build cache restore group
|
||||
shell: bash
|
||||
run: echo "::endgroup::"
|
||||
|
||||
@@ -16,48 +16,19 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: false
|
||||
fetch-single-branch: true
|
||||
submodules: false
|
||||
persist-credentials: true
|
||||
token: ${{ secrets.FORGEJO_TOKEN }}
|
||||
|
||||
- uses: https://github.com/cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
|
||||
- name: Install Lix
|
||||
uses: https://github.com/samueldr/lix-gha-installer-action@f5e94192f565f53d84f41a056956dc0d3183b343
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
# We can skip getting a toolchain hash if this was ran as a dispatch with the intent
|
||||
# to update just the rocksdb hash. If this was ran as a dispatch and the toolchain
|
||||
# files are changed, we still update them, as well as the rocksdb import.
|
||||
- name: Detect changed files
|
||||
id: changes
|
||||
run: |
|
||||
git fetch origin ${{ github.base_ref }} --depth=1 || true
|
||||
if [ -n "${{ github.event.pull_request.base.sha }}" ]; then
|
||||
base=${{ github.event.pull_request.base.sha }}
|
||||
else
|
||||
base=$(git rev-parse HEAD~1)
|
||||
fi
|
||||
echo "Base: $base"
|
||||
echo "HEAD: $(git rev-parse HEAD)"
|
||||
git diff --name-only $base HEAD > changed_files.txt
|
||||
echo "detected changes in $(cat changed_files.txt)"
|
||||
# Join files with commas
|
||||
files=$(paste -sd, changed_files.txt)
|
||||
echo "files=$files" >> $FORGEJO_OUTPUT
|
||||
|
||||
- name: Debug output
|
||||
run: |
|
||||
echo "State of output"
|
||||
echo "Changed files: ${{ steps.changes.outputs.files }}"
|
||||
extra_nix_config: experimental-features = nix-command flakes flake-self-attrs
|
||||
|
||||
- name: Get new toolchain hash
|
||||
if: contains(steps.changes.outputs.files, 'Cargo.toml') || contains(steps.changes.outputs.files, 'Cargo.lock') || contains(steps.changes.outputs.files, 'rust-toolchain.toml')
|
||||
run: |
|
||||
# Set the current sha256 to an empty hash to make `nix build` calculate a new one
|
||||
awk '/fromToolchainFile *\{/{found=1; print; next} found && /sha256 =/{sub(/sha256 = .*/, "sha256 = lib.fakeSha256;"); found=0} 1' nix/packages/rust.nix > temp.nix
|
||||
mv temp.nix nix/packages/rust.nix
|
||||
awk '/fromToolchainFile *\{/{found=1; print; next} found && /sha256 =/{sub(/sha256 = .*/, "sha256 = lib.fakeSha256;"); found=0} 1' nix/rust.nix > temp.nix
|
||||
mv temp.nix nix/rust.nix
|
||||
|
||||
# Build continuwuity and filter for the new hash
|
||||
# We do `|| true` because we want this to fail without stopping the workflow
|
||||
@@ -65,36 +36,17 @@ jobs:
|
||||
|
||||
# Place the new hash in place of the empty hash
|
||||
new_hash=$(cat new_toolchain_hash.txt)
|
||||
sed -i "s|lib.fakeSha256|\"$new_hash\"|" nix/packages/rust.nix
|
||||
sed -i "s|lib.fakeSha256|\"$new_hash\"|" nix/rust.nix
|
||||
|
||||
echo "New hash:"
|
||||
awk -F'"' '/fromToolchainFile/{found=1; next} found && /sha256 =/{print $2; found=0}' nix/packages/rust.nix
|
||||
awk -F'"' '/fromToolchainFile/{found=1; next} found && /sha256 =/{print $2; found=0}' nix/rust.nix
|
||||
echo "Expected new hash:"
|
||||
cat new_toolchain_hash.txt
|
||||
|
||||
rm new_toolchain_hash.txt
|
||||
|
||||
- name: Get new rocksdb hash
|
||||
if: contains(steps.changes.outputs.files, '.nix') || contains(steps.changes.outputs.files, 'flake.lock')
|
||||
run: |
|
||||
# Set the current sha256 to an empty hash to make `nix build` calculate a new one
|
||||
awk '/repo = "rocksdb";/{found=1; print; next} found && /sha256 =/{sub(/sha256 = .*/, "sha256 = lib.fakeSha256;"); found=0} 1' nix/packages/rocksdb/package.nix > temp.nix
|
||||
mv temp.nix nix/packages/rocksdb/package.nix
|
||||
|
||||
# Build continuwuity and filter for the new hash
|
||||
# We do `|| true` because we want this to fail without stopping the workflow
|
||||
nix build .#default 2>&1 | tee >(grep 'got:' | awk '{print $2}' > new_rocksdb_hash.txt) || true
|
||||
|
||||
# Place the new hash in place of the empty hash
|
||||
new_hash=$(cat new_rocksdb_hash.txt)
|
||||
sed -i "s|lib.fakeSha256|\"$new_hash\"|" nix/packages/rocksdb/package.nix
|
||||
|
||||
echo "New hash:"
|
||||
awk -F'"' '/repo = "rocksdb";/{found=1; next} found && /sha256 =/{print $2; found=0}' nix/packages/rocksdb/package.nix
|
||||
echo "Expected new hash:"
|
||||
cat new_rocksdb_hash.txt
|
||||
|
||||
rm new_rocksdb_hash.txt
|
||||
- name: Update rocksdb
|
||||
run: nix run .#update-rocksdb
|
||||
|
||||
- name: Show diff
|
||||
run: git diff flake.nix nix
|
||||
|
||||
271
Cargo.lock
generated
271
Cargo.lock
generated
@@ -785,6 +785,12 @@ dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cesu8"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
@@ -909,6 +915,16 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compression-codecs"
|
||||
version = "0.4.37"
|
||||
@@ -948,10 +964,12 @@ dependencies = [
|
||||
"conduwuit_build_metadata",
|
||||
"conduwuit_core",
|
||||
"conduwuit_database",
|
||||
"conduwuit_macros",
|
||||
"conduwuit_router",
|
||||
"conduwuit_service",
|
||||
"console-subscriber",
|
||||
"const-str",
|
||||
"ctor",
|
||||
"hardened_malloc-rs",
|
||||
"log",
|
||||
"opentelemetry",
|
||||
@@ -981,9 +999,11 @@ dependencies = [
|
||||
"conduwuit_macros",
|
||||
"conduwuit_service",
|
||||
"const-str",
|
||||
"ctor",
|
||||
"futures",
|
||||
"lettre",
|
||||
"log",
|
||||
"resolvematrix",
|
||||
"ruma",
|
||||
"serde-saphyr",
|
||||
"serde_json",
|
||||
@@ -1003,8 +1023,10 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"conduwuit_core",
|
||||
"conduwuit_macros",
|
||||
"conduwuit_service",
|
||||
"const-str",
|
||||
"ctor",
|
||||
"futures",
|
||||
"hmac",
|
||||
"http",
|
||||
@@ -1015,7 +1037,7 @@ dependencies = [
|
||||
"lettre",
|
||||
"log",
|
||||
"rand 0.10.0",
|
||||
"reqwest",
|
||||
"reqwest 0.12.28",
|
||||
"ruma",
|
||||
"serde",
|
||||
"serde_html_form",
|
||||
@@ -1030,6 +1052,7 @@ name = "conduwuit_build_metadata"
|
||||
version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"built",
|
||||
"cargo_metadata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1073,7 +1096,8 @@ dependencies = [
|
||||
"rand 0.10.0",
|
||||
"rand_core 0.6.4",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"reqwest 0.12.28",
|
||||
"resolvematrix",
|
||||
"ring",
|
||||
"ruma",
|
||||
"sanitize-filename",
|
||||
@@ -1102,7 +1126,9 @@ version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"conduwuit_core",
|
||||
"conduwuit_macros",
|
||||
"const-str",
|
||||
"ctor",
|
||||
"futures",
|
||||
"log",
|
||||
"minicbor",
|
||||
@@ -1118,6 +1144,7 @@ dependencies = [
|
||||
name = "conduwuit_macros"
|
||||
version = "0.5.7-alpha.1"
|
||||
dependencies = [
|
||||
"cargo_toml",
|
||||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1136,9 +1163,11 @@ dependencies = [
|
||||
"conduwuit_admin",
|
||||
"conduwuit_api",
|
||||
"conduwuit_core",
|
||||
"conduwuit_macros",
|
||||
"conduwuit_service",
|
||||
"conduwuit_web",
|
||||
"const-str",
|
||||
"ctor",
|
||||
"futures",
|
||||
"http",
|
||||
"http-body-util",
|
||||
@@ -1169,7 +1198,9 @@ dependencies = [
|
||||
"bytes",
|
||||
"conduwuit_core",
|
||||
"conduwuit_database",
|
||||
"conduwuit_macros",
|
||||
"const-str",
|
||||
"ctor",
|
||||
"either",
|
||||
"futures",
|
||||
"governor",
|
||||
@@ -1187,7 +1218,8 @@ dependencies = [
|
||||
"rand 0.10.0",
|
||||
"recaptcha-verify",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"reqwest 0.12.28",
|
||||
"resolvematrix",
|
||||
"ruma",
|
||||
"rustyline-async",
|
||||
"sd-notify",
|
||||
@@ -1291,7 +1323,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "continuwuity-admin-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"ruma-common",
|
||||
"serde",
|
||||
@@ -1726,7 +1758,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "draupnir-antispam"
|
||||
version = "0.1.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"ruma-common",
|
||||
"serde",
|
||||
@@ -2858,6 +2890,50 @@ version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
||||
dependencies = [
|
||||
"cesu8",
|
||||
"cfg-if",
|
||||
"combine",
|
||||
"jni-sys 0.3.1",
|
||||
"log",
|
||||
"thiserror 1.0.69",
|
||||
"walkdir",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
|
||||
dependencies = [
|
||||
"jni-sys 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
|
||||
dependencies = [
|
||||
"jni-sys-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys-macros"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.34"
|
||||
@@ -3241,7 +3317,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "meowlnir-antispam"
|
||||
version = "0.1.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"ruma-common",
|
||||
"serde",
|
||||
@@ -3743,7 +3819,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"opentelemetry",
|
||||
"reqwest",
|
||||
"reqwest 0.12.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3758,7 +3834,7 @@ dependencies = [
|
||||
"opentelemetry-proto",
|
||||
"opentelemetry_sdk",
|
||||
"prost",
|
||||
"reqwest",
|
||||
"reqwest 0.12.28",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tonic",
|
||||
@@ -4229,6 +4305,7 @@ version = "0.11.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"bytes",
|
||||
"getrandom 0.3.4",
|
||||
"lru-slab",
|
||||
@@ -4425,7 +4502,7 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "409bf11a93fe93093f3c0254aab67576524f1e0524692615b5b63091dbc88a79"
|
||||
dependencies = [
|
||||
"reqwest",
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
@@ -4516,12 +4593,62 @@ dependencies = [
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"rustls-platform-verifier",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resolv-conf"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7"
|
||||
|
||||
[[package]]
|
||||
name = "resolvematrix"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52dfcc6f56a81348db1fc6591356cfea9dc840408c75553b2fe225f86de43274"
|
||||
dependencies = [
|
||||
"hickory-resolver",
|
||||
"reqwest 0.13.2",
|
||||
"serde",
|
||||
"thiserror 2.0.18",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.53"
|
||||
@@ -4545,7 +4672,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.10.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"continuwuity-admin-api",
|
||||
@@ -4568,7 +4695,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.10.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4580,7 +4707,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.18.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"assign",
|
||||
@@ -4603,7 +4730,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.13.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64 0.22.1",
|
||||
@@ -4635,7 +4762,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.28.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"indexmap",
|
||||
@@ -4660,7 +4787,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"headers",
|
||||
@@ -4682,7 +4809,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.9.5"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror 2.0.18",
|
||||
@@ -4691,7 +4818,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-identity-service-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4701,7 +4828,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.13.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro-crate",
|
||||
@@ -4716,7 +4843,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4728,7 +4855,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.15.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a97b91adcc012ef04991d823b8b5a79c6686ae48#a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=1415caf8a32af4d943580c5ea4e12be1974593c2#1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
@@ -4859,6 +4986,33 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
|
||||
dependencies = [
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"jni",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"rustls-platform-verifier-android",
|
||||
"rustls-webpki",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier-android"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.9"
|
||||
@@ -4991,7 +5145,7 @@ checksum = "d92d893ba7469d361a6958522fa440e4e2bc8bf4c5803cd1bf40b9af63f8f9a8"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"httpdate",
|
||||
"reqwest",
|
||||
"reqwest 0.12.28",
|
||||
"rustls",
|
||||
"sentry-backtrace",
|
||||
"sentry-contexts",
|
||||
@@ -6492,6 +6646,15 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-root-certs"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.6"
|
||||
@@ -6556,6 +6719,15 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@@ -6601,6 +6773,21 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
@@ -6649,6 +6836,12 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -6667,6 +6860,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -6685,6 +6884,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -6715,6 +6920,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -6733,6 +6944,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -6751,6 +6968,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -6769,6 +6992,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
|
||||
@@ -344,7 +344,7 @@ version = "0.1.2"
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
||||
#branch = "conduwuit-changes"
|
||||
rev = "a97b91adcc012ef04991d823b8b5a79c6686ae48"
|
||||
rev = "1415caf8a32af4d943580c5ea4e12be1974593c2"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -383,7 +383,8 @@ features = [
|
||||
"unstable-pdu",
|
||||
"unstable-msc4155",
|
||||
"unstable-msc4143", # livekit well_known response
|
||||
"unstable-msc4284"
|
||||
"unstable-msc4284",
|
||||
"unstable-msc4439", # pgp_key in .well_known/matrix/support
|
||||
]
|
||||
|
||||
[workspace.dependencies.rust-rocksdb]
|
||||
@@ -569,6 +570,9 @@ features = ["std"]
|
||||
[workspace.dependencies.nonzero_ext]
|
||||
version = "0.3.0"
|
||||
|
||||
[workspace.dependencies.resolvematrix]
|
||||
version = "0.0.3"
|
||||
|
||||
#
|
||||
# Patches
|
||||
#
|
||||
|
||||
1
changelog.d/+7409b1db.feature.md
Normal file
1
changelog.d/+7409b1db.feature.md
Normal file
@@ -0,0 +1 @@
|
||||
Added support for requiring users to accept terms and conditions when registering.
|
||||
1
changelog.d/1505.refactor.md
Normal file
1
changelog.d/1505.refactor.md
Normal file
@@ -0,0 +1 @@
|
||||
Rewrite the resolver service to use an external crate to make it easier to maintain. Rewrite by @s1lv3r, crate by @Jade
|
||||
1
changelog.d/1596.bugfix
Normal file
1
changelog.d/1596.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Refactored nix package. Breaking, since `all-features` package no longer exists. Continuwuity is now built with jemalloc and liburing by default. Contributed by @Henry-Hiles (QuadRadical).
|
||||
2
changelog.d/1609.feature.md
Normal file
2
changelog.d/1609.feature.md
Normal file
@@ -0,0 +1,2 @@
|
||||
Add new config option for [MSC4439](https://github.com/matrix-org/matrix-spec-proposals/pull/4439)
|
||||
PGP key URIs. Contributed by LogN.
|
||||
1
changelog.d/1615.bugfix
Normal file
1
changelog.d/1615.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Fixed resolving IP of servers that only use SRV delegation. Contributed by @tulir.
|
||||
1
changelog.d/1620.misc
Normal file
1
changelog.d/1620.misc
Normal file
@@ -0,0 +1 @@
|
||||
Fixed compiler warning in cf_opts.rs when building in release. Contributed by @ezera.
|
||||
1
changelog.d/1623.bugfix
Normal file
1
changelog.d/1623.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Fixed "Sender must be a local user" error for make_join, make_knock, and make_leave federation routes. Contributed by @nex.
|
||||
1
changelog.d/1629.feature.md
Normal file
1
changelog.d/1629.feature.md
Normal file
@@ -0,0 +1 @@
|
||||
Added admin commands to get build information and features. Contributed by @Jade
|
||||
1
changelog.d/1630.bugfix
Normal file
1
changelog.d/1630.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Fixed restricted joins not being signed when we are being used as an authorising server. Contributed by @nex, reported by [vel](matrix:u/vel:nhjkl.com?action=chat).
|
||||
@@ -523,6 +523,18 @@
|
||||
#
|
||||
#recaptcha_private_site_key =
|
||||
|
||||
# Policy documents, such as terms and conditions or a privacy policy,
|
||||
# which users must agree to when registering an account.
|
||||
#
|
||||
# Example:
|
||||
# ```ignore
|
||||
# [global.registration_terms.privacy_policy]
|
||||
# en = { name = "Privacy Policy", url = "https://homeserver.example/en/privacy_policy.html" }
|
||||
# es = { name = "Política de Privacidad", url = "https://homeserver.example/es/privacy_policy.html" }
|
||||
# ```
|
||||
#
|
||||
#registration_terms = {}
|
||||
|
||||
# Controls whether encrypted rooms and events are allowed.
|
||||
#
|
||||
#allow_encryption = true
|
||||
@@ -1787,11 +1799,9 @@
|
||||
#stream_amplification = 1024
|
||||
|
||||
# Number of sender task workers; determines sender parallelism. Default is
|
||||
# '0' which means the value is determined internally, likely matching the
|
||||
# number of tokio worker-threads or number of cores, etc. Override by
|
||||
# setting a non-zero value.
|
||||
# core count. Override by setting a different value.
|
||||
#
|
||||
#sender_workers = 0
|
||||
#sender_workers = core count
|
||||
|
||||
# Enables listener sockets; can be set to false to disable listening. This
|
||||
# option is intended for developer/diagnostic purposes only.
|
||||
@@ -1869,6 +1879,11 @@
|
||||
#
|
||||
#support_mxid =
|
||||
|
||||
# PGP key URI for server support contacts, to be served as part of the
|
||||
# MSC1929 server support endpoint.
|
||||
#
|
||||
#support_pgp_key =
|
||||
|
||||
# **DEPRECATED**: Use `[global.matrix_rtc].foci` instead.
|
||||
#
|
||||
# A list of MatrixRTC foci URLs which will be served as part of the
|
||||
|
||||
@@ -15,13 +15,13 @@ ARG LLVM_VERSION=21
|
||||
|
||||
# Install repo tools
|
||||
# Line one: compiler tools
|
||||
# Line two: curl, for downloading binaries
|
||||
# Line two: curl, for downloading binaries and wget because llvm.sh is broken with curl
|
||||
# Line three: for xx-verify
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
apt-get update && apt-get install -y \
|
||||
pkg-config make jq \
|
||||
curl git software-properties-common \
|
||||
wget curl git software-properties-common \
|
||||
file
|
||||
|
||||
# LLVM packages
|
||||
|
||||
@@ -133,6 +133,18 @@ ### `!admin query pusher get-pushers`
|
||||
|
||||
Returns all the pushers for the user
|
||||
|
||||
### `!admin query pusher delete-pusher`
|
||||
|
||||
Deletes a specific pusher by ID
|
||||
|
||||
### `!admin query pusher delete-all-user`
|
||||
|
||||
Deletes all pushers for a user
|
||||
|
||||
### `!admin query pusher delete-all-device`
|
||||
|
||||
Deletes all pushers associated with a device ID
|
||||
|
||||
## `!admin query short`
|
||||
|
||||
short service
|
||||
|
||||
@@ -47,3 +47,11 @@ ## `!admin server restart`
|
||||
## `!admin server shutdown`
|
||||
|
||||
Shutdown the server
|
||||
|
||||
## `!admin server list-features`
|
||||
|
||||
List features built into the server
|
||||
|
||||
## `!admin server build-info`
|
||||
|
||||
Build information
|
||||
|
||||
@@ -157,3 +157,7 @@ ## `!admin users force-join-all-local-users`
|
||||
At least 1 server admin must be in the room to reduce abuse.
|
||||
|
||||
Requires the `--yes-i-want-to-do-this` flag.
|
||||
|
||||
## `!admin users reset-push-rules`
|
||||
|
||||
Resets the push-rules (notification settings) of the target user to the server defaults
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
url = "github:edolstra/flake-compat?ref=master";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
outputs =
|
||||
@@ -37,10 +36,10 @@
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [ ./nix ];
|
||||
systems = [
|
||||
# good support
|
||||
"x86_64-linux"
|
||||
# support untested but theoretically there
|
||||
"aarch64-linux"
|
||||
# support untested but theoretically there
|
||||
"aarch64-darwin"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
self',
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
uwulib = inputs.self.uwulib.init pkgs;
|
||||
|
||||
rocksdbAllFeatures = self'.packages.rocksdb.override {
|
||||
enableJemalloc = true;
|
||||
};
|
||||
|
||||
commonAttrs = (uwulib.build.commonAttrs { }) // {
|
||||
buildInputs = [
|
||||
pkgs.liburing
|
||||
pkgs.rust-jemalloc-sys-unprefixed
|
||||
rocksdbAllFeatures
|
||||
];
|
||||
nativeBuildInputs = [
|
||||
pkgs.pkg-config
|
||||
# bindgen needs the build platform's libclang. Apparently due to "splicing
|
||||
# weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the
|
||||
# right thing here.
|
||||
pkgs.rustPlatform.bindgenHook
|
||||
];
|
||||
env = {
|
||||
LIBCLANG_PATH = lib.makeLibraryPath [ pkgs.llvmPackages.libclang.lib ];
|
||||
LD_LIBRARY_PATH = lib.makeLibraryPath [
|
||||
pkgs.liburing
|
||||
pkgs.rust-jemalloc-sys-unprefixed
|
||||
rocksdbAllFeatures
|
||||
];
|
||||
}
|
||||
// uwulib.environment.buildPackageEnv
|
||||
// {
|
||||
ROCKSDB_INCLUDE_DIR = "${rocksdbAllFeatures}/include";
|
||||
ROCKSDB_LIB_DIR = "${rocksdbAllFeatures}/lib";
|
||||
};
|
||||
};
|
||||
cargoArtifacts = self'.packages.continuwuity-all-features-deps;
|
||||
in
|
||||
{
|
||||
# taken from
|
||||
#
|
||||
# https://crane.dev/examples/quick-start.html
|
||||
checks = {
|
||||
continuwuity-all-features-build = self'.packages.continuwuity-all-features-bin;
|
||||
|
||||
continuwuity-all-features-clippy = uwulib.build.craneLibForChecks.cargoClippy (
|
||||
commonAttrs
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
cargoClippyExtraArgs = "-- --deny warnings";
|
||||
}
|
||||
);
|
||||
|
||||
continuwuity-all-features-docs = uwulib.build.craneLibForChecks.cargoDoc (
|
||||
commonAttrs
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
# This can be commented out or tweaked as necessary, e.g. set to
|
||||
# `--deny rustdoc::broken-intra-doc-links` to only enforce that lint
|
||||
env.RUSTDOCFLAGS = "--deny warnings";
|
||||
}
|
||||
);
|
||||
|
||||
# Check formatting
|
||||
continuwuity-all-features-fmt = uwulib.build.craneLibForChecks.cargoFmt {
|
||||
src = uwulib.build.src;
|
||||
};
|
||||
|
||||
continuwuity-all-features-toml-fmt = uwulib.build.craneLibForChecks.taploFmt {
|
||||
src = pkgs.lib.sources.sourceFilesBySuffices uwulib.build.src [ ".toml" ];
|
||||
# taplo arguments can be further customized below as needed
|
||||
taploExtraArgs = "--config ${inputs.self}/taplo.toml";
|
||||
};
|
||||
|
||||
# Audit dependencies
|
||||
continuwuity-all-features-audit = uwulib.build.craneLibForChecks.cargoAudit {
|
||||
inherit (inputs) advisory-db;
|
||||
src = uwulib.build.src;
|
||||
};
|
||||
|
||||
# Audit licenses
|
||||
continuwuity-all-features-deny = uwulib.build.craneLibForChecks.cargoDeny {
|
||||
src = uwulib.build.src;
|
||||
};
|
||||
|
||||
# Run tests with cargo-nextest
|
||||
# Consider setting `doCheck = false` on `continuwuity-all-features` if you do not want
|
||||
# the tests to run twice
|
||||
continuwuity-all-features-nextest = uwulib.build.craneLibForChecks.cargoNextest (
|
||||
commonAttrs
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
partitions = 1;
|
||||
partitionType = "count";
|
||||
cargoNextestPartitionsExtraArgs = "--no-tests=pass";
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
14
nix/crane.nix
Normal file
14
nix/crane.nix
Normal file
@@ -0,0 +1,14 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
self',
|
||||
...
|
||||
}:
|
||||
{
|
||||
_module.args.craneLib = (inputs.crane.mkLib pkgs).overrideToolchain (
|
||||
pkgs: self'.packages.stable-toolchain
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
{
|
||||
imports = [
|
||||
./checks
|
||||
./rust.nix
|
||||
./crane.nix
|
||||
./packages
|
||||
./shells
|
||||
./tests
|
||||
|
||||
./hydra.nix
|
||||
./devshell.nix
|
||||
./fmt.nix
|
||||
./rocksdb-updater.nix
|
||||
];
|
||||
}
|
||||
|
||||
42
nix/devshell.nix
Normal file
42
nix/devshell.nix
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
craneLib,
|
||||
self',
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# basic nix shell containing all things necessary to build continuwuity in all flavors manually (on x86_64-linux)
|
||||
devShells.default = craneLib.devShell {
|
||||
packages = [
|
||||
self'.packages.rocksdb
|
||||
pkgs.nodejs
|
||||
pkgs.pkg-config
|
||||
]
|
||||
++ lib.optionals pkgs.stdenv.isLinux [
|
||||
pkgs.liburing
|
||||
pkgs.rust-jemalloc-sys-unprefixed
|
||||
];
|
||||
|
||||
env = {
|
||||
LIBCLANG_PATH = lib.makeLibraryPath [ pkgs.llvmPackages.libclang.lib ];
|
||||
LD_LIBRARY_PATH = lib.makeLibraryPath (
|
||||
[
|
||||
pkgs.stdenv.cc.cc.lib
|
||||
]
|
||||
++ lib.optionals pkgs.stdenv.isLinux [
|
||||
pkgs.liburing
|
||||
pkgs.jemalloc
|
||||
]
|
||||
);
|
||||
}
|
||||
// lib.optionalAttrs pkgs.stdenv.isLinux {
|
||||
PKG_CONFIG_PATH = lib.makeSearchPath "lib/pkgconfig" [
|
||||
pkgs.liburing.dev
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{ inputs, ... }:
|
||||
let
|
||||
lib = inputs.nixpkgs.lib;
|
||||
in
|
||||
{
|
||||
flake.hydraJobs.packages = builtins.mapAttrs (
|
||||
_name: lib.hydraJob
|
||||
) inputs.self.packages.x86_64-linux;
|
||||
}
|
||||
65
nix/packages/continuwuity.nix
Normal file
65
nix/packages/continuwuity.nix
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
lib,
|
||||
self,
|
||||
stdenv,
|
||||
liburing,
|
||||
craneLib,
|
||||
pkg-config,
|
||||
callPackage,
|
||||
rustPlatform,
|
||||
cargoExtraArgs ? "",
|
||||
rocksdb ? callPackage ./rocksdb.nix { },
|
||||
}:
|
||||
let
|
||||
# see https://crane.dev/API.html#cranelibfiltercargosources
|
||||
# we need to keep the `web` directory which would be filtered out by the regular source filtering function
|
||||
# https://crane.dev/API.html#cranelibcleancargosource
|
||||
isWebTemplate = path: _type: builtins.match ".*(src/(web|service)|docs).*" path != null;
|
||||
isRust = craneLib.filterCargoSources;
|
||||
isNix = path: _type: builtins.match ".+/nix.*" path != null;
|
||||
webOrRustNotNix = p: t: !(isNix p t) && (isWebTemplate p t || isRust p t);
|
||||
|
||||
src = lib.cleanSourceWith {
|
||||
src = self;
|
||||
filter = webOrRustNotNix;
|
||||
name = "source";
|
||||
};
|
||||
|
||||
attrs = {
|
||||
inherit src;
|
||||
nativeBuildInputs = [
|
||||
pkg-config
|
||||
rustPlatform.bindgenHook
|
||||
];
|
||||
buildInputs = lib.optionals stdenv.hostPlatform.isLinux [ liburing ];
|
||||
env = {
|
||||
ROCKSDB_INCLUDE_DIR = "${rocksdb}/include";
|
||||
ROCKSDB_LIB_DIR = "${rocksdb}/lib";
|
||||
};
|
||||
};
|
||||
in
|
||||
craneLib.buildPackage (
|
||||
lib.recursiveUpdate attrs {
|
||||
inherit cargoExtraArgs;
|
||||
cargoArtifacts = craneLib.buildDepsOnly attrs;
|
||||
|
||||
# Needed to make continuwuity link to rocksdb
|
||||
postFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
|
||||
old_rpath="$(patchelf --print-rpath $out/bin/conduwuit)"
|
||||
extra_rpath="${
|
||||
lib.makeLibraryPath [
|
||||
rocksdb
|
||||
]
|
||||
}"
|
||||
|
||||
patchelf --set-rpath "$old_rpath:$extra_rpath" $out/bin/conduwuit
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "A community-driven Matrix homeserver in Rust";
|
||||
mainProgram = "conduwuit";
|
||||
platforms = lib.platforms.all;
|
||||
maintainers = with lib.maintainers; [ quadradical ];
|
||||
};
|
||||
}
|
||||
)
|
||||
@@ -1,59 +0,0 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
self',
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
uwulib = inputs.self.uwulib.init pkgs;
|
||||
in
|
||||
{
|
||||
packages =
|
||||
lib.pipe
|
||||
[
|
||||
# this is the default variant
|
||||
{
|
||||
variantName = "default";
|
||||
commonAttrsArgs.profile = "release";
|
||||
rocksdb = self'.packages.rocksdb;
|
||||
features = { };
|
||||
}
|
||||
# this is the variant with all features enabled (liburing + jemalloc)
|
||||
{
|
||||
variantName = "all-features";
|
||||
commonAttrsArgs.profile = "release";
|
||||
rocksdb = self'.packages.rocksdb.override {
|
||||
enableJemalloc = true;
|
||||
};
|
||||
features = {
|
||||
enabledFeatures = "all";
|
||||
disabledFeatures = uwulib.features.defaultDisabledFeatures ++ [ "bindgen-static" ];
|
||||
};
|
||||
}
|
||||
]
|
||||
[
|
||||
(builtins.map (cfg: rec {
|
||||
deps = {
|
||||
name = "continuwuity-${cfg.variantName}-deps";
|
||||
value = uwulib.build.buildDeps {
|
||||
features = uwulib.features.calcFeatures cfg.features;
|
||||
inherit (cfg) commonAttrsArgs rocksdb;
|
||||
};
|
||||
};
|
||||
bin = {
|
||||
name = "continuwuity-${cfg.variantName}-bin";
|
||||
value = uwulib.build.buildPackage {
|
||||
deps = self'.packages.${deps.name};
|
||||
features = uwulib.features.calcFeatures cfg.features;
|
||||
inherit (cfg) commonAttrsArgs rocksdb;
|
||||
};
|
||||
};
|
||||
}))
|
||||
(builtins.concatMap builtins.attrValues)
|
||||
builtins.listToAttrs
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
{
|
||||
imports = [
|
||||
./continuwuity
|
||||
./rocksdb
|
||||
./rust.nix
|
||||
./uwulib
|
||||
];
|
||||
|
||||
self,
|
||||
...
|
||||
}:
|
||||
{
|
||||
perSystem =
|
||||
{ self', ... }:
|
||||
{
|
||||
packages.default = self'.packages.continuwuity-default-bin;
|
||||
pkgs,
|
||||
craneLib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
packages = {
|
||||
rocksdb = pkgs.callPackage ./rocksdb.nix { };
|
||||
default = pkgs.callPackage ./continuwuity.nix { inherit self craneLib; };
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
34
nix/packages/rocksdb.nix
Normal file
34
nix/packages/rocksdb.nix
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
stdenv,
|
||||
rocksdb,
|
||||
fetchFromGitea,
|
||||
rust-jemalloc-sys-unprefixed,
|
||||
...
|
||||
}:
|
||||
(rocksdb.override {
|
||||
# rocksdb fails to build with prefixed jemalloc, which is required on
|
||||
# darwin due to [1]. In this case, fall back to building rocksdb with
|
||||
# libc malloc. This should not cause conflicts, because all of the
|
||||
# jemalloc symbols are prefixed.
|
||||
#
|
||||
# [1]: https://github.com/tikv/jemallocator/blob/ab0676d77e81268cd09b059260c75b38dbef2d51/jemalloc-sys/src/env.rs#L17
|
||||
jemalloc = rust-jemalloc-sys-unprefixed;
|
||||
enableJemalloc = stdenv.hostPlatform.isLinux;
|
||||
}).overrideAttrs
|
||||
({
|
||||
version = "continuwuity-v0.5.0-unstable-2026-03-27";
|
||||
src = fetchFromGitea {
|
||||
domain = "forgejo.ellis.link";
|
||||
owner = "continuwuation";
|
||||
repo = "rocksdb";
|
||||
rev = "463f47afceebfe088f6922420265546bd237f249";
|
||||
hash = "sha256-1ef75IDMs5Hba4VWEyXPJb02JyShy5k4gJfzGDhopRk=";
|
||||
};
|
||||
|
||||
# We have this already at https://forgejo.ellis.link/continuwuation/rocksdb/commit/a935c0273e1ba44eacf88ce3685a9b9831486155
|
||||
# Unsetting `patches` so we don't have to revert it and make this nix exclusive
|
||||
patches = [ ];
|
||||
|
||||
# Unset postPatch, as our version override breaks version-specific sed calls in the original package
|
||||
postPatch = "";
|
||||
})
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
packages = {
|
||||
rocksdb = pkgs.callPackage ./package.nix { };
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
|
||||
rocksdb,
|
||||
liburing,
|
||||
rust-jemalloc-sys-unprefixed,
|
||||
|
||||
enableJemalloc ? false,
|
||||
|
||||
fetchFromGitea,
|
||||
|
||||
...
|
||||
}:
|
||||
let
|
||||
notDarwin = !stdenv.hostPlatform.isDarwin;
|
||||
in
|
||||
(rocksdb.override {
|
||||
# Override the liburing input for the build with our own so
|
||||
# we have it built with the library flag
|
||||
inherit liburing;
|
||||
jemalloc = rust-jemalloc-sys-unprefixed;
|
||||
|
||||
# rocksdb fails to build with prefixed jemalloc, which is required on
|
||||
# darwin due to [1]. In this case, fall back to building rocksdb with
|
||||
# libc malloc. This should not cause conflicts, because all of the
|
||||
# jemalloc symbols are prefixed.
|
||||
#
|
||||
# [1]: https://github.com/tikv/jemallocator/blob/ab0676d77e81268cd09b059260c75b38dbef2d51/jemalloc-sys/src/env.rs#L17
|
||||
enableJemalloc = enableJemalloc && notDarwin;
|
||||
|
||||
# for some reason enableLiburing in nixpkgs rocksdb is default true
|
||||
# which breaks Darwin entirely
|
||||
enableLiburing = notDarwin;
|
||||
}).overrideAttrs
|
||||
(old: {
|
||||
src = fetchFromGitea {
|
||||
domain = "forgejo.ellis.link";
|
||||
owner = "continuwuation";
|
||||
repo = "rocksdb";
|
||||
rev = "10.5.fb";
|
||||
sha256 = "sha256-X4ApGLkHF9ceBtBg77dimEpu720I79ffLoyPa8JMHaU=";
|
||||
};
|
||||
version = "10.5.fb";
|
||||
cmakeFlags =
|
||||
lib.subtractLists (builtins.map (flag: lib.cmakeBool flag true) [
|
||||
# No real reason to have snappy or zlib, no one uses this
|
||||
"WITH_SNAPPY"
|
||||
"ZLIB"
|
||||
"WITH_ZLIB"
|
||||
# We don't need to use ldb or sst_dump (core_tools)
|
||||
"WITH_CORE_TOOLS"
|
||||
# We don't need to build rocksdb tests
|
||||
"WITH_TESTS"
|
||||
# We use rust-rocksdb via C interface and don't need C++ RTTI
|
||||
"USE_RTTI"
|
||||
# This doesn't exist in RocksDB, and USE_SSE is deprecated for
|
||||
# PORTABLE=$(march)
|
||||
"FORCE_SSE42"
|
||||
]) old.cmakeFlags
|
||||
++ (builtins.map (flag: lib.cmakeBool flag false) [
|
||||
# No real reason to have snappy, no one uses this
|
||||
"WITH_SNAPPY"
|
||||
"ZLIB"
|
||||
"WITH_ZLIB"
|
||||
# We don't need to use ldb or sst_dump (core_tools)
|
||||
"WITH_CORE_TOOLS"
|
||||
# We don't need trace tools
|
||||
"WITH_TRACE_TOOLS"
|
||||
# We don't need to build rocksdb tests
|
||||
"WITH_TESTS"
|
||||
# We use rust-rocksdb via C interface and don't need C++ RTTI
|
||||
"USE_RTTI"
|
||||
]);
|
||||
|
||||
enableLiburing = notDarwin;
|
||||
|
||||
# outputs has "tools" which we don't need or use
|
||||
outputs = [ "out" ];
|
||||
|
||||
# preInstall hooks has stuff for messing with ldb/sst_dump which we don't need or use
|
||||
preInstall = "";
|
||||
|
||||
# We have this already at https://forgejo.ellis.link/continuwuation/rocksdb/commit/a935c0273e1ba44eacf88ce3685a9b9831486155
|
||||
# Unsetting `patches` so we don't have to revert it and make this nix exclusive
|
||||
patches = [ ];
|
||||
})
|
||||
@@ -1,122 +0,0 @@
|
||||
args@{ pkgs, inputs, ... }:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
uwuenv = import ./environment.nix args;
|
||||
selfpkgs = inputs.self.packages.${pkgs.stdenv.system};
|
||||
in
|
||||
rec {
|
||||
# basic, very minimal instance of the crane library with a minimal rust toolchain
|
||||
craneLib = (inputs.crane.mkLib pkgs).overrideToolchain (_: selfpkgs.build-toolchain);
|
||||
# the checks require more rust toolchain components, hence we have this separate instance of the crane library
|
||||
craneLibForChecks = (inputs.crane.mkLib pkgs).overrideToolchain (_: selfpkgs.dev-toolchain);
|
||||
|
||||
# meta information (name, version, etc) of the rust crate based on the Cargo.toml
|
||||
crateInfo = craneLib.crateNameFromCargoToml { cargoToml = "${inputs.self}/Cargo.toml"; };
|
||||
|
||||
src =
|
||||
let
|
||||
# see https://crane.dev/API.html#cranelibfiltercargosources
|
||||
#
|
||||
# we need to keep the `web` directory which would be filtered out by the regular source filtering function
|
||||
#
|
||||
# https://crane.dev/API.html#cranelibcleancargosource
|
||||
isWebTemplate = path: _type: builtins.match ".*(src/(web|service)|docs).*" path != null;
|
||||
isRust = craneLib.filterCargoSources;
|
||||
isNix = path: _type: builtins.match ".+/nix.*" path != null;
|
||||
webOrRustNotNix = p: t: !(isNix p t) && (isWebTemplate p t || isRust p t);
|
||||
in
|
||||
lib.cleanSourceWith {
|
||||
src = inputs.self;
|
||||
filter = webOrRustNotNix;
|
||||
name = "source";
|
||||
};
|
||||
|
||||
# common attrs that are shared between building continuwuity's deps and the package itself
|
||||
commonAttrs =
|
||||
{
|
||||
profile ? "dev",
|
||||
...
|
||||
}:
|
||||
{
|
||||
inherit (crateInfo)
|
||||
pname
|
||||
version
|
||||
;
|
||||
inherit src;
|
||||
|
||||
# this prevents unnecessary rebuilds
|
||||
strictDeps = true;
|
||||
|
||||
dontStrip = profile == "dev" || profile == "test";
|
||||
dontPatchELF = profile == "dev" || profile == "test";
|
||||
|
||||
doCheck = true;
|
||||
|
||||
nativeBuildInputs = [
|
||||
# bindgen needs the build platform's libclang. Apparently due to "splicing
|
||||
# weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the
|
||||
# right thing here.
|
||||
pkgs.rustPlatform.bindgenHook
|
||||
];
|
||||
};
|
||||
|
||||
makeRocksDBEnv =
|
||||
{ rocksdb }:
|
||||
{
|
||||
ROCKSDB_INCLUDE_DIR = "${rocksdb}/include";
|
||||
ROCKSDB_LIB_DIR = "${rocksdb}/lib";
|
||||
};
|
||||
|
||||
# function that builds the continuwuity dependencies derivation
|
||||
buildDeps =
|
||||
{
|
||||
rocksdb,
|
||||
features,
|
||||
commonAttrsArgs,
|
||||
}:
|
||||
craneLib.buildDepsOnly (
|
||||
(commonAttrs commonAttrsArgs)
|
||||
// {
|
||||
env = uwuenv.buildDepsOnlyEnv
|
||||
// (makeRocksDBEnv { inherit rocksdb; })
|
||||
// {
|
||||
# required since we started using unstable reqwest apparently ... otherwise the all-features build will fail
|
||||
RUSTFLAGS = "--cfg reqwest_unstable";
|
||||
};
|
||||
inherit (features) cargoExtraArgs;
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
# function that builds the continuwuity package
|
||||
buildPackage =
|
||||
{
|
||||
deps,
|
||||
rocksdb,
|
||||
features,
|
||||
commonAttrsArgs,
|
||||
}:
|
||||
let
|
||||
rocksdbEnv = makeRocksDBEnv { inherit rocksdb; };
|
||||
in
|
||||
craneLib.buildPackage (
|
||||
(commonAttrs commonAttrsArgs)
|
||||
// {
|
||||
postFixup = ''
|
||||
patchelf --set-rpath "$(${pkgs.patchelf}/bin/patchelf --print-rpath $out/bin/${crateInfo.pname}):${rocksdb}/lib" $out/bin/${crateInfo.pname}
|
||||
'';
|
||||
cargoArtifacts = deps;
|
||||
doCheck = true;
|
||||
env =
|
||||
uwuenv.buildPackageEnv
|
||||
// rocksdbEnv
|
||||
// {
|
||||
# required since we started using unstable reqwest apparently ... otherwise the all-features build will fail
|
||||
RUSTFLAGS = "--cfg reqwest_unstable";
|
||||
};
|
||||
passthru.env = uwuenv.buildPackageEnv // rocksdbEnv;
|
||||
meta.mainProgram = crateInfo.pname;
|
||||
inherit (features) cargoExtraArgs;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
flake.uwulib = {
|
||||
init = pkgs: {
|
||||
features = import ./features.nix { inherit pkgs inputs; };
|
||||
environment = import ./environment.nix { inherit pkgs inputs; };
|
||||
build = import ./build.nix { inherit pkgs inputs; };
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
args@{ pkgs, inputs, ... }:
|
||||
let
|
||||
uwubuild = import ./build.nix args;
|
||||
in
|
||||
rec {
|
||||
buildDepsOnlyEnv = {
|
||||
# https://crane.dev/faq/rebuilds-bindgen.html
|
||||
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
|
||||
CARGO_PROFILE = "release";
|
||||
}
|
||||
// uwubuild.craneLib.mkCrossToolchainEnv (p: pkgs.clangStdenv);
|
||||
|
||||
buildPackageEnv = {
|
||||
GIT_COMMIT_HASH = inputs.self.rev or inputs.self.dirtyRev or "";
|
||||
GIT_COMMIT_HASH_SHORT = inputs.self.shortRev or inputs.self.dirtyShortRev or "";
|
||||
}
|
||||
// buildDepsOnlyEnv;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
{ pkgs, inputs, ... }:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
in
|
||||
rec {
|
||||
defaultDisabledFeatures = [
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
# jemalloc profiling/stats features are expensive and shouldn't
|
||||
# be expected on non-debug builds.
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# we don't want to enable this feature set by default but be more specific about it
|
||||
"full"
|
||||
];
|
||||
# We perform default-feature unification in nix, because some of the dependencies
|
||||
# on the nix side depend on feature values.
|
||||
calcFeatures =
|
||||
{
|
||||
tomlPath ? "${inputs.self}/src/main",
|
||||
# either a list of feature names or a string "all" which enables all non-default features
|
||||
enabledFeatures ? [ ],
|
||||
disabledFeatures ? defaultDisabledFeatures,
|
||||
default_features ? true,
|
||||
disable_release_max_log_level ? false,
|
||||
}:
|
||||
let
|
||||
# simple helper to get the contents of a Cargo.toml file in a nix format
|
||||
getToml = path: lib.importTOML "${path}/Cargo.toml";
|
||||
|
||||
# get all the features except for the default features
|
||||
allFeatures = lib.pipe tomlPath [
|
||||
getToml
|
||||
(manifest: manifest.features)
|
||||
lib.attrNames
|
||||
(lib.remove "default")
|
||||
];
|
||||
|
||||
# get just the default enabled features
|
||||
allDefaultFeatures = lib.pipe tomlPath [
|
||||
getToml
|
||||
(manifest: manifest.features.default)
|
||||
];
|
||||
|
||||
# depending on the value of enabledFeatures choose just a set or all non-default features
|
||||
#
|
||||
# - [ list of features ] -> choose exactly the features listed
|
||||
# - "all" -> choose all non-default features
|
||||
additionalFeatures = if enabledFeatures == "all" then allFeatures else enabledFeatures;
|
||||
|
||||
# unification with default features (if enabled)
|
||||
features = lib.unique (additionalFeatures ++ lib.optionals default_features allDefaultFeatures);
|
||||
|
||||
# prepare the features that are subtracted from the set
|
||||
disabledFeatures' =
|
||||
disabledFeatures ++ lib.optionals disable_release_max_log_level [ "release_max_log_level" ];
|
||||
|
||||
# construct the final feature set
|
||||
finalFeatures = lib.subtractLists disabledFeatures' features;
|
||||
in
|
||||
{
|
||||
# final feature set, useful for querying it
|
||||
features = finalFeatures;
|
||||
|
||||
# crane flag with the relevant features
|
||||
cargoExtraArgs = builtins.concatStringsSep " " [
|
||||
"--no-default-features"
|
||||
"--locked"
|
||||
(lib.optionalString (finalFeatures != [ ]) "--features")
|
||||
(builtins.concatStringsSep "," finalFeatures)
|
||||
];
|
||||
};
|
||||
}
|
||||
14
nix/rocksdb-updater.nix
Normal file
14
nix/rocksdb-updater.nix
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
apps.update-rocksdb = {
|
||||
type = "app";
|
||||
program = pkgs.writeShellApplication {
|
||||
name = "update-rocksdb";
|
||||
runtimeInputs = [ pkgs.nix-update ];
|
||||
text = "nix-update rocksdb -F --version branch";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
system,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
@@ -11,7 +12,7 @@
|
||||
let
|
||||
fnx = inputs.fenix.packages.${system};
|
||||
|
||||
stable = fnx.fromToolchainFile {
|
||||
stable-toolchain = fnx.fromToolchainFile {
|
||||
file = inputs.self + "/rust-toolchain.toml";
|
||||
|
||||
# See also `rust-toolchain.toml`
|
||||
@@ -19,11 +20,10 @@
|
||||
};
|
||||
in
|
||||
{
|
||||
# used for building nix stuff (doesn't include rustfmt overhead)
|
||||
build-toolchain = stable;
|
||||
# used for dev shells
|
||||
inherit stable-toolchain;
|
||||
|
||||
dev-toolchain = fnx.combine [
|
||||
stable
|
||||
stable-toolchain
|
||||
# use the nightly rustfmt because we use nightly features
|
||||
fnx.complete.rustfmt
|
||||
];
|
||||
@@ -1,29 +0,0 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
self',
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
uwulib = inputs.self.uwulib.init pkgs;
|
||||
rocksdbAllFeatures = self'.packages.rocksdb.override {
|
||||
enableJemalloc = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
# basic nix shell containing all things necessary to build continuwuity in all flavors manually (on x86_64-linux)
|
||||
devShells.default = uwulib.build.craneLib.devShell {
|
||||
packages = [
|
||||
pkgs.nodejs
|
||||
pkgs.pkg-config
|
||||
pkgs.liburing
|
||||
pkgs.rust-jemalloc-sys-unprefixed
|
||||
rocksdbAllFeatures
|
||||
];
|
||||
env.LIBCLANG_PATH = lib.makeLibraryPath [ pkgs.llvmPackages.libclang.lib ];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
self',
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
baseTestScript =
|
||||
pkgs.writers.writePython3Bin "do_test" { libraries = [ pkgs.python3Packages.matrix-nio ]; }
|
||||
''
|
||||
import asyncio
|
||||
import nio
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# Connect to continuwuity
|
||||
client = nio.AsyncClient("http://continuwuity:6167", "alice")
|
||||
|
||||
# Register as user alice
|
||||
response = await client.register("alice", "my-secret-password")
|
||||
|
||||
# Log in as user alice
|
||||
response = await client.login("my-secret-password")
|
||||
|
||||
# Create a new room
|
||||
response = await client.room_create(federate=False)
|
||||
print("Matrix room create response:", response)
|
||||
assert isinstance(response, nio.RoomCreateResponse)
|
||||
room_id = response.room_id
|
||||
|
||||
# Join the room
|
||||
response = await client.join(room_id)
|
||||
print("Matrix join response:", response)
|
||||
assert isinstance(response, nio.JoinResponse)
|
||||
|
||||
# Send a message to the room
|
||||
response = await client.room_send(
|
||||
room_id=room_id,
|
||||
message_type="m.room.message",
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"body": "Hello continuwuity!"
|
||||
}
|
||||
)
|
||||
print("Matrix room send response:", response)
|
||||
assert isinstance(response, nio.RoomSendResponse)
|
||||
|
||||
# Sync responses
|
||||
response = await client.sync(timeout=30000)
|
||||
print("Matrix sync response:", response)
|
||||
assert isinstance(response, nio.SyncResponse)
|
||||
|
||||
# Check the message was received by continuwuity
|
||||
last_message = response.rooms.join[room_id].timeline.events[-1].body
|
||||
assert last_message == "Hello continuwuity!"
|
||||
|
||||
# Leave the room
|
||||
response = await client.room_leave(room_id)
|
||||
print("Matrix room leave response:", response)
|
||||
assert isinstance(response, nio.RoomLeaveResponse)
|
||||
|
||||
# Close the client
|
||||
await client.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
'';
|
||||
in
|
||||
{
|
||||
# run some nixos tests as checks
|
||||
checks = lib.pipe self'.packages [
|
||||
# we take all packages (names)
|
||||
builtins.attrNames
|
||||
# we filter out all packages that end with `-bin` (which we are interested in for testing)
|
||||
(builtins.filter (lib.hasSuffix "-bin"))
|
||||
# for each of these binaries we built the basic nixos test
|
||||
#
|
||||
# this test was initially yoinked from
|
||||
#
|
||||
# https://github.com/NixOS/nixpkgs/blob/960ce26339661b1b69c6f12b9063ca51b688615f/nixos/tests/matrix/continuwuity.nix
|
||||
(builtins.concatMap (
|
||||
name:
|
||||
builtins.map
|
||||
(
|
||||
{ config, suffix }:
|
||||
{
|
||||
name = "test-${name}-${suffix}";
|
||||
value = pkgs.testers.runNixOSTest {
|
||||
inherit name;
|
||||
|
||||
nodes = {
|
||||
continuwuity = {
|
||||
services.matrix-continuwuity = {
|
||||
enable = true;
|
||||
package = self'.packages.${name};
|
||||
settings = config;
|
||||
extraEnvironment.RUST_BACKTRACE = "yes";
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = [ 6167 ];
|
||||
};
|
||||
client.environment.systemPackages = [ baseTestScript ];
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
with subtest("start continuwuity"):
|
||||
continuwuity.wait_for_unit("continuwuity.service")
|
||||
continuwuity.wait_for_open_port(6167)
|
||||
|
||||
with subtest("ensure messages can be exchanged"):
|
||||
client.succeed("${lib.getExe baseTestScript} >&2")
|
||||
'';
|
||||
|
||||
};
|
||||
}
|
||||
)
|
||||
[
|
||||
{
|
||||
suffix = "base";
|
||||
config = {
|
||||
global = {
|
||||
server_name = name;
|
||||
address = [ "0.0.0.0" ];
|
||||
allow_registration = true;
|
||||
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
{
|
||||
suffix = "with-room-version";
|
||||
config = {
|
||||
global = {
|
||||
server_name = name;
|
||||
address = [ "0.0.0.0" ];
|
||||
allow_registration = true;
|
||||
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true;
|
||||
default_room_version = "12";
|
||||
};
|
||||
};
|
||||
}
|
||||
]
|
||||
))
|
||||
builtins.listToAttrs
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "conduwuit_admin"
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -79,6 +80,7 @@ conduwuit-database.workspace = true
|
||||
conduwuit-macros.workspace = true
|
||||
conduwuit-service.workspace = true
|
||||
const-str.workspace = true
|
||||
ctor.workspace = true
|
||||
futures.workspace = true
|
||||
lettre.workspace = true
|
||||
log.workspace = true
|
||||
@@ -88,6 +90,7 @@ serde-saphyr.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
resolvematrix.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
};
|
||||
use futures::{FutureExt, StreamExt, TryStreamExt};
|
||||
use lettre::message::Mailbox;
|
||||
use resolvematrix::server::{MatrixResolver, ResolvedDestination};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
|
||||
OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomVersionId,
|
||||
@@ -736,13 +737,20 @@ pub(super) async fn resolve_true_destination(
|
||||
);
|
||||
}
|
||||
|
||||
let actual = self
|
||||
.services
|
||||
.resolver
|
||||
.resolve_actual_dest(&server_name, !no_cache)
|
||||
.await?;
|
||||
let resolver: &MatrixResolver = if no_cache {
|
||||
&MatrixResolver::new()?
|
||||
} else {
|
||||
&self.services.resolver.resolver
|
||||
};
|
||||
|
||||
let msg = format!("Destination: {}\nHostname URI: {}", actual.dest, actual.host);
|
||||
let actual = resolver.resolve_server(server_name.as_str()).await?;
|
||||
|
||||
let destination = match actual.destination {
|
||||
| ResolvedDestination::Literal(addr) => addr.to_string(),
|
||||
| ResolvedDestination::Named(host, port) => format!("{host}:{port}"),
|
||||
};
|
||||
|
||||
let msg = format!("Destination: {}\nHostname URI (SNI): {}", destination, actual.host);
|
||||
self.write_str(&msg).await
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#![allow(clippy::enum_glob_use)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
conduwuit_macros::introspect_crate! {}
|
||||
|
||||
pub(crate) mod admin;
|
||||
pub(crate) mod context;
|
||||
pub(crate) mod processor;
|
||||
|
||||
@@ -46,7 +46,7 @@ async fn destinations_cache(&self, server_name: Option<OwnedServerName>) -> Resu
|
||||
writeln!(self, "| Server Name | Destination | Hostname | Expires |").await?;
|
||||
writeln!(self, "| ----------- | ----------- | -------- | ------- |").await?;
|
||||
|
||||
let mut destinations = self.services.resolver.cache.destinations().boxed();
|
||||
let mut destinations = self.services.resolver.dns.cache.destinations().boxed();
|
||||
|
||||
while let Some((name, CachedDest { dest, host, expire })) = destinations.next().await {
|
||||
if let Some(server_name) = server_name.as_ref() {
|
||||
@@ -70,7 +70,7 @@ async fn overrides_cache(&self, server_name: Option<String>) -> Result {
|
||||
writeln!(self, "| Server Name | IP | Port | Expires | Overriding |").await?;
|
||||
writeln!(self, "| ----------- | --- | ----:| ------- | ---------- |").await?;
|
||||
|
||||
let mut overrides = self.services.resolver.cache.overrides().boxed();
|
||||
let mut overrides = self.services.resolver.dns.cache.overrides().boxed();
|
||||
|
||||
while let Some((name, CachedOverride { ips, port, expire, overriding })) =
|
||||
overrides.next().await
|
||||
@@ -92,11 +92,11 @@ async fn overrides_cache(&self, server_name: Option<String>) -> Result {
|
||||
#[admin_command]
|
||||
async fn flush_cache(&self, name: Option<OwnedServerName>, all: bool) -> Result {
|
||||
if all {
|
||||
self.services.resolver.cache.clear().await;
|
||||
self.services.resolver.dns.cache.clear().await;
|
||||
writeln!(self, "Resolver caches cleared!").await
|
||||
} else if let Some(name) = name {
|
||||
self.services.resolver.cache.del_destination(&name);
|
||||
self.services.resolver.cache.del_override(&name);
|
||||
self.services.resolver.dns.cache.del_destination(&name);
|
||||
self.services.resolver.dns.cache.del_override(&name);
|
||||
self.write_str(&format!("Cleared {name} from resolver caches!"))
|
||||
.await
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use conduwuit::{Err, Result};
|
||||
use futures::StreamExt;
|
||||
use ruma::OwnedRoomId;
|
||||
use ruma::{OwnedRoomId, OwnedRoomOrAliasId};
|
||||
|
||||
use crate::{PAGE_SIZE, admin_command, get_room_info};
|
||||
|
||||
@@ -82,3 +82,185 @@ pub(super) async fn exists(&self, room_id: OwnedRoomId) -> Result {
|
||||
|
||||
self.write_str(&format!("{result}")).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn purge_sync_tokens(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
// Resolve the room ID from the room or alias ID
|
||||
let room_id = self.services.rooms.alias.resolve(&room).await?;
|
||||
|
||||
// Delete all tokens for this room using the service method
|
||||
let Ok(deleted_count) = self.services.rooms.user.delete_room_tokens(&room_id).await else {
|
||||
return Err!("Failed to delete sync tokens for room {}", room_id.as_str());
|
||||
};
|
||||
|
||||
self.write_str(&format!(
|
||||
"Successfully deleted {deleted_count} sync tokens for room {}",
|
||||
room_id.as_str()
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Target options for room purging
|
||||
#[derive(Default, Debug, clap::ValueEnum, Clone)]
|
||||
pub enum RoomTargetOption {
|
||||
#[default]
|
||||
/// Target all rooms
|
||||
All,
|
||||
/// Target only disabled rooms
|
||||
DisabledOnly,
|
||||
/// Target only banned rooms
|
||||
BannedOnly,
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn purge_all_sync_tokens(
|
||||
&self,
|
||||
target_option: Option<RoomTargetOption>,
|
||||
execute: bool,
|
||||
) -> Result {
|
||||
use conduwuit::{debug, info};
|
||||
|
||||
let mode = if !execute { "Simulating" } else { "Starting" };
|
||||
|
||||
// strictly, we should check if these reach the max value after the loop and
|
||||
// warn the user that the count is too large
|
||||
let mut total_rooms_checked: usize = 0;
|
||||
let mut total_tokens_deleted: usize = 0;
|
||||
let mut error_count: u32 = 0;
|
||||
let mut skipped_rooms: usize = 0;
|
||||
|
||||
info!("{} purge of sync tokens", mode);
|
||||
|
||||
// Get all rooms in the server
|
||||
let all_rooms = self
|
||||
.services
|
||||
.rooms
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
|
||||
info!("Found {} rooms total on the server", all_rooms.len());
|
||||
|
||||
// Filter rooms based on options
|
||||
let mut rooms = Vec::new();
|
||||
for room_id in all_rooms {
|
||||
if let Some(target) = &target_option {
|
||||
match target {
|
||||
| RoomTargetOption::DisabledOnly => {
|
||||
if !self.services.rooms.metadata.is_disabled(room_id).await {
|
||||
debug!("Skipping room {} as it's not disabled", room_id.as_str());
|
||||
skipped_rooms = skipped_rooms.saturating_add(1);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
| RoomTargetOption::BannedOnly => {
|
||||
if !self.services.rooms.metadata.is_banned(room_id).await {
|
||||
debug!("Skipping room {} as it's not banned", room_id.as_str());
|
||||
skipped_rooms = skipped_rooms.saturating_add(1);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
| RoomTargetOption::All => {},
|
||||
}
|
||||
}
|
||||
|
||||
rooms.push(room_id);
|
||||
}
|
||||
|
||||
// Total number of rooms we'll be checking
|
||||
let total_rooms = rooms.len();
|
||||
info!(
|
||||
"Processing {} rooms after filtering (skipped {} rooms)",
|
||||
total_rooms, skipped_rooms
|
||||
);
|
||||
|
||||
// Process each room
|
||||
for room_id in rooms {
|
||||
total_rooms_checked = total_rooms_checked.saturating_add(1);
|
||||
|
||||
// Log progress periodically
|
||||
if total_rooms_checked.is_multiple_of(100) || total_rooms_checked == total_rooms {
|
||||
info!(
|
||||
"Progress: {}/{} rooms checked, {} tokens {}",
|
||||
total_rooms_checked,
|
||||
total_rooms,
|
||||
total_tokens_deleted,
|
||||
if !execute { "would be deleted" } else { "deleted" }
|
||||
);
|
||||
}
|
||||
|
||||
// In dry run mode, just count what would be deleted, don't actually delete
|
||||
debug!(
|
||||
"Room {}: {}",
|
||||
room_id.as_str(),
|
||||
if !execute {
|
||||
"would purge sync tokens"
|
||||
} else {
|
||||
"purging sync tokens"
|
||||
}
|
||||
);
|
||||
|
||||
if !execute {
|
||||
// For dry run mode, count tokens without deleting
|
||||
match self.services.rooms.user.count_room_tokens(room_id).await {
|
||||
| Ok(count) =>
|
||||
if count > 0 {
|
||||
debug!(
|
||||
"Would delete {} sync tokens for room {}",
|
||||
count,
|
||||
room_id.as_str()
|
||||
);
|
||||
total_tokens_deleted = total_tokens_deleted.saturating_add(count);
|
||||
} else {
|
||||
debug!("No sync tokens found for room {}", room_id.as_str());
|
||||
},
|
||||
| Err(e) => {
|
||||
debug!("Error counting sync tokens for room {}: {:?}", room_id.as_str(), e);
|
||||
error_count = error_count.saturating_add(1);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// Real deletion mode
|
||||
match self.services.rooms.user.delete_room_tokens(room_id).await {
|
||||
| Ok(count) =>
|
||||
if count > 0 {
|
||||
debug!("Deleted {} sync tokens for room {}", count, room_id.as_str());
|
||||
total_tokens_deleted = total_tokens_deleted.saturating_add(count);
|
||||
} else {
|
||||
debug!("No sync tokens found for room {}", room_id.as_str());
|
||||
},
|
||||
| Err(e) => {
|
||||
debug!("Error purging sync tokens for room {}: {:?}", room_id.as_str(), e);
|
||||
error_count = error_count.saturating_add(1);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let action = if !execute { "would be deleted" } else { "deleted" };
|
||||
info!(
|
||||
"Finished {}: checked {} rooms out of {} total, {} tokens {}, errors: {}",
|
||||
if !execute {
|
||||
"purge simulation"
|
||||
} else {
|
||||
"purging sync tokens"
|
||||
},
|
||||
total_rooms_checked,
|
||||
total_rooms,
|
||||
total_tokens_deleted,
|
||||
action,
|
||||
error_count
|
||||
);
|
||||
|
||||
self.write_str(&format!(
|
||||
"Finished {}: checked {} rooms out of {} total, {} tokens {}, errors: {}",
|
||||
if !execute { "simulation" } else { "purging sync tokens" },
|
||||
total_rooms_checked,
|
||||
total_rooms,
|
||||
total_tokens_deleted,
|
||||
action,
|
||||
error_count
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
mod moderation;
|
||||
|
||||
use clap::Subcommand;
|
||||
use commands::RoomTargetOption;
|
||||
use conduwuit::Result;
|
||||
use ruma::OwnedRoomId;
|
||||
use ruma::{OwnedRoomId, OwnedRoomOrAliasId};
|
||||
|
||||
use self::{
|
||||
alias::RoomAliasCommand, directory::RoomDirectoryCommand, info::RoomInfoCommand,
|
||||
@@ -60,4 +61,25 @@ pub enum RoomCommand {
|
||||
Exists {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Delete all sync tokens for a room
|
||||
PurgeSyncTokens {
|
||||
/// Room ID or alias to purge sync tokens for
|
||||
#[arg(value_parser)]
|
||||
room: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Delete sync tokens for all rooms that have no local users
|
||||
///
|
||||
/// By default, processes all empty rooms.
|
||||
PurgeAllSyncTokens {
|
||||
/// Target specific room types
|
||||
#[arg(long, value_enum)]
|
||||
target_option: Option<RoomTargetOption>,
|
||||
|
||||
/// Execute token deletions. Otherwise,
|
||||
/// Performs a dry run without actually deleting any tokens
|
||||
#[arg(long)]
|
||||
execute: bool,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use std::{fmt::Write, path::PathBuf, sync::Arc};
|
||||
|
||||
use conduwuit::{
|
||||
Err, Result,
|
||||
@@ -153,3 +153,97 @@ pub(super) async fn shutdown(&self) -> Result {
|
||||
|
||||
self.write_str("Shutting down server...").await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_features(&self) -> Result {
|
||||
let mut enabled_features = conduwuit::info::introspection::ENABLED_FEATURES
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.iter()
|
||||
.flat_map(|(_, f)| f.iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
enabled_features.sort_unstable();
|
||||
enabled_features.dedup();
|
||||
|
||||
let mut available_features = conduwuit::build_metadata::WORKSPACE_FEATURES
|
||||
.iter()
|
||||
.flat_map(|(_, f)| f.iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
available_features.sort_unstable();
|
||||
available_features.dedup();
|
||||
|
||||
let mut features = String::new();
|
||||
|
||||
for feature in available_features {
|
||||
let active = enabled_features.contains(&feature);
|
||||
let emoji = if active { "✅" } else { "❌" };
|
||||
let remark = if active { "[enabled]" } else { "" };
|
||||
writeln!(features, "{emoji} {feature} {remark}")?;
|
||||
}
|
||||
|
||||
self.write_str(&features).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn build_info(&self) -> Result {
|
||||
use conduwuit::build_metadata::built;
|
||||
|
||||
let mut info = String::new();
|
||||
|
||||
// Version information
|
||||
writeln!(info, "# Build Information\n")?;
|
||||
writeln!(info, "**Version:** {}", built::PKG_VERSION)?;
|
||||
writeln!(info, "**Package:** {}", built::PKG_NAME)?;
|
||||
writeln!(info, "**Description:** {}", built::PKG_DESCRIPTION)?;
|
||||
|
||||
// Git information
|
||||
writeln!(info, "\n## Git Information\n")?;
|
||||
if let Some(hash) = conduwuit::build_metadata::GIT_COMMIT_HASH {
|
||||
writeln!(info, "**Commit Hash:** {hash}")?;
|
||||
}
|
||||
if let Some(hash) = conduwuit::build_metadata::GIT_COMMIT_HASH_SHORT {
|
||||
writeln!(info, "**Commit Hash (short):** {hash}")?;
|
||||
}
|
||||
if let Some(url) = conduwuit::build_metadata::GIT_REMOTE_WEB_URL {
|
||||
writeln!(info, "**Repository:** {url}")?;
|
||||
}
|
||||
if let Some(url) = conduwuit::build_metadata::GIT_REMOTE_COMMIT_URL {
|
||||
writeln!(info, "**Commit URL:** {url}")?;
|
||||
}
|
||||
|
||||
// Build environment
|
||||
writeln!(info, "\n## Build Environment\n")?;
|
||||
writeln!(info, "**Profile:** {}", built::PROFILE)?;
|
||||
writeln!(info, "**Optimization Level:** {}", built::OPT_LEVEL)?;
|
||||
writeln!(info, "**Debug:** {}", built::DEBUG)?;
|
||||
writeln!(info, "**Target:** {}", built::TARGET)?;
|
||||
writeln!(info, "**Host:** {}", built::HOST)?;
|
||||
|
||||
// Rust compiler information
|
||||
writeln!(info, "\n## Compiler Information\n")?;
|
||||
writeln!(info, "**Rustc Version:** {}", built::RUSTC_VERSION)?;
|
||||
if !built::RUSTDOC_VERSION.is_empty() {
|
||||
writeln!(info, "**Rustdoc Version:** {}", built::RUSTDOC_VERSION)?;
|
||||
}
|
||||
|
||||
// Target configuration
|
||||
writeln!(info, "\n## Target Configuration\n")?;
|
||||
writeln!(info, "**Architecture:** {}", built::CFG_TARGET_ARCH)?;
|
||||
writeln!(info, "**OS:** {}", built::CFG_OS)?;
|
||||
writeln!(info, "**Family:** {}", built::CFG_FAMILY)?;
|
||||
writeln!(info, "**Endianness:** {}", built::CFG_ENDIAN)?;
|
||||
writeln!(info, "**Pointer Width:** {} bits", built::CFG_POINTER_WIDTH)?;
|
||||
if !built::CFG_ENV.is_empty() {
|
||||
writeln!(info, "**Environment:** {}", built::CFG_ENV)?;
|
||||
}
|
||||
|
||||
// CI information
|
||||
if let Some(ci) = built::CI_PLATFORM {
|
||||
writeln!(info, "\n## CI Platform\n")?;
|
||||
writeln!(info, "**Platform:** {ci}")?;
|
||||
}
|
||||
|
||||
self.write_str(&info).await
|
||||
}
|
||||
|
||||
@@ -52,4 +52,10 @@ pub enum ServerCommand {
|
||||
|
||||
/// Shutdown the server
|
||||
Shutdown,
|
||||
|
||||
/// List features built into the server
|
||||
ListFeatures,
|
||||
|
||||
/// Build information
|
||||
BuildInfo,
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "conduwuit_api"
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -76,8 +77,10 @@ axum.workspace = true
|
||||
base64.workspace = true
|
||||
bytes.workspace = true
|
||||
conduwuit-core.workspace = true
|
||||
conduwuit-macros.workspace = true
|
||||
conduwuit-service.workspace = true
|
||||
const-str.workspace = true
|
||||
ctor.workspace = true
|
||||
futures.workspace = true
|
||||
hmac.workspace = true
|
||||
http.workspace = true
|
||||
|
||||
@@ -425,7 +425,9 @@ pub async fn full_user_deactivate(
|
||||
// TODO: Redact all messages sent by the user in the room
|
||||
}
|
||||
|
||||
super::update_all_rooms(services, pdu_queue, user_id).await;
|
||||
super::update_all_rooms(services, pdu_queue, user_id)
|
||||
.boxed()
|
||||
.await;
|
||||
for room_id in all_joined_rooms {
|
||||
services.rooms.state_cache.forget(room_id, user_id);
|
||||
}
|
||||
|
||||
@@ -462,6 +462,31 @@ async fn create_registration_uiaa_session(
|
||||
flows.push(untrusted_flow);
|
||||
}
|
||||
|
||||
// Require all users to agree to the terms and conditions, if configured
|
||||
let terms = &services.config.registration_terms;
|
||||
if !terms.is_empty() {
|
||||
let mut terms =
|
||||
serde_json::to_value(terms.clone()).expect("failed to serialize terms");
|
||||
|
||||
// Insert a dummy `version` field
|
||||
for (_, documents) in terms.as_object_mut().unwrap() {
|
||||
let documents = documents.as_object_mut().unwrap();
|
||||
|
||||
documents.insert("version".to_owned(), "latest".into());
|
||||
}
|
||||
|
||||
params.insert(
|
||||
AuthType::Terms.as_str().to_owned(),
|
||||
serde_json::json!({
|
||||
"policies": terms,
|
||||
}),
|
||||
);
|
||||
|
||||
for flow in &mut flows {
|
||||
flow.stages.insert(0, AuthType::Terms);
|
||||
}
|
||||
}
|
||||
|
||||
if flows.is_empty() {
|
||||
// No flows are configured. Bail out by default
|
||||
// unless open registration was explicitly enabled.
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::iter::once;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Event, Result, err, info,
|
||||
Err, Event, Result, RoomVersion, err, info,
|
||||
utils::{
|
||||
TryFutureExtExt,
|
||||
math::Expected,
|
||||
@@ -30,12 +32,14 @@
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
create::RoomCreateEventContent,
|
||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
},
|
||||
},
|
||||
uint,
|
||||
};
|
||||
use tokio::join;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -339,36 +343,63 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
})
|
||||
}
|
||||
|
||||
/// Check whether the user can publish to the room directory via power levels of
|
||||
/// room history visibility event or room creator
|
||||
/// Checks whether the given user ID is allowed to publish the target room to
|
||||
/// the server's public room directory. Users are allowed to publish rooms if
|
||||
/// they are server admins, room creators (in v12), or have the power level to
|
||||
/// send `m.room.canonical_alias`.
|
||||
async fn user_can_publish_room(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
) -> Result<bool> {
|
||||
match services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")
|
||||
.await
|
||||
if services.users.is_admin(user_id).await {
|
||||
// Server admins can always publish to their own room directory.
|
||||
return Ok(true);
|
||||
}
|
||||
let (create_event, room_version, power_levels_content) = join!(
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, ""),
|
||||
services.rooms.state.get_room_version(room_id),
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<RoomPowerLevelsEventContent>(
|
||||
room_id,
|
||||
&StateEventType::RoomPowerLevels,
|
||||
""
|
||||
)
|
||||
);
|
||||
let room_version = room_version
|
||||
.as_ref()
|
||||
.map_err(|_| err!(Request(NotFound("Unknown room"))))?;
|
||||
let create_event = create_event.map_err(|_| err!(Request(NotFound("Unknown room"))))?;
|
||||
if RoomVersion::new(room_version)
|
||||
.expect("room version must be supported")
|
||||
.explicitly_privilege_room_creators
|
||||
{
|
||||
| Ok(event) => serde_json::from_str(event.content().get())
|
||||
.map_err(|_| err!(Database("Invalid event content for m.room.power_levels")))
|
||||
.map(|content: RoomPowerLevelsEventContent| {
|
||||
RoomPowerLevels::from(content)
|
||||
.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
||||
}),
|
||||
| _ => {
|
||||
match services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
{
|
||||
| Ok(event) => Ok(event.sender() == user_id),
|
||||
| _ => Err!(Request(Forbidden("User is not allowed to publish this room"))),
|
||||
}
|
||||
},
|
||||
let create_content: RoomCreateEventContent =
|
||||
serde_json::from_str(create_event.content().get())
|
||||
.map_err(|_| err!(Database("Invalid event content for m.room.create")))?;
|
||||
let is_creator = create_content
|
||||
.additional_creators
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.chain(once(create_event.sender().to_owned()))
|
||||
.any(|sender| sender == user_id);
|
||||
if is_creator {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
match power_levels_content.map(RoomPowerLevels::from) {
|
||||
| Ok(pl) => Ok(pl.user_can_send_state(user_id, StateEventType::RoomCanonicalAlias)),
|
||||
| Err(e) =>
|
||||
if e.is_not_found() {
|
||||
Ok(create_event.sender() == user_id)
|
||||
} else {
|
||||
Err!(Database("Invalid event content for m.room.power_levels: {e}"))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
};
|
||||
use conduwuit_service::Services;
|
||||
use futures::{
|
||||
StreamExt, TryStreamExt,
|
||||
FutureExt, StreamExt, TryStreamExt,
|
||||
future::{join, join3, join4},
|
||||
};
|
||||
use ruma::{
|
||||
@@ -51,6 +51,7 @@ pub(crate) async fn set_displayname_route(
|
||||
.await;
|
||||
|
||||
update_displayname(&services, &body.user_id, body.displayname.clone(), &all_joined_rooms)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
@@ -149,6 +150,7 @@ pub(crate) async fn set_avatar_url_route(
|
||||
body.blurhash.clone(),
|
||||
&all_joined_rooms,
|
||||
)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
@@ -344,7 +346,9 @@ pub async fn update_displayname(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
update_all_rooms(services, all_joined_rooms, user_id).await;
|
||||
update_all_rooms(services, all_joined_rooms, user_id)
|
||||
.boxed()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn update_avatar_url(
|
||||
@@ -394,7 +398,9 @@ pub async fn update_avatar_url(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
update_all_rooms(services, all_joined_rooms, user_id).await;
|
||||
update_all_rooms(services, all_joined_rooms, user_id)
|
||||
.boxed()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn update_all_rooms(
|
||||
|
||||
@@ -137,6 +137,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
Some(&body.room_id),
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
// Change lock to replacement room
|
||||
drop(state_lock);
|
||||
|
||||
@@ -60,6 +60,7 @@ pub(crate) async fn send_state_event_for_key_route(
|
||||
None
|
||||
},
|
||||
)
|
||||
.boxed()
|
||||
.await?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ pub(super) async fn load_joined_room(
|
||||
and `join*` functions are used to perform steps in parallel which do not depend on each other.
|
||||
*/
|
||||
|
||||
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
|
||||
let (
|
||||
account_data,
|
||||
ephemeral,
|
||||
@@ -82,6 +83,7 @@ pub(super) async fn load_joined_room(
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
drop(insert_lock);
|
||||
|
||||
if !timeline.is_empty() || !state_events.is_empty() {
|
||||
trace!(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{Err, Result};
|
||||
use futures::StreamExt;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
OwnedRoomId,
|
||||
api::{
|
||||
@@ -112,6 +112,7 @@ pub(crate) async fn set_profile_key_route(
|
||||
Some(display_name.to_owned()),
|
||||
&all_joined_rooms,
|
||||
)
|
||||
.boxed()
|
||||
.await;
|
||||
} else if body.key_name == "avatar_url" {
|
||||
let Some(avatar_url) = profile_key_value.as_str() else {
|
||||
@@ -127,7 +128,9 @@ pub(crate) async fn set_profile_key_route(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
update_avatar_url(&services, &body.user_id, Some(mxc), None, &all_joined_rooms).await;
|
||||
update_avatar_url(&services, &body.user_id, Some(mxc), None, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await;
|
||||
} else {
|
||||
services.users.set_profile_key(
|
||||
&body.user_id,
|
||||
@@ -178,7 +181,9 @@ pub(crate) async fn delete_profile_key_route(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
update_displayname(&services, &body.user_id, None, &all_joined_rooms).await;
|
||||
update_displayname(&services, &body.user_id, None, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await;
|
||||
} else if body.key_name == "avatar_url" {
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
@@ -188,7 +193,9 @@ pub(crate) async fn delete_profile_key_route(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
update_avatar_url(&services, &body.user_id, None, None, &all_joined_rooms).await;
|
||||
update_avatar_url(&services, &body.user_id, None, None, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await;
|
||||
} else {
|
||||
services
|
||||
.users
|
||||
|
||||
@@ -71,6 +71,7 @@ pub(crate) async fn well_known_support(
|
||||
|
||||
let email_address = services.config.well_known.support_email.clone();
|
||||
let matrix_id = services.config.well_known.support_mxid.clone();
|
||||
let pgp_key = services.config.well_known.support_pgp_key.clone();
|
||||
|
||||
// TODO: support defining multiple contacts in the config
|
||||
let mut contacts: Vec<Contact> = vec![];
|
||||
@@ -88,6 +89,7 @@ pub(crate) async fn well_known_support(
|
||||
role: role_value.clone(),
|
||||
email_address: email_address.clone(),
|
||||
matrix_id: matrix_id.clone(),
|
||||
pgp_key: pgp_key.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,6 +106,7 @@ pub(crate) async fn well_known_support(
|
||||
role: role_value.clone(),
|
||||
email_address: None,
|
||||
matrix_id: Some(user_id.to_owned()),
|
||||
pgp_key: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
extern crate conduwuit_core as conduwuit;
|
||||
extern crate conduwuit_service as service;
|
||||
|
||||
conduwuit_macros::introspect_crate! {}
|
||||
|
||||
pub mod client;
|
||||
pub mod router;
|
||||
pub mod server;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use std::borrow::ToOwned;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Error, Result, debug, debug_info, info, matrix::pdu::PduBuilder, warn};
|
||||
use conduwuit::{
|
||||
Err, Error, Result, debug, debug_info, info, matrix::pdu::PduBuilder, utils, warn,
|
||||
};
|
||||
use conduwuit_service::Services;
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
CanonicalJsonObject, OwnedUserId, RoomId, RoomVersionId, UserId,
|
||||
OwnedUserId, RoomId, RoomVersionId, UserId,
|
||||
api::{client::error::ErrorKind, federation::membership::prepare_join_event},
|
||||
events::{
|
||||
StateEventType,
|
||||
@@ -40,6 +42,7 @@ pub(crate) async fn create_join_event_template_route(
|
||||
{
|
||||
info!(
|
||||
origin = body.origin().as_str(),
|
||||
room_id = %body.room_id,
|
||||
"Refusing to serve make_join for room we aren't participating in"
|
||||
);
|
||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||
@@ -133,10 +136,10 @@ pub(crate) async fn create_join_event_template_route(
|
||||
}
|
||||
}
|
||||
|
||||
let (_pdu, mut pdu_json) = services
|
||||
let (pdu, _) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
.create_event(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
join_authorized_via_users_server,
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
@@ -147,6 +150,8 @@ pub(crate) async fn create_join_event_template_route(
|
||||
)
|
||||
.await?;
|
||||
drop(state_lock);
|
||||
let mut pdu_json = utils::to_canonical_object(&pdu)
|
||||
.expect("Barebones PDU should be convertible to canonical JSON");
|
||||
pdu_json.remove("event_id");
|
||||
|
||||
Ok(prepare_join_event::v1::Response {
|
||||
@@ -297,18 +302,3 @@ pub(crate) async 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 => Ok(()),
|
||||
| _ => {
|
||||
pdu_json.remove("event_id");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use RoomVersionId::*;
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Error, Result, debug_warn, info, matrix::pdu::PduBuilder, warn};
|
||||
use conduwuit::{Err, Error, Result, debug_warn, info, matrix::pdu::PduBuilder, utils, warn};
|
||||
use ruma::{
|
||||
RoomVersionId,
|
||||
api::{client::error::ErrorKind, federation::knock::create_knock_event_template},
|
||||
@@ -28,6 +28,7 @@ pub(crate) async fn create_knock_event_template_route(
|
||||
{
|
||||
info!(
|
||||
origin = body.origin().as_str(),
|
||||
room_id = %body.room_id,
|
||||
"Refusing to serve make_knock for room we aren't participating in"
|
||||
);
|
||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||
@@ -98,10 +99,10 @@ pub(crate) async fn create_knock_event_template_route(
|
||||
}
|
||||
}
|
||||
|
||||
let (_pdu, mut pdu_json) = services
|
||||
let (pdu, _) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
.create_event(
|
||||
PduBuilder::state(
|
||||
body.user_id.to_string(),
|
||||
&RoomMemberEventContent::new(MembershipState::Knock),
|
||||
@@ -113,9 +114,9 @@ pub(crate) async fn create_knock_event_template_route(
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
// room v3 and above removed the "event_id" field from remote PDU format
|
||||
super::maybe_strip_event_id(&mut pdu_json, &room_version_id)?;
|
||||
let mut pdu_json = utils::to_canonical_object(&pdu)
|
||||
.expect("Barebones PDU should be convertible to canonical JSON");
|
||||
pdu_json.remove("event_id");
|
||||
|
||||
Ok(create_knock_event_template::v1::Response {
|
||||
room_version: room_version_id,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, info, matrix::pdu::PduBuilder};
|
||||
use conduwuit::{Err, Result, info, matrix::pdu::PduBuilder, utils};
|
||||
use ruma::{
|
||||
api::federation::membership::prepare_leave_event,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
|
||||
use super::make_join::maybe_strip_event_id;
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_matrix/federation/v1/make_leave/{roomId}/{eventId}`
|
||||
@@ -49,10 +48,10 @@ pub(crate) async fn create_leave_event_template_route(
|
||||
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
let (_pdu, mut pdu_json) = services
|
||||
let (pdu, _) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
.create_event(
|
||||
PduBuilder::state(
|
||||
body.user_id.to_string(),
|
||||
&RoomMemberEventContent::new(MembershipState::Leave),
|
||||
@@ -64,9 +63,9 @@ pub(crate) async fn create_leave_event_template_route(
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
// room v3 and above removed the "event_id" field from remote PDU format
|
||||
maybe_strip_event_id(&mut pdu_json, &room_version_id)?;
|
||||
let mut pdu_json = utils::to_canonical_object(&pdu)
|
||||
.expect("Barebones PDU should be convertible to canonical JSON");
|
||||
pdu_json.remove("event_id");
|
||||
|
||||
Ok(prepare_leave_event::v1::Response {
|
||||
room_version: Some(room_version_id),
|
||||
|
||||
@@ -187,13 +187,14 @@ async fn create_join_event(
|
||||
"Joining user did not pass restricted room's rules."
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
trace!("Signing send_join event");
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut value, &room_version_id)
|
||||
.map_err(|e| err!(Request(InvalidParam(warn!("Failed to sign send_join event: {e}")))))?;
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut value, &room_version_id)
|
||||
.map_err(|e| {
|
||||
err!(Request(InvalidParam(warn!("Failed to sign send_join event: {e}"))))
|
||||
})?;
|
||||
}
|
||||
|
||||
let mutex_lock = services
|
||||
.rooms
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "conduwuit_build_metadata"
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -27,6 +28,6 @@ crate-type = [
|
||||
|
||||
[build-dependencies]
|
||||
built = { version = "0.8", features = [] }
|
||||
|
||||
cargo_metadata = { version = "0.23.1" }
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use std::process::Command;
|
||||
use std::{
|
||||
collections::BTreeMap, env, fmt::Write as FmtWrite, fs, io::Write, path::Path,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use cargo_metadata::MetadataCommand;
|
||||
fn run_git_command(args: &[&str]) -> Option<String> {
|
||||
Command::new("git")
|
||||
.args(args)
|
||||
@@ -11,12 +15,60 @@ fn run_git_command(args: &[&str]) -> Option<String> {
|
||||
.filter(|s| !s.is_empty())
|
||||
}
|
||||
fn get_env(env_var: &str) -> Option<String> {
|
||||
match std::env::var(env_var) {
|
||||
match env::var(env_var) {
|
||||
| Ok(val) if !val.is_empty() => Some(val),
|
||||
| _ => None,
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=Cargo.toml");
|
||||
|
||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); // Cargo.toml path
|
||||
let manifest_path = Path::new(&manifest_dir).join("Cargo.toml");
|
||||
|
||||
let metadata = MetadataCommand::new()
|
||||
.manifest_path(&manifest_path)
|
||||
.no_deps()
|
||||
.exec()
|
||||
.expect("failed to parse `cargo metadata`");
|
||||
|
||||
let workspace_packages = metadata
|
||||
.workspace_members
|
||||
.iter()
|
||||
.map(|package| {
|
||||
let package = metadata.packages.iter().find(|p| p.id == *package).unwrap();
|
||||
println!("cargo:rerun-if-changed={}", package.manifest_path.as_str());
|
||||
package
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Extract available features from workspace packages
|
||||
let mut available_features: BTreeMap<String, Vec<String>> = BTreeMap::new();
|
||||
for package in &workspace_packages {
|
||||
let crate_name = package
|
||||
.name
|
||||
.trim_start_matches("conduwuit-")
|
||||
.replace('-', "_");
|
||||
let features: Vec<String> = package.features.keys().cloned().collect();
|
||||
if !features.is_empty() {
|
||||
available_features.insert(crate_name, features);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Rust code for available features
|
||||
let features_code = generate_features_code(&available_features);
|
||||
let features_dst =
|
||||
Path::new(&env::var("OUT_DIR").expect("OUT_DIR not set")).join("available_features.rs");
|
||||
let mut features_file = fs::File::create(features_dst).unwrap();
|
||||
features_file.write_all(features_code.as_bytes()).unwrap();
|
||||
|
||||
let dst = Path::new(&env::var("OUT_DIR").expect("OUT_DIR not set")).join("pkg.json");
|
||||
|
||||
let mut out_file = fs::File::create(dst).unwrap();
|
||||
out_file
|
||||
.write_all(format!("{workspace_packages:?}").as_bytes())
|
||||
.unwrap();
|
||||
|
||||
// built gets the default crate from the workspace. Not sure if this is intended
|
||||
// behavior, but it's what we want.
|
||||
built::write_built_file().expect("Failed to acquire build-time information");
|
||||
@@ -91,3 +143,30 @@ fn main() {
|
||||
println!("cargo:rerun-if-env-changed=GIT_REMOTE_URL");
|
||||
println!("cargo:rerun-if-env-changed=GIT_REMOTE_COMMIT_URL");
|
||||
}
|
||||
|
||||
fn generate_features_code(features: &BTreeMap<String, Vec<String>>) -> String {
|
||||
let mut code = String::from(
|
||||
r#"
|
||||
/// All available features for workspace crates
|
||||
pub const WORKSPACE_FEATURES: &[(&str, &[&str])] = &[
|
||||
"#,
|
||||
);
|
||||
|
||||
for (crate_name, feature_list) in features {
|
||||
write!(code, " (\"{crate_name}\", &[").unwrap();
|
||||
for (i, feature) in feature_list.iter().enumerate() {
|
||||
if i > 0 {
|
||||
code.push_str(", ");
|
||||
}
|
||||
write!(code, "\"{feature}\"").unwrap();
|
||||
}
|
||||
code.push_str("]),\n");
|
||||
}
|
||||
|
||||
code.push_str(
|
||||
r#"];
|
||||
"#,
|
||||
);
|
||||
|
||||
code
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@ pub mod built {
|
||||
include!(concat!(env!("OUT_DIR"), "/built.rs"));
|
||||
}
|
||||
|
||||
// Include generated available features
|
||||
// This provides: pub const WORKSPACE_FEATURES: &[(&str, &[&str])]
|
||||
include!(concat!(env!("OUT_DIR"), "/available_features.rs"));
|
||||
|
||||
pub static GIT_COMMIT_HASH: Option<&str> = option_env!("GIT_COMMIT_HASH");
|
||||
|
||||
pub static GIT_COMMIT_HASH_SHORT: Option<&str> = option_env!("GIT_COMMIT_HASH_SHORT");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "conduwuit_core"
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -116,6 +117,7 @@ url.workspace = true
|
||||
parking_lot.workspace = true
|
||||
lock_api.workspace = true
|
||||
hyper-util.workspace = true
|
||||
resolvematrix.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix.workspace = true
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
pub mod proxy;
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
collections::{BTreeMap, BTreeSet, HashMap},
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
path::PathBuf,
|
||||
};
|
||||
@@ -22,7 +22,7 @@
|
||||
OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, RoomVersionId,
|
||||
api::client::discovery::{discover_homeserver::RtcFocusInfo, discover_support::ContactRole},
|
||||
};
|
||||
use serde::{Deserialize, de::IgnoredAny};
|
||||
use serde::{Deserialize, Serialize, de::IgnoredAny};
|
||||
use url::Url;
|
||||
|
||||
use self::proxy::ProxyConfig;
|
||||
@@ -655,6 +655,20 @@ pub struct Config {
|
||||
/// even if `recaptcha_site_key` is set.
|
||||
pub recaptcha_private_site_key: Option<String>,
|
||||
|
||||
/// Policy documents, such as terms and conditions or a privacy policy,
|
||||
/// which users must agree to when registering an account.
|
||||
///
|
||||
/// Example:
|
||||
/// ```ignore
|
||||
/// [global.registration_terms.privacy_policy]
|
||||
/// en = { name = "Privacy Policy", url = "https://homeserver.example/en/privacy_policy.html" }
|
||||
/// es = { name = "Política de Privacidad", url = "https://homeserver.example/es/privacy_policy.html" }
|
||||
/// ```
|
||||
///
|
||||
/// default: {}
|
||||
#[serde(default)]
|
||||
pub registration_terms: HashMap<String, HashMap<String, TermsDocument>>,
|
||||
|
||||
/// Controls whether encrypted rooms and events are allowed.
|
||||
#[serde(default = "true_fn")]
|
||||
pub allow_encryption: bool,
|
||||
@@ -2059,12 +2073,10 @@ pub struct Config {
|
||||
pub stream_amplification: usize,
|
||||
|
||||
/// Number of sender task workers; determines sender parallelism. Default is
|
||||
/// '0' which means the value is determined internally, likely matching the
|
||||
/// number of tokio worker-threads or number of cores, etc. Override by
|
||||
/// setting a non-zero value.
|
||||
/// core count. Override by setting a different value.
|
||||
///
|
||||
/// default: 0
|
||||
#[serde(default)]
|
||||
/// default: core count
|
||||
#[serde(default = "default_sender_workers")]
|
||||
pub sender_workers: usize,
|
||||
|
||||
/// Enables listener sockets; can be set to false to disable listening. This
|
||||
@@ -2191,6 +2203,10 @@ pub struct WellKnownConfig {
|
||||
/// listed.
|
||||
pub support_mxid: Option<OwnedUserId>,
|
||||
|
||||
/// PGP key URI for server support contacts, to be served as part of the
|
||||
/// MSC1929 server support endpoint.
|
||||
pub support_pgp_key: Option<String>,
|
||||
|
||||
/// **DEPRECATED**: Use `[global.matrix_rtc].foci` instead.
|
||||
///
|
||||
/// A list of MatrixRTC foci URLs which will be served as part of the
|
||||
@@ -2494,6 +2510,13 @@ pub struct SmtpConfig {
|
||||
pub require_email_for_token_registration: bool,
|
||||
}
|
||||
|
||||
/// A policy document for use with a m.login.terms stage.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct TermsDocument {
|
||||
pub name: String,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
const DEPRECATED_KEYS: &[&str] = &[
|
||||
"cache_capacity",
|
||||
"conduit_cache_capacity_modifier",
|
||||
@@ -2592,45 +2615,47 @@ fn default_database_backups_to_keep() -> i16 { 1 }
|
||||
|
||||
fn default_db_write_buffer_capacity_mb() -> f64 { 48.0 + parallelism_scaled_f64(4.0) }
|
||||
|
||||
fn default_db_cache_capacity_mb() -> f64 { 128.0 + parallelism_scaled_f64(64.0) }
|
||||
fn default_db_cache_capacity_mb() -> f64 { 512.0 + parallelism_scaled_f64(512.0) }
|
||||
|
||||
fn default_pdu_cache_capacity() -> u32 { parallelism_scaled_u32(10_000).saturating_add(100_000) }
|
||||
fn default_pdu_cache_capacity() -> u32 { parallelism_scaled_u32(50_000).saturating_add(100_000) }
|
||||
|
||||
fn default_cache_capacity_modifier() -> f64 { 1.0 }
|
||||
|
||||
fn default_auth_chain_cache_capacity() -> u32 {
|
||||
parallelism_scaled_u32(10_000).saturating_add(100_000)
|
||||
}
|
||||
|
||||
fn default_shorteventid_cache_capacity() -> u32 {
|
||||
parallelism_scaled_u32(50_000).saturating_add(100_000)
|
||||
}
|
||||
|
||||
fn default_shorteventid_cache_capacity() -> u32 {
|
||||
parallelism_scaled_u32(100_000).saturating_add(100_000)
|
||||
}
|
||||
|
||||
fn default_eventidshort_cache_capacity() -> u32 {
|
||||
parallelism_scaled_u32(25_000).saturating_add(100_000)
|
||||
parallelism_scaled_u32(50_000).saturating_add(100_000)
|
||||
}
|
||||
|
||||
fn default_eventid_pdu_cache_capacity() -> u32 {
|
||||
parallelism_scaled_u32(25_000).saturating_add(100_000)
|
||||
parallelism_scaled_u32(50_000).saturating_add(100_000)
|
||||
}
|
||||
|
||||
fn default_shortstatekey_cache_capacity() -> u32 {
|
||||
parallelism_scaled_u32(10_000).saturating_add(100_000)
|
||||
parallelism_scaled_u32(50_000).saturating_add(100_000)
|
||||
}
|
||||
|
||||
fn default_statekeyshort_cache_capacity() -> u32 {
|
||||
parallelism_scaled_u32(10_000).saturating_add(100_000)
|
||||
parallelism_scaled_u32(50_000).saturating_add(100_000)
|
||||
}
|
||||
|
||||
fn default_servernameevent_data_cache_capacity() -> u32 {
|
||||
parallelism_scaled_u32(100_000).saturating_add(500_000)
|
||||
parallelism_scaled_u32(100_000).saturating_add(100_000)
|
||||
}
|
||||
|
||||
fn default_stateinfo_cache_capacity() -> u32 { parallelism_scaled_u32(100) }
|
||||
fn default_stateinfo_cache_capacity() -> u32 { parallelism_scaled_u32(500).clamp(100, 12000) }
|
||||
|
||||
fn default_roomid_spacehierarchy_cache_capacity() -> u32 { parallelism_scaled_u32(1000) }
|
||||
fn default_roomid_spacehierarchy_cache_capacity() -> u32 {
|
||||
parallelism_scaled_u32(500).clamp(100, 12000)
|
||||
}
|
||||
|
||||
fn default_dns_cache_entries() -> u32 { 32768 }
|
||||
fn default_dns_cache_entries() -> u32 { 327_680 }
|
||||
|
||||
fn default_dns_min_ttl() -> u64 { 60 * 180 }
|
||||
|
||||
@@ -2838,15 +2863,26 @@ fn default_admin_log_capture() -> String {
|
||||
|
||||
fn default_admin_room_tag() -> String { "m.server_notice".to_owned() }
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::as_conversions, clippy::cast_precision_loss)]
|
||||
fn parallelism_scaled_f64(val: f64) -> f64 { val * (sys::available_parallelism() as f64) }
|
||||
pub fn parallelism_scaled_f64(val: f64) -> f64 { val * (sys::available_parallelism() as f64) }
|
||||
|
||||
fn parallelism_scaled_u32(val: u32) -> u32 {
|
||||
let val = val.try_into().expect("failed to cast u32 to usize");
|
||||
parallelism_scaled(val).try_into().unwrap_or(u32::MAX)
|
||||
#[must_use]
|
||||
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
|
||||
pub fn parallelism_scaled_u32(val: u32) -> u32 {
|
||||
val.saturating_mul(sys::available_parallelism() as u32)
|
||||
}
|
||||
|
||||
fn parallelism_scaled(val: usize) -> usize { val.saturating_mul(sys::available_parallelism()) }
|
||||
#[must_use]
|
||||
#[allow(clippy::as_conversions, clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
|
||||
pub fn parallelism_scaled_i32(val: i32) -> i32 {
|
||||
val.saturating_mul(sys::available_parallelism() as i32)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn parallelism_scaled(val: usize) -> usize {
|
||||
val.saturating_mul(sys::available_parallelism())
|
||||
}
|
||||
|
||||
fn default_trusted_server_batch_size() -> usize { 256 }
|
||||
|
||||
@@ -2866,6 +2902,8 @@ fn default_stream_width_scale() -> f32 { 1.0 }
|
||||
|
||||
fn default_stream_amplification() -> usize { 1024 }
|
||||
|
||||
fn default_sender_workers() -> usize { parallelism_scaled(1) }
|
||||
|
||||
fn default_client_receive_timeout() -> u64 { 75 }
|
||||
|
||||
fn default_client_request_timeout() -> u64 { 180 }
|
||||
|
||||
@@ -86,6 +86,8 @@ pub enum Error {
|
||||
YamlDe(#[from] serde_saphyr::Error),
|
||||
#[error(transparent)]
|
||||
YamlSer(#[from] serde_saphyr::ser_error::Error),
|
||||
#[error(transparent)]
|
||||
ResolveServer(#[from] resolvematrix::server::ResolveServerError),
|
||||
|
||||
// ruma/conduwuit
|
||||
#[error("Arithmetic operation failed: {0}")]
|
||||
|
||||
7
src/core/info/introspection.rs
Normal file
7
src/core/info/introspection.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
//! Information about features the crates were compiled with.
|
||||
//! Only available for crates that have called the `introspect_crate` macro
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub static ENABLED_FEATURES: std::sync::Mutex<BTreeMap<&str, &[&str]>> =
|
||||
std::sync::Mutex::new(BTreeMap::new());
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod introspection;
|
||||
pub mod room_version;
|
||||
pub mod version;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
pub use ::smallvec;
|
||||
pub use ::toml;
|
||||
pub use ::tracing;
|
||||
pub use conduwuit_build_metadata as build_metadata;
|
||||
pub use config::Config;
|
||||
pub use error::Error;
|
||||
pub use info::{
|
||||
@@ -34,6 +35,8 @@
|
||||
|
||||
pub use crate as conduwuit_core;
|
||||
|
||||
conduwuit_macros::introspect_crate! {}
|
||||
|
||||
#[cfg(any(not(conduwuit_mods), not(feature = "conduwuit_mods")))]
|
||||
pub mod mods {
|
||||
#[macro_export]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "conduwuit_database"
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -54,7 +55,9 @@ bindgen-runtime = [
|
||||
[dependencies]
|
||||
async-channel.workspace = true
|
||||
conduwuit-core.workspace = true
|
||||
conduwuit-macros.workspace = true
|
||||
const-str.workspace = true
|
||||
ctor.workspace = true
|
||||
futures.workspace = true
|
||||
log.workspace = true
|
||||
minicbor.workspace = true
|
||||
|
||||
@@ -29,7 +29,7 @@ fn descriptor_cf_options(
|
||||
set_table_options(&mut opts, &desc, cache)?;
|
||||
|
||||
opts.set_min_write_buffer_number(1);
|
||||
opts.set_max_write_buffer_number(2);
|
||||
opts.set_max_write_buffer_number(3);
|
||||
opts.set_write_buffer_size(desc.write_size);
|
||||
|
||||
opts.set_target_file_size_base(desc.file_size);
|
||||
@@ -70,17 +70,19 @@ fn descriptor_cf_options(
|
||||
);
|
||||
}
|
||||
|
||||
let mut opts = opts
|
||||
let opts = opts
|
||||
.get_options_from_string("{{arena_block_size=2097152;}}")
|
||||
.map_err(map_err)?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
let opts = opts
|
||||
.get_options_from_string(
|
||||
let opts = {
|
||||
let mut opts = opts;
|
||||
opts.get_options_from_string(
|
||||
"{{paranoid_checks=true;paranoid_file_checks=true;force_consistency_checks=true;\
|
||||
verify_sst_unique_id_in_manifest=true;}}",
|
||||
)
|
||||
.map_err(map_err)?;
|
||||
.map_err(map_err)?
|
||||
};
|
||||
|
||||
Ok(opts)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
extern crate conduwuit_core as conduwuit;
|
||||
extern crate rust_rocksdb as rocksdb;
|
||||
|
||||
conduwuit_macros::introspect_crate! {}
|
||||
|
||||
conduwuit::mod_ctor! {}
|
||||
conduwuit::mod_dtor! {}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "conduwuit_macros"
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -17,6 +18,7 @@ syn.workspace = true
|
||||
quote.workspace = true
|
||||
proc-macro2.workspace = true
|
||||
itertools.workspace = true
|
||||
cargo_toml.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
63
src/macros/build_info.rs
Normal file
63
src/macros/build_info.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
pub(super) fn introspect(_args: TokenStream) -> Result<TokenStream> {
|
||||
let cargo_crate_name = std::env::var("CARGO_CRATE_NAME").unwrap();
|
||||
let crate_name = cargo_crate_name.trim_start_matches("conduwuit_");
|
||||
let is_core = cargo_crate_name == "conduwuit_core";
|
||||
|
||||
let flags = std::env::args().collect::<Vec<_>>();
|
||||
|
||||
let mut enabled_features = Vec::new();
|
||||
append_features(&mut enabled_features, flags);
|
||||
|
||||
let enabled_count = enabled_features.len();
|
||||
|
||||
let import_path = if is_core {
|
||||
quote! { use crate::conduwuit_core; }
|
||||
} else {
|
||||
quote! { use ::conduwuit_core; }
|
||||
};
|
||||
|
||||
let ret = quote! {
|
||||
#[doc(hidden)]
|
||||
mod __compile_introspection {
|
||||
#import_path
|
||||
|
||||
/// Features that were enabled when this crate was compiled
|
||||
const ENABLED: [&str; #enabled_count] = [#( #enabled_features ),*];
|
||||
|
||||
const CRATE_NAME: &str = #crate_name;
|
||||
|
||||
/// Register this crate's features with the global registry during static initialization
|
||||
#[::ctor::ctor]
|
||||
fn register() {
|
||||
conduwuit_core::info::introspection::ENABLED_FEATURES.lock().unwrap().insert(#crate_name, &ENABLED);
|
||||
}
|
||||
#[::ctor::dtor]
|
||||
fn unregister() {
|
||||
conduwuit_core::info::introspection::ENABLED_FEATURES.lock().unwrap().remove(#crate_name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn append_features(features: &mut Vec<String>, flags: Vec<String>) {
|
||||
let mut next_is_cfg = false;
|
||||
for flag in flags {
|
||||
let is_cfg = flag == "--cfg";
|
||||
let is_feature = flag.starts_with("feature=");
|
||||
if std::mem::replace(&mut next_is_cfg, is_cfg) && is_feature {
|
||||
if let Some(feature) = flag
|
||||
.split_once('=')
|
||||
.map(|(_, feature)| feature.trim_matches('"'))
|
||||
{
|
||||
features.push(feature.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
mod admin;
|
||||
mod build_info;
|
||||
mod config;
|
||||
mod debug;
|
||||
mod implement;
|
||||
@@ -44,6 +45,13 @@ pub fn config_example_generator(args: TokenStream, input: TokenStream) -> TokenS
|
||||
attribute_macro::<ItemStruct, _>(args, input, config::example_generator)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn introspect_crate(input: TokenStream) -> TokenStream {
|
||||
build_info::introspect(input.into())
|
||||
.unwrap_or_else(|e| e.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
fn attribute_macro<I, F>(args: TokenStream, input: TokenStream, func: F) -> TokenStream
|
||||
where
|
||||
F: Fn(I, &[Meta]) -> Result<TokenStream>,
|
||||
|
||||
@@ -207,8 +207,10 @@ conduwuit-database.workspace = true
|
||||
conduwuit-router.workspace = true
|
||||
conduwuit-service.workspace = true
|
||||
conduwuit-build-metadata.workspace = true
|
||||
conduwuit-macros.workspace = true
|
||||
|
||||
clap.workspace = true
|
||||
ctor.workspace = true
|
||||
console-subscriber.optional = true
|
||||
console-subscriber.workspace = true
|
||||
const-str.workspace = true
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
use conduwuit_core::{debug_info, error};
|
||||
|
||||
conduwuit_macros::introspect_crate! {}
|
||||
|
||||
mod clap;
|
||||
mod deadlock;
|
||||
mod logging;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "conduwuit_router"
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -99,9 +100,11 @@ bytes.workspace = true
|
||||
conduwuit-admin.workspace = true
|
||||
conduwuit-api.workspace = true
|
||||
conduwuit-core.workspace = true
|
||||
conduwuit-macros.workspace = true
|
||||
conduwuit-service.workspace = true
|
||||
conduwuit-web.workspace = true
|
||||
const-str.workspace = true
|
||||
ctor.workspace = true
|
||||
futures.workspace = true
|
||||
http.workspace = true
|
||||
http-body-util.workspace = true
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
extern crate conduwuit_core as conduwuit;
|
||||
|
||||
conduwuit_macros::introspect_crate! {}
|
||||
|
||||
use std::{panic::AssertUnwindSafe, pin::Pin, sync::Arc};
|
||||
|
||||
use conduwuit::{Error, Result, Server};
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
extract::State,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use conduwuit::{Result, debug, debug_error, debug_warn, err, error, trace};
|
||||
use conduwuit::{Result, debug_warn, err, error, info, trace};
|
||||
use conduwuit_service::Services;
|
||||
use futures::FutureExt;
|
||||
use http::{Method, StatusCode, Uri};
|
||||
@@ -102,11 +102,11 @@ fn handle_result(method: &Method, uri: &Uri, result: Response) -> Result<Respons
|
||||
let reason = status.canonical_reason().unwrap_or("Unknown Reason");
|
||||
|
||||
if status.is_server_error() {
|
||||
error!(%method, %uri, "{code} {reason}");
|
||||
info!(%method, %uri, "{code} {reason}");
|
||||
} else if status.is_client_error() {
|
||||
debug_error!(%method, %uri, "{code} {reason}");
|
||||
info!(%method, %uri, "{code} {reason}");
|
||||
} else if status.is_redirection() {
|
||||
debug!(%method, %uri, "{code} {reason}");
|
||||
trace!(%method, %uri, "{code} {reason}");
|
||||
} else {
|
||||
trace!(%method, %uri, "{code} {reason}");
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "conduwuit_service"
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -88,7 +89,9 @@ base64.workspace = true
|
||||
bytes.workspace = true
|
||||
conduwuit-core.workspace = true
|
||||
conduwuit-database.workspace = true
|
||||
conduwuit-macros.workspace = true
|
||||
const-str.workspace = true
|
||||
ctor.workspace = true
|
||||
either.workspace = true
|
||||
futures.workspace = true
|
||||
governor.workspace = true
|
||||
@@ -126,6 +129,7 @@ blurhash.optional = true
|
||||
recaptcha-verify = { version = "0.2.0", default-features = false }
|
||||
yansi.workspace = true
|
||||
lettre.workspace = true
|
||||
resolvematrix.workspace = true
|
||||
|
||||
[target.'cfg(all(unix, target_os = "linux"))'.dependencies]
|
||||
sd-notify.workspace = true
|
||||
|
||||
@@ -43,7 +43,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
default: base(config)?
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.dns_resolver(resolver.dns.resolver.clone())
|
||||
.build()?,
|
||||
|
||||
url_preview: base(config)
|
||||
@@ -51,19 +51,19 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
builder_interface(builder, url_preview_bind_iface.as_deref())
|
||||
})?
|
||||
.local_address(url_preview_bind_addr)
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.dns_resolver(resolver.dns.resolver.clone())
|
||||
.timeout(Duration::from_secs(config.url_preview_timeout))
|
||||
.redirect(redirect::Policy::limited(3))
|
||||
.user_agent(url_preview_user_agent)
|
||||
.build()?,
|
||||
|
||||
extern_media: base(config)?
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.dns_resolver(resolver.dns.resolver.clone())
|
||||
.redirect(redirect::Policy::limited(3))
|
||||
.build()?,
|
||||
|
||||
well_known: base(config)?
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.dns_resolver(resolver.dns.resolver.clone())
|
||||
.connect_timeout(Duration::from_secs(config.well_known_conn_timeout))
|
||||
.read_timeout(Duration::from_secs(config.well_known_timeout))
|
||||
.timeout(Duration::from_secs(config.well_known_timeout))
|
||||
@@ -72,7 +72,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
.build()?,
|
||||
|
||||
federation: base(config)?
|
||||
.dns_resolver(resolver.resolver.hooked.clone())
|
||||
.dns_resolver(resolver.dns.resolver.hooked.clone())
|
||||
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
|
||||
.read_timeout(Duration::from_secs(config.federation_timeout))
|
||||
.timeout(Duration::from_secs(
|
||||
@@ -86,7 +86,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
.build()?,
|
||||
|
||||
synapse: base(config)?
|
||||
.dns_resolver(resolver.resolver.hooked.clone())
|
||||
.dns_resolver(resolver.dns.resolver.hooked.clone())
|
||||
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
|
||||
.read_timeout(Duration::from_secs(config.federation_timeout.saturating_mul(6)))
|
||||
.timeout(Duration::from_secs(
|
||||
@@ -100,7 +100,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
.build()?,
|
||||
|
||||
sender: base(config)?
|
||||
.dns_resolver(resolver.resolver.hooked.clone())
|
||||
.dns_resolver(resolver.dns.resolver.hooked.clone())
|
||||
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
|
||||
.read_timeout(Duration::from_secs(config.sender_timeout))
|
||||
.timeout(Duration::from_secs(config.sender_timeout))
|
||||
@@ -110,7 +110,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
.build()?,
|
||||
|
||||
appservice: base(config)?
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.dns_resolver(resolver.dns.resolver.clone())
|
||||
.connect_timeout(Duration::from_secs(5))
|
||||
.read_timeout(Duration::from_secs(config.appservice_timeout))
|
||||
.timeout(Duration::from_secs(config.appservice_timeout))
|
||||
@@ -120,7 +120,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
.build()?,
|
||||
|
||||
pusher: base(config)?
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.dns_resolver(resolver.dns.resolver.clone())
|
||||
.connect_timeout(Duration::from_secs(config.pusher_conn_timeout))
|
||||
.timeout(Duration::from_secs(config.pusher_timeout))
|
||||
.pool_max_idle_per_host(1)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
use http::{HeaderValue, header::AUTHORIZATION};
|
||||
use ipaddress::IPAddress;
|
||||
use reqwest::{Client, Method, Request, Response, Url};
|
||||
use resolvematrix::server::Resolution;
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, ServerName, ServerSigningKeyId,
|
||||
api::{
|
||||
@@ -17,8 +18,6 @@
|
||||
serde::Base64,
|
||||
};
|
||||
|
||||
use crate::resolver::actual::ActualDest;
|
||||
|
||||
/// Sends a request to a federation server
|
||||
#[implement(super::Service)]
|
||||
#[tracing::instrument(skip_all, name = "request", level = "debug")]
|
||||
@@ -68,7 +67,12 @@ pub async fn execute_on<T>(
|
||||
return Err!(Request(Forbidden(debug_warn!("Federation with {dest} is not allowed."))));
|
||||
}
|
||||
|
||||
let actual = self.services.resolver.get_actual_dest(dest).await?;
|
||||
let actual = self
|
||||
.services
|
||||
.resolver
|
||||
.resolver
|
||||
.resolve_server(dest.as_str())
|
||||
.await?;
|
||||
let request = into_http_request::<T>(&actual, request)?;
|
||||
let request = self.prepare(dest, request)?;
|
||||
self.perform::<T>(dest, &actual, request, client).await
|
||||
@@ -78,7 +82,7 @@ pub async fn execute_on<T>(
|
||||
async fn perform<T>(
|
||||
&self,
|
||||
dest: &ServerName,
|
||||
actual: &ActualDest,
|
||||
actual: &Resolution,
|
||||
request: Request,
|
||||
client: &Client,
|
||||
) -> Result<T::IncomingResponse>
|
||||
@@ -125,7 +129,7 @@ fn validate_url(&self, url: &Url) -> Result<()> {
|
||||
async fn handle_response<T>(
|
||||
&self,
|
||||
dest: &ServerName,
|
||||
actual: &ActualDest,
|
||||
actual: &Resolution,
|
||||
method: &Method,
|
||||
url: &Url,
|
||||
response: Response,
|
||||
@@ -156,7 +160,7 @@ async fn handle_response<T>(
|
||||
|
||||
async fn into_http_response(
|
||||
dest: &ServerName,
|
||||
actual: &ActualDest,
|
||||
actual: &Resolution,
|
||||
method: &Method,
|
||||
url: &Url,
|
||||
mut response: Response,
|
||||
@@ -168,7 +172,7 @@ async fn into_http_response(
|
||||
request_url = %url,
|
||||
response_url = %response.url(),
|
||||
"Received response from {}",
|
||||
actual.string(),
|
||||
actual.base_url(),
|
||||
);
|
||||
|
||||
let mut http_response_builder = http::Response::builder()
|
||||
@@ -205,7 +209,7 @@ async fn into_http_response(
|
||||
}
|
||||
|
||||
fn handle_error(
|
||||
actual: &ActualDest,
|
||||
actual: &Resolution,
|
||||
method: &Method,
|
||||
url: &Url,
|
||||
mut e: reqwest::Error,
|
||||
@@ -300,7 +304,7 @@ fn sign_request(&self, http_request: &mut http::Request<Vec<u8>>, dest: &ServerN
|
||||
debug_assert!(authorization.is_none(), "Authorization header already present");
|
||||
}
|
||||
|
||||
fn into_http_request<T>(actual: &ActualDest, request: T) -> Result<http::Request<Vec<u8>>>
|
||||
fn into_http_request<T>(actual: &Resolution, request: T) -> Result<http::Request<Vec<u8>>>
|
||||
where
|
||||
T: OutgoingRequest + Send,
|
||||
{
|
||||
@@ -308,7 +312,7 @@ fn into_http_request<T>(actual: &ActualDest, request: T) -> Result<http::Request
|
||||
|
||||
let http_request = request
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
actual.string().as_str(),
|
||||
actual.base_url().as_str(),
|
||||
SendAccessToken::None,
|
||||
&VERSIONS,
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use askama::Template;
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{Result, info, utils::ReadyExt};
|
||||
use futures::StreamExt;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{UserId, events::room::message::RoomMessageEventContent};
|
||||
|
||||
use crate::{
|
||||
@@ -133,7 +133,7 @@ struct WelcomeMessage<'a> {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
self.services.admin.make_user_admin(user).await?;
|
||||
self.services.admin.make_user_admin(user).boxed().await?;
|
||||
|
||||
// Send the welcome message
|
||||
let welcome_message = WelcomeMessage {
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
extern crate conduwuit_core as conduwuit;
|
||||
extern crate conduwuit_database as database;
|
||||
|
||||
conduwuit_macros::introspect_crate! {}
|
||||
|
||||
mod manager;
|
||||
mod migrations;
|
||||
mod service;
|
||||
|
||||
@@ -100,7 +100,7 @@ pub async fn get_presence(&self, user_id: &UserId) -> Result<PresenceEvent> {
|
||||
/// Pings the presence of the given user in the given room, setting the
|
||||
/// specified state.
|
||||
pub async fn ping_presence(&self, user_id: &UserId, new_state: &PresenceState) -> Result<()> {
|
||||
const REFRESH_TIMEOUT: u64 = 60 * 1000;
|
||||
const REFRESH_TIMEOUT: u64 = 60 * 1000 * 4;
|
||||
|
||||
let last_presence = self.db.get_presence(user_id).await;
|
||||
let state_changed = match last_presence {
|
||||
|
||||
@@ -1,398 +0,0 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
net::{IpAddr, SocketAddr},
|
||||
};
|
||||
|
||||
use conduwuit::{Err, Result, debug, debug_info, err, error, trace};
|
||||
use futures::{FutureExt, TryFutureExt};
|
||||
use hickory_resolver::ResolveError;
|
||||
use ipaddress::IPAddress;
|
||||
use ruma::ServerName;
|
||||
|
||||
use super::{
|
||||
cache::{CachedDest, CachedOverride, MAX_IPS},
|
||||
fed::{FedDest, PortString, add_port_to_hostname, get_ip_with_port},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ActualDest {
|
||||
pub(crate) dest: FedDest,
|
||||
pub(crate) host: String,
|
||||
}
|
||||
|
||||
impl ActualDest {
|
||||
#[inline]
|
||||
pub(crate) fn string(&self) -> String { self.dest.https_string() }
|
||||
}
|
||||
|
||||
impl super::Service {
|
||||
#[tracing::instrument(skip_all, level = "debug", name = "resolve")]
|
||||
pub(crate) async fn get_actual_dest(&self, server_name: &ServerName) -> Result<ActualDest> {
|
||||
let (CachedDest { dest, host, .. }, _cached) =
|
||||
self.lookup_actual_dest(server_name).await?;
|
||||
|
||||
Ok(ActualDest { dest, host })
|
||||
}
|
||||
|
||||
pub(crate) async fn lookup_actual_dest(
|
||||
&self,
|
||||
server_name: &ServerName,
|
||||
) -> Result<(CachedDest, bool)> {
|
||||
if let Ok(result) = self.cache.get_destination(server_name).await {
|
||||
return Ok((result, true));
|
||||
}
|
||||
|
||||
let _dedup = self.resolving.lock(server_name.as_str());
|
||||
if let Ok(result) = self.cache.get_destination(server_name).await {
|
||||
return Ok((result, true));
|
||||
}
|
||||
|
||||
self.resolve_actual_dest(server_name, true)
|
||||
.inspect_ok(|result| self.cache.set_destination(server_name, result))
|
||||
.map_ok(|result| (result, false))
|
||||
.boxed()
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns: `actual_destination`, host header
|
||||
/// Implemented according to the specification at <https://matrix.org/docs/spec/server_server/r0.1.4#resolving-server-names>
|
||||
/// Numbers in comments below refer to bullet points in linked section of
|
||||
/// specification
|
||||
#[tracing::instrument(name = "actual", level = "debug", skip(self, cache))]
|
||||
pub async fn resolve_actual_dest(
|
||||
&self,
|
||||
dest: &ServerName,
|
||||
cache: bool,
|
||||
) -> Result<CachedDest> {
|
||||
self.validate_dest(dest)?;
|
||||
let mut host = dest.as_str().to_owned();
|
||||
let actual_dest = match get_ip_with_port(dest.as_str()) {
|
||||
| Some(host_port) => Self::actual_dest_1(host_port)?,
|
||||
| None =>
|
||||
if let Some(pos) = dest.as_str().find(':') {
|
||||
self.actual_dest_2(dest, cache, pos).await?
|
||||
} else {
|
||||
self.conditional_query_and_cache(dest.as_str(), 8448, true)
|
||||
.await?;
|
||||
self.services.server.check_running()?;
|
||||
match self.request_well_known(dest.as_str()).await? {
|
||||
| Some(delegated) =>
|
||||
self.actual_dest_3(&mut host, cache, delegated).await?,
|
||||
| _ => match self.query_srv_record(dest.as_str()).await? {
|
||||
| Some(overrider) =>
|
||||
self.actual_dest_4(&host, cache, overrider).await?,
|
||||
| _ => self.actual_dest_5(dest, cache).await?,
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Can't use get_ip_with_port here because we don't want to add a port
|
||||
// to an IP address if it wasn't specified
|
||||
let host = if let Ok(addr) = host.parse::<SocketAddr>() {
|
||||
FedDest::Literal(addr)
|
||||
} else if let Ok(addr) = host.parse::<IpAddr>() {
|
||||
FedDest::Named(addr.to_string(), FedDest::default_port())
|
||||
} else if let Some(pos) = host.find(':') {
|
||||
let (host, port) = host.split_at(pos);
|
||||
FedDest::Named(
|
||||
host.to_owned(),
|
||||
port.try_into().unwrap_or_else(|_| FedDest::default_port()),
|
||||
)
|
||||
} else {
|
||||
FedDest::Named(host, FedDest::default_port())
|
||||
};
|
||||
|
||||
debug!("Actual destination: {actual_dest:?} hostname: {host:?}");
|
||||
Ok(CachedDest {
|
||||
dest: actual_dest,
|
||||
host: host.uri_string(),
|
||||
expire: CachedDest::default_expire(),
|
||||
})
|
||||
}
|
||||
|
||||
fn actual_dest_1(host_port: FedDest) -> Result<FedDest> {
|
||||
debug!("1: IP literal with provided or default port");
|
||||
Ok(host_port)
|
||||
}
|
||||
|
||||
async fn actual_dest_2(&self, dest: &ServerName, cache: bool, pos: usize) -> Result<FedDest> {
|
||||
debug!("2: Hostname with included port");
|
||||
let (host, port) = dest.as_str().split_at(pos);
|
||||
self.conditional_query_and_cache(host, port.parse::<u16>().unwrap_or(8448), cache)
|
||||
.await?;
|
||||
|
||||
Ok(FedDest::Named(
|
||||
host.to_owned(),
|
||||
port.try_into().unwrap_or_else(|_| FedDest::default_port()),
|
||||
))
|
||||
}
|
||||
|
||||
async fn actual_dest_3(
|
||||
&self,
|
||||
host: &mut String,
|
||||
cache: bool,
|
||||
delegated: String,
|
||||
) -> Result<FedDest> {
|
||||
debug!("3: A .well-known file is available");
|
||||
*host = add_port_to_hostname(&delegated).uri_string();
|
||||
match get_ip_with_port(&delegated) {
|
||||
| Some(host_and_port) => Self::actual_dest_3_1(host_and_port),
|
||||
| None =>
|
||||
if let Some(pos) = delegated.find(':') {
|
||||
self.actual_dest_3_2(cache, delegated, pos).await
|
||||
} else {
|
||||
trace!("Delegated hostname has no port in this branch");
|
||||
match self.query_srv_record(&delegated).await? {
|
||||
| Some(overrider) =>
|
||||
self.actual_dest_3_3(cache, delegated, overrider).await,
|
||||
| _ => self.actual_dest_3_4(cache, delegated).await,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn actual_dest_3_1(host_and_port: FedDest) -> Result<FedDest> {
|
||||
debug!("3.1: IP literal in .well-known file");
|
||||
Ok(host_and_port)
|
||||
}
|
||||
|
||||
async fn actual_dest_3_2(
|
||||
&self,
|
||||
cache: bool,
|
||||
delegated: String,
|
||||
pos: usize,
|
||||
) -> Result<FedDest> {
|
||||
debug!("3.2: Hostname with port in .well-known file");
|
||||
let (host, port) = delegated.split_at(pos);
|
||||
self.conditional_query_and_cache(host, port.parse::<u16>().unwrap_or(8448), cache)
|
||||
.await?;
|
||||
|
||||
Ok(FedDest::Named(
|
||||
host.to_owned(),
|
||||
port.try_into().unwrap_or_else(|_| FedDest::default_port()),
|
||||
))
|
||||
}
|
||||
|
||||
async fn actual_dest_3_3(
|
||||
&self,
|
||||
cache: bool,
|
||||
delegated: String,
|
||||
overrider: FedDest,
|
||||
) -> Result<FedDest> {
|
||||
debug!("3.3: SRV lookup successful");
|
||||
let force_port = overrider.port();
|
||||
self.conditional_query_and_cache_override(
|
||||
&delegated,
|
||||
&overrider.hostname(),
|
||||
force_port.unwrap_or(8448),
|
||||
cache,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(port) = force_port {
|
||||
return Ok(FedDest::Named(
|
||||
delegated,
|
||||
format!(":{port}")
|
||||
.as_str()
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| FedDest::default_port()),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(add_port_to_hostname(&delegated))
|
||||
}
|
||||
|
||||
async fn actual_dest_3_4(&self, cache: bool, delegated: String) -> Result<FedDest> {
|
||||
debug!("3.4: No SRV records, just use the hostname from .well-known");
|
||||
self.conditional_query_and_cache(&delegated, 8448, cache)
|
||||
.await?;
|
||||
Ok(add_port_to_hostname(&delegated))
|
||||
}
|
||||
|
||||
async fn actual_dest_4(
|
||||
&self,
|
||||
host: &str,
|
||||
cache: bool,
|
||||
overrider: FedDest,
|
||||
) -> Result<FedDest> {
|
||||
debug!("4: No .well-known; SRV record found");
|
||||
let force_port = overrider.port();
|
||||
self.conditional_query_and_cache_override(
|
||||
host,
|
||||
&overrider.hostname(),
|
||||
force_port.unwrap_or(8448),
|
||||
cache,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(port) = force_port {
|
||||
let port = format!(":{port}");
|
||||
|
||||
return Ok(FedDest::Named(
|
||||
host.to_owned(),
|
||||
PortString::from(port.as_str()).unwrap_or_else(|_| FedDest::default_port()),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(add_port_to_hostname(host))
|
||||
}
|
||||
|
||||
async fn actual_dest_5(&self, dest: &ServerName, cache: bool) -> Result<FedDest> {
|
||||
debug!("5: No SRV record found");
|
||||
self.conditional_query_and_cache(dest.as_str(), 8448, cache)
|
||||
.await?;
|
||||
|
||||
Ok(add_port_to_hostname(dest.as_str()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn conditional_query_and_cache(
|
||||
&self,
|
||||
hostname: &str,
|
||||
port: u16,
|
||||
cache: bool,
|
||||
) -> Result {
|
||||
self.conditional_query_and_cache_override(hostname, hostname, port, cache)
|
||||
.await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn conditional_query_and_cache_override(
|
||||
&self,
|
||||
untername: &str,
|
||||
hostname: &str,
|
||||
port: u16,
|
||||
cache: bool,
|
||||
) -> Result {
|
||||
if !cache {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.cache.has_override(untername).await {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.query_and_cache_override(untername, hostname, port)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "ip", level = "debug", skip(self))]
|
||||
async fn query_and_cache_override(
|
||||
&self,
|
||||
untername: &'_ str,
|
||||
hostname: &'_ str,
|
||||
port: u16,
|
||||
) -> Result {
|
||||
self.services.server.check_running()?;
|
||||
|
||||
debug!("querying IP for {untername:?} ({hostname:?}:{port})");
|
||||
match self.resolver.resolver.lookup_ip(hostname.to_owned()).await {
|
||||
| Err(e) => Self::handle_resolve_error(&e, hostname),
|
||||
| Ok(override_ip) => {
|
||||
self.cache.set_override(untername, &CachedOverride {
|
||||
ips: override_ip.into_iter().take(MAX_IPS).collect(),
|
||||
port,
|
||||
expire: CachedOverride::default_expire(),
|
||||
overriding: (hostname != untername)
|
||||
.then_some(hostname.into())
|
||||
.inspect(|_| debug_info!("{untername:?} overridden by {hostname:?}")),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "srv", level = "debug", skip(self))]
|
||||
async fn query_srv_record(&self, hostname: &'_ str) -> Result<Option<FedDest>> {
|
||||
let hostnames =
|
||||
[format!("_matrix-fed._tcp.{hostname}."), format!("_matrix._tcp.{hostname}.")];
|
||||
|
||||
for hostname in hostnames {
|
||||
self.services.server.check_running()?;
|
||||
|
||||
debug!("querying SRV for {hostname:?}");
|
||||
let hostname = hostname.trim_end_matches('.');
|
||||
match self.resolver.resolver.srv_lookup(hostname).await {
|
||||
| Err(e) => Self::handle_resolve_error(&e, hostname)?,
|
||||
| Ok(result) => {
|
||||
return Ok(result.iter().next().map(|result| {
|
||||
FedDest::Named(
|
||||
result.target().to_string().trim_end_matches('.').to_owned(),
|
||||
format!(":{}", result.port())
|
||||
.as_str()
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| FedDest::default_port()),
|
||||
)
|
||||
}));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn handle_resolve_error(e: &ResolveError, host: &'_ str) -> Result<()> {
|
||||
use hickory_resolver::{ResolveErrorKind::Proto, proto::ProtoErrorKind};
|
||||
|
||||
match e.kind() {
|
||||
| Proto(e) => match e.kind() {
|
||||
| ProtoErrorKind::NoRecordsFound { .. } => {
|
||||
// Raise to debug_warn if we can find out the result wasn't from cache
|
||||
debug!(%host, "No DNS records found: {e}");
|
||||
Ok(())
|
||||
},
|
||||
| ProtoErrorKind::Timeout => {
|
||||
Err!(warn!(%host, "DNS {e}"))
|
||||
},
|
||||
| ProtoErrorKind::NoConnections => {
|
||||
error!(
|
||||
"Your DNS server is overloaded and has ran out of connections. It is \
|
||||
strongly recommended you remediate this issue to ensure proper \
|
||||
federation connectivity."
|
||||
);
|
||||
|
||||
Err!(error!(%host, "DNS error: {e}"))
|
||||
},
|
||||
| _ => Err!(error!(%host, "DNS error: {e}")),
|
||||
},
|
||||
| _ => Err!(error!(%host, "DNS error: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_dest(&self, dest: &ServerName) -> Result<()> {
|
||||
if dest == self.services.server.name && !self.services.server.config.federation_loopback {
|
||||
return Err!("Won't send federation request to ourselves");
|
||||
}
|
||||
|
||||
if dest.is_ip_literal() || IPAddress::is_valid(dest.host()) {
|
||||
self.validate_dest_ip_literal(dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_dest_ip_literal(&self, dest: &ServerName) -> Result<()> {
|
||||
trace!("Destination is an IP literal, checking against IP range denylist.",);
|
||||
debug_assert!(
|
||||
dest.is_ip_literal() || !IPAddress::is_valid(dest.host()),
|
||||
"Destination is not an IP literal."
|
||||
);
|
||||
let ip = IPAddress::parse(dest.host()).map_err(|e| {
|
||||
err!(BadServerResponse(debug_error!("Failed to parse IP literal from string: {e}")))
|
||||
})?;
|
||||
|
||||
self.validate_ip(&ip)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn validate_ip(&self, ip: &IPAddress) -> Result<()> {
|
||||
if !self.services.client.valid_cidr_range(ip) {
|
||||
return Err!(BadServerResponse("Not allowed to send requests to this IP"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Result,
|
||||
arrayvec::ArrayVec,
|
||||
at, err, implement,
|
||||
utils::{math::Expected, rand, stream::TryIgnore},
|
||||
utils::{math::Expected, stream::TryIgnore},
|
||||
};
|
||||
use database::{Cbor, Deserialized, Map};
|
||||
use futures::{Stream, StreamExt, future::join};
|
||||
@@ -127,11 +127,6 @@ impl CachedDest {
|
||||
#[must_use]
|
||||
pub fn valid(&self) -> bool { self.expire > SystemTime::now() }
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn default_expire() -> SystemTime {
|
||||
rand::time_from_now_secs(60 * 60 * 18..60 * 60 * 36)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn size(&self) -> usize {
|
||||
@@ -147,11 +142,6 @@ impl CachedOverride {
|
||||
#[must_use]
|
||||
pub fn valid(&self) -> bool { self.expire > SystemTime::now() }
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn default_expire() -> SystemTime {
|
||||
rand::time_from_now_secs(60 * 60 * 6..60 * 60 * 12)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn size(&self) -> usize { size_of_val(self) }
|
||||
|
||||
@@ -53,9 +53,9 @@ pub(super) fn build(server: &Arc<Server>, cache: Arc<Cache>) -> Result<Arc<Self>
|
||||
opts.cache_size = config.dns_cache_entries as usize;
|
||||
opts.preserve_intermediates = true;
|
||||
opts.negative_min_ttl = Some(Duration::from_secs(config.dns_min_ttl_nxdomain));
|
||||
opts.negative_max_ttl = Some(Duration::from_secs(60 * 60 * 24 * 30));
|
||||
opts.negative_max_ttl = Some(Duration::from_secs(60 * 60 * 24));
|
||||
opts.positive_min_ttl = Some(Duration::from_secs(config.dns_min_ttl));
|
||||
opts.positive_max_ttl = Some(Duration::from_secs(60 * 60 * 24 * 7));
|
||||
opts.positive_max_ttl = Some(Duration::from_secs(60 * 60 * 24));
|
||||
opts.timeout = Duration::from_secs(config.dns_timeout);
|
||||
opts.attempts = config.dns_attempts as usize;
|
||||
opts.try_tcp_on_error = config.dns_tcp_fallback;
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt,
|
||||
net::{IpAddr, SocketAddr},
|
||||
};
|
||||
use std::{fmt, net::SocketAddr};
|
||||
|
||||
use conduwuit::{arrayvec::ArrayString, utils::math::Expected};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub enum FedDest {
|
||||
Literal(SocketAddr),
|
||||
Named(String, PortString),
|
||||
Literal(SocketAddr), // "ip:port"
|
||||
Named(String, PortString), // ("hostname", ":port")
|
||||
}
|
||||
|
||||
/// numeric or service-name
|
||||
@@ -18,36 +14,7 @@ pub enum FedDest {
|
||||
|
||||
const DEFAULT_PORT: &str = ":8448";
|
||||
|
||||
pub(crate) fn get_ip_with_port(dest_str: &str) -> Option<FedDest> {
|
||||
if let Ok(dest) = dest_str.parse::<SocketAddr>() {
|
||||
Some(FedDest::Literal(dest))
|
||||
} else if let Ok(ip_addr) = dest_str.parse::<IpAddr>() {
|
||||
Some(FedDest::Literal(SocketAddr::new(ip_addr, 8448)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_port_to_hostname(dest: &str) -> FedDest {
|
||||
let (host, port) = match dest.find(':') {
|
||||
| None => (dest, DEFAULT_PORT),
|
||||
| Some(pos) => dest.split_at(pos),
|
||||
};
|
||||
|
||||
FedDest::Named(
|
||||
host.to_owned(),
|
||||
PortString::from(port).unwrap_or_else(|_| FedDest::default_port()),
|
||||
)
|
||||
}
|
||||
|
||||
impl FedDest {
|
||||
pub(crate) fn https_string(&self) -> String {
|
||||
match self {
|
||||
| Self::Literal(addr) => format!("https://{addr}"),
|
||||
| Self::Named(host, port) => format!("https://{host}{port}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn uri_string(&self) -> String {
|
||||
match self {
|
||||
| Self::Literal(addr) => addr.to_string(),
|
||||
@@ -55,23 +22,6 @@ pub(crate) fn uri_string(&self) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn hostname(&self) -> Cow<'_, str> {
|
||||
match &self {
|
||||
| Self::Literal(addr) => addr.ip().to_string().into(),
|
||||
| Self::Named(host, _) => host.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::string_slice)]
|
||||
pub(crate) fn port(&self) -> Option<u16> {
|
||||
match &self {
|
||||
| Self::Literal(addr) => Some(addr.port()),
|
||||
| Self::Named(_, port) => port[1..].parse().ok(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn default_port() -> PortString {
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
pub mod actual;
|
||||
pub mod cache;
|
||||
mod dns;
|
||||
pub mod fed;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod well_known;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{Result, Server, arrayvec::ArrayString, utils::MutexMap};
|
||||
use conduwuit::{Err, Result, implement};
|
||||
use ipaddress::IPAddress;
|
||||
use resolvematrix::server::MatrixResolver;
|
||||
|
||||
use self::{cache::Cache, dns::Resolver};
|
||||
use crate::{Dep, client};
|
||||
|
||||
pub struct Service {
|
||||
pub cache: Arc<Cache>,
|
||||
pub resolver: Arc<Resolver>,
|
||||
resolving: Resolving,
|
||||
pub resolver: MatrixResolver,
|
||||
pub dns: Dns,
|
||||
services: Services,
|
||||
}
|
||||
|
||||
struct Services {
|
||||
server: Arc<Server>,
|
||||
client: Dep<client::Service>,
|
||||
}
|
||||
|
||||
type Resolving = MutexMap<NameBuf, ()>;
|
||||
type NameBuf = ArrayString<256>;
|
||||
pub struct Dns {
|
||||
pub cache: Arc<Cache>,
|
||||
pub resolver: Arc<Resolver>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Service for Service {
|
||||
@@ -35,20 +33,31 @@ impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
let cache = Cache::new(&args);
|
||||
Ok(Arc::new(Self {
|
||||
cache: cache.clone(),
|
||||
resolver: Resolver::build(args.server, cache)?,
|
||||
resolving: MutexMap::new(),
|
||||
resolver: MatrixResolver::new()?,
|
||||
dns: Dns {
|
||||
cache: cache.clone(),
|
||||
resolver: Resolver::build(args.server, cache)?,
|
||||
},
|
||||
services: Services {
|
||||
server: args.server.clone(),
|
||||
client: args.depend::<client::Service>("client"),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
async fn clear_cache(&self) {
|
||||
self.resolver.clear_cache();
|
||||
self.cache.clear().await;
|
||||
// No ability to clean resolvematrix cache at the moment
|
||||
self.dns.resolver.clear_cache();
|
||||
self.dns.cache.clear().await;
|
||||
}
|
||||
|
||||
fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
|
||||
fn name(&self) -> &str { crate::service::make_name(module_path!()) }
|
||||
}
|
||||
|
||||
#[implement(Service)]
|
||||
pub fn validate_ip(&self, ip: &IPAddress) -> Result<()> {
|
||||
if !self.services.client.valid_cidr_range(ip) {
|
||||
return Err!(BadServerResponse("Not allowed to send requests to this IP"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
use super::fed::{FedDest, add_port_to_hostname, get_ip_with_port};
|
||||
|
||||
#[test]
|
||||
fn ips_get_default_ports() {
|
||||
assert_eq!(
|
||||
get_ip_with_port("1.1.1.1"),
|
||||
Some(FedDest::Literal("1.1.1.1:8448".parse().unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
get_ip_with_port("dead:beef::"),
|
||||
Some(FedDest::Literal("[dead:beef::]:8448".parse().unwrap()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ips_keep_custom_ports() {
|
||||
assert_eq!(
|
||||
get_ip_with_port("1.1.1.1:1234"),
|
||||
Some(FedDest::Literal("1.1.1.1:1234".parse().unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
get_ip_with_port("[dead::beef]:8933"),
|
||||
Some(FedDest::Literal("[dead::beef]:8933".parse().unwrap()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostnames_get_default_ports() {
|
||||
assert_eq!(
|
||||
add_port_to_hostname("example.com"),
|
||||
FedDest::Named(String::from("example.com"), ":8448".try_into().unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostnames_keep_custom_ports() {
|
||||
assert_eq!(
|
||||
add_port_to_hostname("example.com:1337"),
|
||||
FedDest::Named(String::from("example.com"), ":1337".try_into().unwrap())
|
||||
);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
use conduwuit::{
|
||||
Result, debug, debug_error, debug_info, implement, trace, utils::response::LimitReadExt,
|
||||
};
|
||||
|
||||
#[implement(super::Service)]
|
||||
#[tracing::instrument(name = "well-known", level = "debug", skip(self, dest))]
|
||||
pub(super) async fn request_well_known(&self, dest: &str) -> Result<Option<String>> {
|
||||
trace!("Requesting well known for {dest}");
|
||||
let response = self
|
||||
.services
|
||||
.client
|
||||
.well_known
|
||||
.get(format!("https://{dest}/.well-known/matrix/server"))
|
||||
.send()
|
||||
.await;
|
||||
|
||||
trace!("response: {response:?}");
|
||||
if let Err(e) = &response {
|
||||
debug!("error: {e:?}");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let response = response?;
|
||||
if !response.status().is_success() {
|
||||
debug!("response not 2XX");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Ok(text) = response.limit_read_text(8192).await else {
|
||||
debug!("failed to read well-known response (too large or non-text content)");
|
||||
return Ok(None);
|
||||
};
|
||||
trace!("response text: {text:?}");
|
||||
|
||||
let body: serde_json::Value = serde_json::from_str(&text).unwrap_or_default();
|
||||
|
||||
let m_server = body
|
||||
.get("m.server")
|
||||
.unwrap_or(&serde_json::Value::Null)
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
|
||||
if ruma::identifiers_validation::server_name::validate(m_server).is_err() {
|
||||
debug_error!("response content missing or invalid");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
debug_info!("{dest:?} found at {m_server:?}");
|
||||
Ok(Some(m_server.to_owned()))
|
||||
}
|
||||
@@ -80,7 +80,7 @@ pub(super) async fn fetch_and_handle_outliers<'a, Pdu, Events>(
|
||||
{
|
||||
// Exponential backoff
|
||||
const MIN_DURATION: u64 = 60 * 2;
|
||||
const MAX_DURATION: u64 = 60 * 60 * 8;
|
||||
const MAX_DURATION: u64 = 60 * 60;
|
||||
if continue_exponential_backoff_secs(
|
||||
MIN_DURATION,
|
||||
MAX_DURATION,
|
||||
|
||||
@@ -46,7 +46,7 @@ pub(super) async fn handle_prev_pdu<'a, Pdu>(
|
||||
{
|
||||
// Exponential backoff
|
||||
const MIN_DURATION: u64 = 5 * 60;
|
||||
const MAX_DURATION: u64 = 60 * 60 * 24;
|
||||
const MAX_DURATION: u64 = 60 * 60;
|
||||
if continue_exponential_backoff_secs(MIN_DURATION, MAX_DURATION, time.elapsed(), *tries) {
|
||||
debug!(
|
||||
?tries,
|
||||
|
||||
@@ -197,6 +197,15 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu<Pdu>(
|
||||
.await;
|
||||
extremities.push(incoming_pdu.event_id().to_owned());
|
||||
|
||||
if extremities.is_empty() {
|
||||
info!(
|
||||
"Retained zero extremities when upgrading outlier PDU to timeline PDU with {} \
|
||||
previous events, event id: {}",
|
||||
incoming_pdu.prev_events.len(),
|
||||
incoming_pdu.event_id
|
||||
);
|
||||
}
|
||||
|
||||
debug!(
|
||||
"Retained {} extremities checked against {} prev_events",
|
||||
extremities.len(),
|
||||
|
||||
@@ -56,36 +56,32 @@ pub fn pdu_fits(owned_obj: &mut CanonicalJsonObject) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pulls the room version ID out of the given (create) event.
|
||||
fn room_version_from_event(
|
||||
room_id: OwnedRoomId,
|
||||
event_type: &TimelineEventType,
|
||||
content: &RawValue,
|
||||
) -> Result<RoomVersionId> {
|
||||
if event_type == &TimelineEventType::RoomCreate {
|
||||
let content: RoomCreateEventContent = serde_json::from_str(content.get())?;
|
||||
Ok(content.room_version)
|
||||
} else {
|
||||
Err(Error::InconsistentRoomState(
|
||||
"non-create event for room of unknown version",
|
||||
room_id,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Creates an event, but does not hash or sign it.
|
||||
#[implement(super::Service)]
|
||||
pub async fn create_hash_and_sign_event(
|
||||
pub async fn create_event(
|
||||
&self,
|
||||
pdu_builder: PduBuilder,
|
||||
sender: &UserId,
|
||||
room_id: Option<&RoomId>,
|
||||
_mutex_lock: &RoomMutexGuard, /* Take mutex guard to make sure users get the room
|
||||
* state mutex */
|
||||
) -> Result<(PduEvent, CanonicalJsonObject)> {
|
||||
#[allow(clippy::boxed_local)]
|
||||
fn from_evt(
|
||||
room_id: OwnedRoomId,
|
||||
event_type: &TimelineEventType,
|
||||
content: &RawValue,
|
||||
) -> Result<RoomVersionId> {
|
||||
if event_type == &TimelineEventType::RoomCreate {
|
||||
let content: RoomCreateEventContent = serde_json::from_str(content.get())?;
|
||||
Ok(content.room_version)
|
||||
} else {
|
||||
Err(Error::InconsistentRoomState(
|
||||
"non-create event for room of unknown version",
|
||||
room_id,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if !self.services.globals.user_is_local(sender) {
|
||||
return Err!(Request(Forbidden("Sender must be a local user")));
|
||||
}
|
||||
|
||||
_mutex_lock: &RoomMutexGuard,
|
||||
) -> Result<(PduEvent, RoomVersionId)> {
|
||||
let PduBuilder {
|
||||
event_type,
|
||||
content,
|
||||
@@ -108,12 +104,16 @@ fn from_evt(
|
||||
.get_room_version(room_id)
|
||||
.await
|
||||
.or_else(|_| {
|
||||
from_evt(room_id.to_owned(), &event_type.clone(), &content.clone())
|
||||
room_version_from_event(
|
||||
room_id.to_owned(),
|
||||
&event_type.clone(),
|
||||
&content.clone(),
|
||||
)
|
||||
})?
|
||||
},
|
||||
| None => {
|
||||
trace!("No room ID, assuming room creation");
|
||||
from_evt(
|
||||
room_version_from_event(
|
||||
RoomId::new(self.services.globals.server_name()),
|
||||
&event_type.clone(),
|
||||
&content.clone(),
|
||||
@@ -186,7 +186,7 @@ fn from_evt(
|
||||
}
|
||||
}
|
||||
|
||||
let mut pdu = PduEvent {
|
||||
let pdu = PduEvent {
|
||||
event_id: ruma::event_id!("$thiswillbefilledinlater").into(),
|
||||
room_id: room_id.map(ToOwned::to_owned),
|
||||
sender: sender.to_owned(),
|
||||
@@ -259,19 +259,29 @@ fn from_evt(
|
||||
pdu.event_id,
|
||||
pdu.room_id.as_ref().map_or("None", |id| id.as_str())
|
||||
);
|
||||
Ok((pdu, room_version_id))
|
||||
}
|
||||
|
||||
#[implement(super::Service)]
|
||||
pub async fn create_hash_and_sign_event(
|
||||
&self,
|
||||
pdu_builder: PduBuilder,
|
||||
sender: &UserId,
|
||||
room_id: Option<&RoomId>,
|
||||
mutex_lock: &RoomMutexGuard, /* Take mutex guard to make sure users get the room
|
||||
* state mutex */
|
||||
) -> Result<(PduEvent, CanonicalJsonObject)> {
|
||||
if !self.services.globals.user_is_local(sender) {
|
||||
return Err!(Request(Forbidden("Sender must be a local user")));
|
||||
}
|
||||
let (mut pdu, room_version_id) = self
|
||||
.create_event(pdu_builder, sender, room_id, mutex_lock)
|
||||
.await?;
|
||||
// Hash and sign
|
||||
let mut pdu_json = utils::to_canonical_object(&pdu).map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Failed to convert PDU to canonical JSON: {e}"))))
|
||||
})?;
|
||||
|
||||
// room v3 and above removed the "event_id" field from remote PDU format
|
||||
match room_version_id {
|
||||
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
| _ => {
|
||||
pdu_json.remove("event_id");
|
||||
},
|
||||
}
|
||||
pdu_json.remove("event_id");
|
||||
|
||||
trace!("hashing and signing event {}", pdu.event_id);
|
||||
if let Err(e) = self
|
||||
|
||||
@@ -127,3 +127,63 @@ pub async fn get_token_shortstatehash(
|
||||
.await
|
||||
.deserialized()
|
||||
}
|
||||
|
||||
/// Count how many sync tokens exist for a room without deleting them
|
||||
///
|
||||
/// This is useful for dry runs to see how many tokens would be deleted
|
||||
#[implement(Service)]
|
||||
pub async fn count_room_tokens(&self, room_id: &RoomId) -> Result<usize> {
|
||||
use futures::TryStreamExt;
|
||||
|
||||
let shortroomid = self.services.short.get_shortroomid(room_id).await?;
|
||||
|
||||
// Create a prefix to search by - all entries for this room will start with its
|
||||
// short ID
|
||||
let prefix = &[shortroomid];
|
||||
|
||||
// Collect all keys into a Vec and count them
|
||||
let keys = self
|
||||
.db
|
||||
.roomsynctoken_shortstatehash
|
||||
.keys_prefix_raw(prefix)
|
||||
.map_ok(|_| ()) // We only need to count, not store the keys
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
|
||||
Ok(keys.len())
|
||||
}
|
||||
|
||||
/// Delete all sync tokens associated with a room
|
||||
///
|
||||
/// This helps clean up the database as these tokens are never otherwise removed
|
||||
#[implement(Service)]
|
||||
pub async fn delete_room_tokens(&self, room_id: &RoomId) -> Result<usize> {
|
||||
use futures::TryStreamExt;
|
||||
|
||||
let shortroomid = self.services.short.get_shortroomid(room_id).await?;
|
||||
|
||||
// Create a prefix to search by - all entries for this room will start with its
|
||||
// short ID
|
||||
let prefix = &[shortroomid];
|
||||
|
||||
// Collect all keys into a Vec first, then delete them
|
||||
let keys = self
|
||||
.db
|
||||
.roomsynctoken_shortstatehash
|
||||
.keys_prefix_raw(prefix)
|
||||
.map_ok(|key| {
|
||||
// Clone the key since we can't store references in the Vec
|
||||
Vec::from(key)
|
||||
})
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
|
||||
// Delete each key individually
|
||||
for key in &keys {
|
||||
self.db.roomsynctoken_shortstatehash.del(key);
|
||||
}
|
||||
|
||||
let count = keys.len();
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user