Compare commits
242 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce066f1a5a | |||
| fe0969e7ca | |||
| 2e3e14b045 | |||
| f51d4237c7 | |||
| d8a87258d6 | |||
| 3da2242730 | |||
| 5add9a8c34 | |||
| 5b1642f641 | |||
| eb94aa7079 | |||
| 24884c3b9b | |||
| a0a002b17f | |||
| f632b06e6d | |||
| 1f88866612 | |||
| ccef1a4c8b | |||
| 5c258f41c8 | |||
| 15126ee1b2 | |||
| e37ac56dba | |||
| a74613be96 | |||
| a95ded167f | |||
| 95121b6e05 | |||
| e6884ef13a | |||
| 49033db2b8 | |||
| ddc8c3b46c | |||
| ea95627dce | |||
| c3e751542d | |||
| 4b765aa17c | |||
| 954cfc6bb7 | |||
| 2f85a5c1ac | |||
| 7e50db4193 | |||
| 7a3cc3941e | |||
| ca82b59c6f | |||
| 3b5607ecdc | |||
| 96f6a75bc8 | |||
| 68f42baf73 | |||
| 271959ee27 | |||
| c423a83656 | |||
| 4e975887cf | |||
| 8bb69eb81d | |||
| 2100618d47 | |||
| 936d2915e2 | |||
| f014231644 | |||
| 7d487d53d8 | |||
| d7d874f88d | |||
| ee864bcd9e | |||
| 111cbea6fc | |||
| 85f734ec74 | |||
| 2468e0c3de | |||
| f841c2356d | |||
| c64adbec0e | |||
| 4458efa2b2 | |||
| 5c0bf29122 | |||
| 91b49a7786 | |||
| 5a17fbccf5 | |||
| 5ed95ea357 | |||
| ccfa939bd3 | |||
| 263e338088 | |||
| 2fb43dd38d | |||
| 59efabbbc2 | |||
| 010e4ee35a | |||
| 992c0a1e58 | |||
| 9b20c6918f | |||
| 3dc91525ce | |||
| a88f913a17 | |||
| 4343218957 | |||
| b116984e46 | |||
| b0ac5255c8 | |||
| 29fc5b9b52 | |||
| 3ccd9ea326 | |||
| 8a2ae401df | |||
| f465d77ad3 | |||
| 2fd6f6b0ff | |||
| 8b6018d77d | |||
| 463f1a1287 | |||
| 04fc87ad07 | |||
| dc573f4562 | |||
| 74826dcb94 | |||
| b20c4e0539 | |||
| fb8a2846df | |||
| 04971d0430 | |||
| f098532b09 | |||
| 60927c1c72 | |||
| 03296412ab | |||
| 934ab6a4fe | |||
| 1378399f9d | |||
| 09680f653f | |||
| 39f19c483a | |||
| 39c0f4ea3d | |||
| 405167fc3f | |||
| b13ea4ff45 | |||
| 100fc5e0f7 | |||
| 96f9d64111 | |||
| 30ad5da5f1 | |||
| ce0ca96df4 | |||
| 4851ad26e2 | |||
| 328759a60f | |||
| 463fa4fd53 | |||
| db494e0d68 | |||
| 4788040458 | |||
| 06531993f6 | |||
| c29197b3f4 | |||
| 739eab46d5 | |||
| 923a98eb66 | |||
| 4430e4dee0 | |||
| d67f19a55d | |||
| b903b46d16 | |||
| 167559bb27 | |||
| 838e4b9d8d | |||
| 038b71fc9d | |||
| 720fbd09c2 | |||
| c42cb90dd3 | |||
| 5950355348 | |||
| f79bd2ac72 | |||
| 80ec0e31b1 | |||
| bda44b16b1 | |||
| e2280aa1a5 | |||
| bdf2de076a | |||
| 1797fec3c9 | |||
| 188fa5a073 | |||
| f0c63c539b | |||
| 649e9da1f8 | |||
| df28359a19 | |||
| 9370e93a8d | |||
| bdd5845490 | |||
| bacffd6174 | |||
| a1bfd7a018 | |||
| 7009f56a7a | |||
| 2c0bfac43e | |||
| fcb6c8a113 | |||
| 1ab77aeb91 | |||
| 3d73b53136 | |||
| 101fdbc9b1 | |||
| e53c2fbc5a | |||
| 95006f7e46 | |||
| cce270d938 | |||
| 5ec49b3f62 | |||
| e4dc4a1ba5 | |||
| d2fb6d04c9 | |||
| 05efd9b044 | |||
| b3f2288d07 | |||
| 084751ae38 | |||
| aa7a310200 | |||
| 38c989a07e | |||
| 5be679e17b | |||
| b8baa1223d | |||
| b87f1649d4 | |||
| 4a6f089b23 | |||
| 4600c7f32d | |||
| 4cc92dd175 | |||
| 93ec4e579b | |||
| c111d2e395 | |||
| 03d890cd49 | |||
| a35b6cbfdd | |||
| 5570220c89 | |||
| 899b79873e | |||
| 57969f9480 | |||
| d88ab37120 | |||
| bc58e5002d | |||
| 160f48043e | |||
| 0023b09f5b | |||
| db3c718ddc | |||
| e73aa2aa21 | |||
| 61f2a3c68b | |||
| 0627b46f40 | |||
| f10f5319db | |||
| 16e76d45cb | |||
| 2a304c2b6c | |||
| 271f720286 | |||
| 2d251eb19c | |||
| 01b2928d55 | |||
| 50c2d2b801 | |||
| 5dcdafe207 | |||
| c62d653989 | |||
| 454dd43d4c | |||
| 8077e910f6 | |||
| b5d4a1c1b0 | |||
| aa9540af21 | |||
| 080975ab0e | |||
| f129d90900 | |||
| 53223a4d5f | |||
| 05befa4ba2 | |||
| a8e690f22b | |||
| 192c1e08da | |||
| 0fa6976d86 | |||
| 93e7cf461d | |||
| d036d8adcb | |||
| 7a4bbe2ff6 | |||
| 438911c18d | |||
| bd71435a22 | |||
| 53fa7c3729 | |||
| d2facaee0b | |||
| 0dae9280d9 | |||
| 35336eb686 | |||
| efea13a675 | |||
| b5ee15a216 | |||
| 0873e18e14 | |||
| 6abc4ad798 | |||
| 373991a8d6 | |||
| 68ad351f84 | |||
| 83e853e7a3 | |||
| 1c453b1b55 | |||
| 60141950f7 | |||
| 391bfd986e | |||
| da03de1d32 | |||
| e54f4d4397 | |||
| daa5c34ea3 | |||
| 3259ea08b5 | |||
| 158de10fe6 | |||
| 4718387dbe | |||
| a43c78e55f | |||
| 7ba0777bd3 | |||
| 59d86d3641 | |||
| 51df946911 | |||
| 23a9055199 | |||
| 100c6f572b | |||
| eb5556e74e | |||
| 8296e0ed67 | |||
| 8e3be6feb0 | |||
| 59c4062305 | |||
| 113a27c1d5 | |||
| 5af880e4f4 | |||
| 56a1b0e761 | |||
| 5722c4ae39 | |||
| dfd13780df | |||
| dcd7422c45 | |||
| 7397064edd | |||
| 52a561ff9e | |||
| 5e72d36800 | |||
| 611f09829e | |||
| a388c2e06e | |||
| 24b37e03a0 | |||
| a309ef55c9 | |||
| c1c084dda1 | |||
| 72d9e8ed2b | |||
| a3638dbb15 | |||
| 5254eb4f72 | |||
| b31e81a469 | |||
| 0e580292a6 | |||
| 38a24e0170 | |||
| 8350aced39 | |||
| 04e3de08eb | |||
| 2bc53139fa | |||
| 8691141237 |
+262
-25
@@ -27,6 +27,16 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
# sccache only on main repo
|
||||||
|
SCCACHE_GHA_ENABLED: "${{ (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'true' || 'false' }}"
|
||||||
|
RUSTC_WRAPPER: "${{ (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'sccache' || '' }}"
|
||||||
|
SCCACHE_BUCKET: "${{ (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'sccache' || '' }}"
|
||||||
|
SCCACHE_S3_USE_SSL: ${{ vars.SCCACHE_S3_USE_SSL }}
|
||||||
|
SCCACHE_REGION: ${{ vars.SCCACHE_REGION }}
|
||||||
|
SCCACHE_ENDPOINT: ${{ vars.SCCACHE_ENDPOINT }}
|
||||||
|
SCCACHE_CACHE_MULTIARCH: ${{ vars.SCCACHE_CACHE_MULTIARCH }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
# Required to make some things output color
|
# Required to make some things output color
|
||||||
TERM: ansi
|
TERM: ansi
|
||||||
# Publishing to my nix binary cache
|
# Publishing to my nix binary cache
|
||||||
@@ -38,8 +48,11 @@ env:
|
|||||||
# Custom nix binary cache if fork is being used
|
# Custom nix binary cache if fork is being used
|
||||||
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
||||||
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
||||||
# Get error output from nix that we can actually use
|
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
|
||||||
NIX_CONFIG: show-trace = true
|
NIX_CONFIG: |
|
||||||
|
show-trace = true
|
||||||
|
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||||
|
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
packages: write
|
packages: write
|
||||||
@@ -49,6 +62,8 @@ jobs:
|
|||||||
tests:
|
tests:
|
||||||
name: Test
|
name: Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
CARGO_PROFILE: "test"
|
||||||
steps:
|
steps:
|
||||||
- name: Free Disk Space (Ubuntu)
|
- name: Free Disk Space (Ubuntu)
|
||||||
uses: jlumbroso/free-disk-space@main
|
uses: jlumbroso/free-disk-space@main
|
||||||
@@ -57,7 +72,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Tag comparison check
|
- name: Tag comparison check
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: ${{ startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') }}
|
||||||
run: |
|
run: |
|
||||||
# Tag mismatch with latest repo tag check to prevent potential downgrades
|
# Tag mismatch with latest repo tag check to prevent potential downgrades
|
||||||
LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
|
LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||||
@@ -115,29 +130,48 @@ jobs:
|
|||||||
- name: Prepare build environment
|
- name: Prepare build environment
|
||||||
run: |
|
run: |
|
||||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||||
direnv allow
|
direnv allow
|
||||||
nix develop .#all-features --command true
|
nix develop .#all-features --command true
|
||||||
|
|
||||||
- name: Cache CI dependencies
|
- name: Cache CI dependencies
|
||||||
run: |
|
run: |
|
||||||
|
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
|
||||||
|
ATTEMPTS=3
|
||||||
|
SUCCESS=false
|
||||||
|
while (( ATTEMPTS-- > 0 ))
|
||||||
|
do
|
||||||
bin/nix-build-and-cache ci
|
bin/nix-build-and-cache ci
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
SUCCESS=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
sleep 3
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $SUCCESS == "false" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# use sccache for Rust
|
||||||
|
- name: Run sccache-cache
|
||||||
|
if: (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]')
|
||||||
|
uses: mozilla-actions/sccache-action@main
|
||||||
|
|
||||||
|
# use rust-cache
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
cache-all-crates: "true"
|
||||||
|
|
||||||
- name: Run CI tests
|
- name: Run CI tests
|
||||||
run: |
|
run: |
|
||||||
direnv exec . engage > >(tee -a test_output.log)
|
direnv exec . engage > >(tee -a test_output.log)
|
||||||
|
|
||||||
- name: Sync Complement repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: 'matrix-org/complement'
|
|
||||||
path: complement_src
|
|
||||||
|
|
||||||
- name: Run Complement tests
|
- name: Run Complement tests
|
||||||
run: |
|
run: |
|
||||||
direnv exec . bin/complement 'complement_src' 'complement_test_logs.jsonl' 'complement_test_results.jsonl'
|
# the nix devshell sets $COMPLEMENT_SRC, so "/dev/null" is no-op
|
||||||
|
direnv exec . bin/complement "/dev/null" complement_test_logs.jsonl complement_test_results.jsonl > >(tee -a test_output.log)
|
||||||
cp -v -f result complement_oci_image.tar.gz
|
cp -v -f result complement_oci_image.tar.gz
|
||||||
|
|
||||||
- name: Upload Complement OCI image
|
- name: Upload Complement OCI image
|
||||||
@@ -163,11 +197,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Diff Complement results with checked-in repo results
|
- name: Diff Complement results with checked-in repo results
|
||||||
run: |
|
run: |
|
||||||
diff -u --color=always tests/test_results/complement/test_results.jsonl complement_test_results.jsonl > >(tee -a complement_test_output.log)
|
diff -u --color=always tests/test_results/complement/test_results.jsonl complement_test_results.jsonl > >(tee -a complement_diff_output.log)
|
||||||
echo '# Complement diff results' >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo '```diff' >> $GITHUB_STEP_SUMMARY
|
|
||||||
tail -n 100 complement_test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
- name: Update Job Summary
|
- name: Update Job Summary
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
@@ -175,11 +205,21 @@ jobs:
|
|||||||
if [ ${{ job.status }} == 'success' ]; then
|
if [ ${{ job.status }} == 'success' ]; then
|
||||||
echo '# ✅ completed suwuccessfully' >> $GITHUB_STEP_SUMMARY
|
echo '# ✅ completed suwuccessfully' >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
else
|
||||||
|
echo '# CI failure' >> $GITHUB_STEP_SUMMARY
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
tail -n 40 test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
tail -n 40 test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
echo '# Complement diff results' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```diff' >> $GITHUB_STEP_SUMMARY
|
||||||
|
tail -n 100 complement_diff_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Run cargo clean test artifacts
|
||||||
|
run: |
|
||||||
|
cargo clean --profile test
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -190,6 +230,9 @@ jobs:
|
|||||||
- target: aarch64-unknown-linux-musl
|
- target: aarch64-unknown-linux-musl
|
||||||
- target: x86_64-unknown-linux-musl
|
- target: x86_64-unknown-linux-musl
|
||||||
steps:
|
steps:
|
||||||
|
- name: Free Disk Space (Ubuntu)
|
||||||
|
uses: jlumbroso/free-disk-space@main
|
||||||
|
|
||||||
- name: Sync repository
|
- name: Sync repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -240,16 +283,43 @@ jobs:
|
|||||||
- name: Prepare build environment
|
- name: Prepare build environment
|
||||||
run: |
|
run: |
|
||||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||||
direnv allow
|
direnv allow
|
||||||
nix develop .#all-features --command true
|
nix develop .#all-features --command true
|
||||||
|
|
||||||
|
# use sccache for Rust
|
||||||
|
- name: Run sccache-cache
|
||||||
|
if: (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]')
|
||||||
|
uses: mozilla-actions/sccache-action@main
|
||||||
|
|
||||||
|
# use rust-cache
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
cache-all-crates: "true"
|
||||||
|
|
||||||
- name: Build static ${{ matrix.target }}
|
- name: Build static ${{ matrix.target }}
|
||||||
run: |
|
run: |
|
||||||
CARGO_DEB_TARGET_TUPLE=$(echo ${{ matrix.target }} | grep -o -E '^([^-]*-){3}[^-]*')
|
CARGO_DEB_TARGET_TUPLE=$(echo ${{ matrix.target }} | grep -o -E '^([^-]*-){3}[^-]*')
|
||||||
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
|
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
|
||||||
|
|
||||||
bin/nix-build-and-cache just .#static-${{ matrix.target }}
|
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
|
||||||
|
ATTEMPTS=3
|
||||||
|
SUCCESS=false
|
||||||
|
while (( ATTEMPTS-- > 0 ))
|
||||||
|
do
|
||||||
|
bin/nix-build-and-cache just .#static-${{ matrix.target }}-all-features
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
SUCCESS=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
sleep 3
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $SUCCESS == "false" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -v -p target/release/
|
mkdir -v -p target/release/
|
||||||
mkdir -v -p target/$CARGO_DEB_TARGET_TUPLE/release/
|
mkdir -v -p target/$CARGO_DEB_TARGET_TUPLE/release/
|
||||||
cp -v -f result/bin/conduit target/release/conduwuit
|
cp -v -f result/bin/conduit target/release/conduwuit
|
||||||
@@ -259,6 +329,66 @@ jobs:
|
|||||||
mv -v target/release/conduwuit static-${{ matrix.target }}
|
mv -v target/release/conduwuit static-${{ matrix.target }}
|
||||||
mv -v target/release/${{ matrix.target }}.deb ${{ matrix.target }}.deb
|
mv -v target/release/${{ matrix.target }}.deb ${{ matrix.target }}.deb
|
||||||
|
|
||||||
|
# quick smoke test of the x86_64 static release binary
|
||||||
|
- name: Run x86_64 static release binary
|
||||||
|
run: |
|
||||||
|
# GH actions default runners are x86_64 only
|
||||||
|
if file result/bin/conduit | grep x86-64; then
|
||||||
|
result/bin/conduit --version
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build static debug ${{ matrix.target }}
|
||||||
|
run: |
|
||||||
|
CARGO_DEB_TARGET_TUPLE=$(echo ${{ matrix.target }} | grep -o -E '^([^-]*-){3}[^-]*')
|
||||||
|
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
|
||||||
|
|
||||||
|
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
|
||||||
|
ATTEMPTS=3
|
||||||
|
SUCCESS=false
|
||||||
|
while (( ATTEMPTS-- > 0 ))
|
||||||
|
do
|
||||||
|
bin/nix-build-and-cache just .#static-${{ matrix.target }}-all-features-debug
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
SUCCESS=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
sleep 3
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $SUCCESS == "false" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# > warning: dev profile is not supported and will be a hard error in the future. cargo-deb is for making releases, and it doesn't make sense to use it with dev profiles.
|
||||||
|
# so we need to coerce cargo-deb into thinking this is a release binary
|
||||||
|
mkdir -v -p target/release/
|
||||||
|
mkdir -v -p target/$CARGO_DEB_TARGET_TUPLE/release/
|
||||||
|
cp -v -f result/bin/conduit target/release/conduwuit
|
||||||
|
cp -v -f result/bin/conduit target/$CARGO_DEB_TARGET_TUPLE/release/conduwuit
|
||||||
|
# -p conduit is the main crate name
|
||||||
|
direnv exec . cargo deb --verbose --no-build --no-strip -p conduit --target=$CARGO_DEB_TARGET_TUPLE --output target/release/${{ matrix.target }}-debug.deb
|
||||||
|
mv -v target/release/conduwuit static-${{ matrix.target }}-debug
|
||||||
|
mv -v target/release/${{ matrix.target }}-debug.deb ${{ matrix.target }}-debug.deb
|
||||||
|
|
||||||
|
# quick smoke test of the x86_64 static debug binary
|
||||||
|
- name: Run x86_64 static debug binary
|
||||||
|
run: |
|
||||||
|
# GH actions default runners are x86_64 only
|
||||||
|
if file result/bin/conduit | grep x86-64; then
|
||||||
|
result/bin/conduit --version
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check validity of produced deb package, invalid debs will error on these commands
|
||||||
|
- name: Validate produced deb package
|
||||||
|
run: |
|
||||||
|
# List contents
|
||||||
|
dpkg-deb --contents ${{ matrix.target }}.deb
|
||||||
|
dpkg-deb --contents ${{ matrix.target }}-debug.deb
|
||||||
|
# List info
|
||||||
|
dpkg-deb --info ${{ matrix.target }}.deb
|
||||||
|
dpkg-deb --info ${{ matrix.target }}-debug.deb
|
||||||
|
|
||||||
- name: Upload static-${{ matrix.target }}
|
- name: Upload static-${{ matrix.target }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -274,11 +404,65 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
compression-level: 0
|
compression-level: 0
|
||||||
|
|
||||||
|
- name: Upload static-${{ matrix.target }}-debug
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: static-${{ matrix.target }}-debug
|
||||||
|
path: static-${{ matrix.target }}-debug
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Upload deb ${{ matrix.target }}-debug
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: deb-${{ matrix.target }}-debug
|
||||||
|
path: ${{ matrix.target }}-debug.deb
|
||||||
|
if-no-files-found: error
|
||||||
|
compression-level: 0
|
||||||
|
|
||||||
- name: Build OCI image ${{ matrix.target }}
|
- name: Build OCI image ${{ matrix.target }}
|
||||||
run: |
|
run: |
|
||||||
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}
|
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
|
||||||
|
ATTEMPTS=3
|
||||||
|
SUCCESS=false
|
||||||
|
while (( ATTEMPTS-- > 0 ))
|
||||||
|
do
|
||||||
|
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}-all-features
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
SUCCESS=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
sleep 3
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $SUCCESS == "false" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
cp -v -f result oci-image-${{ matrix.target }}.tar.gz
|
cp -v -f result oci-image-${{ matrix.target }}.tar.gz
|
||||||
|
|
||||||
|
- name: Build debug OCI image ${{ matrix.target }}
|
||||||
|
run: |
|
||||||
|
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
|
||||||
|
ATTEMPTS=3
|
||||||
|
SUCCESS=false
|
||||||
|
while (( ATTEMPTS-- > 0 ))
|
||||||
|
do
|
||||||
|
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}-all-features-debug
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
SUCCESS=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
sleep 3
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $SUCCESS == "false" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp -v -f result oci-image-${{ matrix.target }}-debug.tar.gz
|
||||||
|
|
||||||
- name: Upload OCI image ${{ matrix.target }}
|
- name: Upload OCI image ${{ matrix.target }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -287,6 +471,14 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
compression-level: 0
|
compression-level: 0
|
||||||
|
|
||||||
|
- name: Upload OCI image ${{ matrix.target }}-debug
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: oci-image-${{ matrix.target }}-debug
|
||||||
|
path: oci-image-${{ matrix.target }}-debug.tar.gz
|
||||||
|
if-no-files-found: error
|
||||||
|
compression-level: 0
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
name: Docker publish
|
name: Docker publish
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -296,15 +488,15 @@ jobs:
|
|||||||
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||||
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||||
DOCKER_TAG: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
DOCKER_TAG: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||||
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||||
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||||
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||||
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||||
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||||
GLCR_ARM64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
GLCR_ARM64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||||
GLCR_AMD64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
GLCR_AMD64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||||
GLCR_TAG: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
GLCR_TAG: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||||
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||||
|
|
||||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
|
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
|
||||||
@@ -337,8 +529,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Move OCI images into position
|
- name: Move OCI images into position
|
||||||
run: |
|
run: |
|
||||||
mv -v oci-image-x86_64-*/*.tar.gz oci-image-amd64.tar.gz
|
mv -v oci-image-x86_64-unknown-linux-musl/*.tar.gz oci-image-amd64.tar.gz
|
||||||
mv -v oci-image-aarch64-*/*.tar.gz oci-image-arm64v8.tar.gz
|
mv -v oci-image-aarch64-unknown-linux-musl/*.tar.gz oci-image-arm64v8.tar.gz
|
||||||
|
mv -v oci-image-x86_64-unknown-linux-musl-debug/*.tar.gz oci-image-amd64-debug.tar.gz
|
||||||
|
mv -v oci-image-aarch64-unknown-linux-musl-debug/*.tar.gz oci-image-arm64v8-debug.tar.gz
|
||||||
|
|
||||||
- name: Load and push amd64 image
|
- name: Load and push amd64 image
|
||||||
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||||
@@ -362,6 +556,28 @@ jobs:
|
|||||||
docker push ${{ env.GHCR_ARM64 }}
|
docker push ${{ env.GHCR_ARM64 }}
|
||||||
docker push ${{ env.GLCR_ARM64 }}
|
docker push ${{ env.GLCR_ARM64 }}
|
||||||
|
|
||||||
|
- name: Load and push amd64 debug image
|
||||||
|
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||||
|
run: |
|
||||||
|
docker load -i oci-image-amd64-debug.tar.gz
|
||||||
|
docker tag $(docker images -q conduit:main) ${{ env.DOCKER_AMD64 }}-debug
|
||||||
|
docker tag $(docker images -q conduit:main) ${{ env.GHCR_AMD64 }}-debug
|
||||||
|
docker tag $(docker images -q conduit:main) ${{ env.GLCR_AMD64 }}-debug
|
||||||
|
docker push ${{ env.DOCKER_AMD64 }}-debug
|
||||||
|
docker push ${{ env.GHCR_AMD64 }}-debug
|
||||||
|
docker push ${{ env.GLCR_AMD64 }}-debug
|
||||||
|
|
||||||
|
- name: Load and push arm64 debug image
|
||||||
|
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||||
|
run: |
|
||||||
|
docker load -i oci-image-arm64v8-debug.tar.gz
|
||||||
|
docker tag $(docker images -q conduit:main) ${{ env.DOCKER_ARM64 }}-debug
|
||||||
|
docker tag $(docker images -q conduit:main) ${{ env.GHCR_ARM64 }}-debug
|
||||||
|
docker tag $(docker images -q conduit:main) ${{ env.GLCR_ARM64 }}-debug
|
||||||
|
docker push ${{ env.DOCKER_ARM64 }}-debug
|
||||||
|
docker push ${{ env.GHCR_ARM64 }}-debug
|
||||||
|
docker push ${{ env.GLCR_ARM64 }}-debug
|
||||||
|
|
||||||
- name: Create Docker combined manifests
|
- name: Create Docker combined manifests
|
||||||
run: |
|
run: |
|
||||||
# Dockerhub Container Registry
|
# Dockerhub Container Registry
|
||||||
@@ -371,9 +587,21 @@ jobs:
|
|||||||
docker manifest create ${{ env.GHCR_TAG }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
|
docker manifest create ${{ env.GHCR_TAG }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
|
||||||
docker manifest create ${{ env.GHCR_BRANCH }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
|
docker manifest create ${{ env.GHCR_BRANCH }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
|
||||||
# GitLab Container Registry
|
# GitLab Container Registry
|
||||||
docker manifest create ${{ env.GLCR_TAG }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GCCR_AMD64 }}
|
docker manifest create ${{ env.GLCR_TAG }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GLCR_AMD64 }}
|
||||||
docker manifest create ${{ env.GLCR_BRANCH }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GLCR_AMD64 }}
|
docker manifest create ${{ env.GLCR_BRANCH }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GLCR_AMD64 }}
|
||||||
|
|
||||||
|
- name: Create Docker combined debug manifests
|
||||||
|
run: |
|
||||||
|
# Dockerhub Container Registry
|
||||||
|
docker manifest create ${{ env.DOCKER_TAG }}-debug --amend ${{ env.DOCKER_ARM64 }}-debug --amend ${{ env.DOCKER_AMD64 }}-debug
|
||||||
|
docker manifest create ${{ env.DOCKER_BRANCH }}-debug --amend ${{ env.DOCKER_ARM64 }}-debug --amend ${{ env.DOCKER_AMD64 }}-debug
|
||||||
|
# GitHub Container Registry
|
||||||
|
docker manifest create ${{ env.GHCR_TAG }}-debug --amend ${{ env.GHCR_ARM64 }}-debug --amend ${{ env.GHCR_AMD64 }}-debug
|
||||||
|
docker manifest create ${{ env.GHCR_BRANCH }}-debug --amend ${{ env.GHCR_ARM64 }}-debug --amend ${{ env.GHCR_AMD64 }}-debug
|
||||||
|
# GitLab Container Registry
|
||||||
|
docker manifest create ${{ env.GLCR_TAG }}-debug --amend ${{ env.GLCR_ARM64 }}-debug --amend ${{ env.GLCR_AMD64 }}-debug
|
||||||
|
docker manifest create ${{ env.GLCR_BRANCH }}-debug --amend ${{ env.GLCR_ARM64 }}-debug --amend ${{ env.GLCR_AMD64 }}-debug
|
||||||
|
|
||||||
- name: Push manifests to Docker registries
|
- name: Push manifests to Docker registries
|
||||||
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||||
run: |
|
run: |
|
||||||
@@ -383,6 +611,12 @@ jobs:
|
|||||||
docker manifest push ${{ env.GHCR_BRANCH }}
|
docker manifest push ${{ env.GHCR_BRANCH }}
|
||||||
docker manifest push ${{ env.GLCR_TAG }}
|
docker manifest push ${{ env.GLCR_TAG }}
|
||||||
docker manifest push ${{ env.GLCR_BRANCH }}
|
docker manifest push ${{ env.GLCR_BRANCH }}
|
||||||
|
docker manifest push ${{ env.DOCKER_TAG }}-debug
|
||||||
|
docker manifest push ${{ env.DOCKER_BRANCH }}-debug
|
||||||
|
docker manifest push ${{ env.GHCR_TAG }}-debug
|
||||||
|
docker manifest push ${{ env.GHCR_BRANCH }}-debug
|
||||||
|
docker manifest push ${{ env.GLCR_TAG }}-debug
|
||||||
|
docker manifest push ${{ env.GLCR_BRANCH }}-debug
|
||||||
|
|
||||||
- name: Add Image Links to Job Summary
|
- name: Add Image Links to Job Summary
|
||||||
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||||
@@ -390,3 +624,6 @@ jobs:
|
|||||||
echo "- \`docker pull ${{ env.DOCKER_TAG }}\`" >> $GITHUB_STEP_SUMMARY
|
echo "- \`docker pull ${{ env.DOCKER_TAG }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- \`docker pull ${{ env.GHCR_TAG }}\`" >> $GITHUB_STEP_SUMMARY
|
echo "- \`docker pull ${{ env.GHCR_TAG }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- \`docker pull ${{ env.GLCR_TAG }}\`" >> $GITHUB_STEP_SUMMARY
|
echo "- \`docker pull ${{ env.GLCR_TAG }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- \`docker pull ${{ env.DOCKER_TAG }}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- \`docker pull ${{ env.GHCR_TAG }}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- \`docker pull ${{ env.GLCR_TAG }}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
@@ -21,8 +21,11 @@ env:
|
|||||||
# Custom nix binary cache if fork is being used
|
# Custom nix binary cache if fork is being used
|
||||||
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
||||||
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
||||||
# Get error output from nix that we can actually use
|
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
|
||||||
NIX_CONFIG: show-trace = true
|
NIX_CONFIG: |
|
||||||
|
show-trace = true
|
||||||
|
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||||
|
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||||
|
|
||||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||||
@@ -44,6 +47,9 @@ jobs:
|
|||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Free Disk Space (Ubuntu)
|
||||||
|
uses: jlumbroso/free-disk-space@main
|
||||||
|
|
||||||
- name: Sync repository
|
- name: Sync repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -98,13 +104,29 @@ jobs:
|
|||||||
- name: Prepare build environment
|
- name: Prepare build environment
|
||||||
run: |
|
run: |
|
||||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||||
direnv allow
|
direnv allow
|
||||||
nix develop --command true
|
nix develop --command true
|
||||||
|
|
||||||
- name: Cache CI dependencies
|
- name: Cache CI dependencies
|
||||||
run: |
|
run: |
|
||||||
|
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
|
||||||
|
ATTEMPTS=3
|
||||||
|
SUCCESS=false
|
||||||
|
while (( ATTEMPTS-- > 0 ))
|
||||||
|
do
|
||||||
bin/nix-build-and-cache ci
|
bin/nix-build-and-cache ci
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
SUCCESS=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
sleep 3
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $SUCCESS == "false" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Run lychee and markdownlint
|
- name: Run lychee and markdownlint
|
||||||
run: |
|
run: |
|
||||||
@@ -113,8 +135,26 @@ jobs:
|
|||||||
|
|
||||||
- name: Build documentation (book)
|
- name: Build documentation (book)
|
||||||
run: |
|
run: |
|
||||||
./bin/nix-build-and-cache just .#book
|
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
|
||||||
|
ATTEMPTS=3
|
||||||
|
SUCCESS=false
|
||||||
|
while (( ATTEMPTS-- > 0 ))
|
||||||
|
do
|
||||||
|
bin/nix-build-and-cache just .#book
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
SUCCESS=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
sleep 3
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $SUCCESS == "false" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
cp -r --dereference result public
|
cp -r --dereference result public
|
||||||
|
|
||||||
- name: Upload generated documentation (book) as normal artifact
|
- name: Upload generated documentation (book) as normal artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run Trivy code and vulnerability scanner on repo
|
- name: Run Trivy code and vulnerability scanner on repo
|
||||||
uses: aquasecurity/trivy-action@0.23.0
|
uses: aquasecurity/trivy-action@0.24.0
|
||||||
with:
|
with:
|
||||||
scan-type: repo
|
scan-type: repo
|
||||||
format: sarif
|
format: sarif
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
severity: CRITICAL,HIGH,MEDIUM,LOW
|
severity: CRITICAL,HIGH,MEDIUM,LOW
|
||||||
|
|
||||||
- name: Run Trivy code and vulnerability scanner on filesystem
|
- name: Run Trivy code and vulnerability scanner on filesystem
|
||||||
uses: aquasecurity/trivy-action@0.23.0
|
uses: aquasecurity/trivy-action@0.24.0
|
||||||
with:
|
with:
|
||||||
scan-type: fs
|
scan-type: fs
|
||||||
format: sarif
|
format: sarif
|
||||||
|
|||||||
+2
-2
@@ -58,7 +58,7 @@ before_script:
|
|||||||
|
|
||||||
ci:
|
ci:
|
||||||
stage: ci
|
stage: ci
|
||||||
image: nixos/nix:2.23.1
|
image: nixos/nix:2.23.3
|
||||||
script:
|
script:
|
||||||
# Cache CI dependencies
|
# Cache CI dependencies
|
||||||
- ./bin/nix-build-and-cache ci
|
- ./bin/nix-build-and-cache ci
|
||||||
@@ -83,7 +83,7 @@ ci:
|
|||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
stage: artifacts
|
stage: artifacts
|
||||||
image: nixos/nix:2.23.1
|
image: nixos/nix:2.23.3
|
||||||
script:
|
script:
|
||||||
- ./bin/nix-build-and-cache just .#static-x86_64-unknown-linux-musl
|
- ./bin/nix-build-and-cache just .#static-x86_64-unknown-linux-musl
|
||||||
- cp result/bin/conduit x86_64-unknown-linux-musl
|
- cp result/bin/conduit x86_64-unknown-linux-musl
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ The following binaries are used in [`engage.toml`][engage.toml]:
|
|||||||
- [`cargo-deb`][cargo-deb]
|
- [`cargo-deb`][cargo-deb]
|
||||||
- [`lychee`][lychee]
|
- [`lychee`][lychee]
|
||||||
- [`markdownlint-cli`][markdownlint-cli]
|
- [`markdownlint-cli`][markdownlint-cli]
|
||||||
|
- `dpkg`
|
||||||
|
|
||||||
### Matrix tests
|
### Matrix tests
|
||||||
|
|
||||||
|
|||||||
Generated
+446
-459
File diff suppressed because it is too large
Load Diff
+116
-51
@@ -14,17 +14,33 @@ categories = ["network-programming"]
|
|||||||
description = "a very cool fork of Conduit, a Matrix homeserver written in Rust"
|
description = "a very cool fork of Conduit, a Matrix homeserver written in Rust"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
homepage = "https://conduwuit.puppyirl.gay/"
|
homepage = "https://conduwuit.puppyirl.gay/"
|
||||||
keywords = ["chat", "matrix", "server"]
|
keywords = ["chat", "matrix", "server", "uwu"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
# See also `rust-toolchain.toml`
|
# See also `rust-toolchain.toml`
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/girlbossceo/conduwuit"
|
repository = "https://github.com/girlbossceo/conduwuit"
|
||||||
rust-version = "1.77.0"
|
rust-version = "1.80.0"
|
||||||
version = "0.4.4"
|
version = "0.4.6"
|
||||||
|
|
||||||
[workspace.metadata.crane]
|
[workspace.metadata.crane]
|
||||||
name = "conduit"
|
name = "conduit"
|
||||||
|
|
||||||
|
[workspace.dependencies.const-str]
|
||||||
|
version = "0.5.7"
|
||||||
|
|
||||||
|
[workspace.dependencies.ctor]
|
||||||
|
version = "0.2.8"
|
||||||
|
|
||||||
|
[workspace.dependencies.cargo_toml]
|
||||||
|
version = "0.20"
|
||||||
|
default-features = false
|
||||||
|
features = ["features"]
|
||||||
|
|
||||||
|
[workspace.dependencies.toml]
|
||||||
|
version = "0.8.14"
|
||||||
|
default-features = false
|
||||||
|
features = ["parse"]
|
||||||
|
|
||||||
[workspace.dependencies.sanitize-filename]
|
[workspace.dependencies.sanitize-filename]
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|
||||||
@@ -50,7 +66,7 @@ version = "0.8.5"
|
|||||||
|
|
||||||
# Used for the http request / response body type for Ruma endpoints used with reqwest
|
# Used for the http request / response body type for Ruma endpoints used with reqwest
|
||||||
[workspace.dependencies.bytes]
|
[workspace.dependencies.bytes]
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
|
|
||||||
[workspace.dependencies.http-body-util]
|
[workspace.dependencies.http-body-util]
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -81,6 +97,7 @@ features = ["typed-header", "tracing"]
|
|||||||
|
|
||||||
[workspace.dependencies.axum-server]
|
[workspace.dependencies.axum-server]
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
default-features = false
|
||||||
features = ["tls-rustls"]
|
features = ["tls-rustls"]
|
||||||
|
|
||||||
[workspace.dependencies.axum-client-ip]
|
[workspace.dependencies.axum-client-ip]
|
||||||
@@ -88,10 +105,12 @@ version = "0.6.0"
|
|||||||
|
|
||||||
[workspace.dependencies.tower]
|
[workspace.dependencies.tower]
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
|
default-features = false
|
||||||
features = ["util"]
|
features = ["util"]
|
||||||
|
|
||||||
[workspace.dependencies.tower-http]
|
[workspace.dependencies.tower-http]
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"add-extension",
|
"add-extension",
|
||||||
"cors",
|
"cors",
|
||||||
@@ -113,11 +132,13 @@ features = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.serde]
|
[workspace.dependencies.serde]
|
||||||
version = "1.0.203"
|
version = "1.0.204"
|
||||||
|
default-features = false
|
||||||
features = ["rc"]
|
features = ["rc"]
|
||||||
|
|
||||||
[workspace.dependencies.serde_json]
|
[workspace.dependencies.serde_json]
|
||||||
version = "1.0.120"
|
version = "1.0.121"
|
||||||
|
default-features = false
|
||||||
features = ["raw_value"]
|
features = ["raw_value"]
|
||||||
|
|
||||||
# Used for appservice registration files
|
# Used for appservice registration files
|
||||||
@@ -169,7 +190,7 @@ default-features = false
|
|||||||
|
|
||||||
# used for conduit's CLI and admin room command parsing
|
# used for conduit's CLI and admin room command parsing
|
||||||
[workspace.dependencies.clap]
|
[workspace.dependencies.clap]
|
||||||
version = "4.5.4"
|
version = "4.5.9"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"std",
|
"std",
|
||||||
@@ -185,7 +206,8 @@ version = "0.3.30"
|
|||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[workspace.dependencies.tokio]
|
[workspace.dependencies.tokio]
|
||||||
version = "1.38.0"
|
version = "1.39.2"
|
||||||
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"fs",
|
"fs",
|
||||||
"net",
|
"net",
|
||||||
@@ -197,12 +219,16 @@ features = [
|
|||||||
"io-util",
|
"io-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[workspace.dependencies.tokio-metrics]
|
||||||
|
version = "0.3.1"
|
||||||
|
|
||||||
[workspace.dependencies.libloading]
|
[workspace.dependencies.libloading]
|
||||||
version = "0.8.3"
|
version = "0.8.5"
|
||||||
|
|
||||||
# Validating urls in config, was already a transitive dependency
|
# Validating urls in config, was already a transitive dependency
|
||||||
[workspace.dependencies.url]
|
[workspace.dependencies.url]
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
default-features = false
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
|
|
||||||
# standard date and time tools
|
# standard date and time tools
|
||||||
@@ -212,7 +238,8 @@ features = ["alloc", "std"]
|
|||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[workspace.dependencies.hyper]
|
[workspace.dependencies.hyper]
|
||||||
version = "1.4.0"
|
version = "1.4.1"
|
||||||
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"server",
|
"server",
|
||||||
"http1",
|
"http1",
|
||||||
@@ -221,6 +248,7 @@ features = [
|
|||||||
|
|
||||||
[workspace.dependencies.hyper-util]
|
[workspace.dependencies.hyper-util]
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"client",
|
"client",
|
||||||
"server-auto",
|
"server-auto",
|
||||||
@@ -232,11 +260,13 @@ features = [
|
|||||||
# to support multiple variations of setting a config option
|
# to support multiple variations of setting a config option
|
||||||
[workspace.dependencies.either]
|
[workspace.dependencies.either]
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
|
default-features = false
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
|
|
||||||
# Used for reading the configuration from conduwuit.toml & environment variables
|
# Used for reading the configuration from conduwuit.toml & environment variables
|
||||||
[workspace.dependencies.figment]
|
[workspace.dependencies.figment]
|
||||||
version = "0.10.18"
|
version = "0.10.18"
|
||||||
|
default-features = false
|
||||||
features = ["env", "toml"]
|
features = ["env", "toml"]
|
||||||
|
|
||||||
[workspace.dependencies.hickory-resolver]
|
[workspace.dependencies.hickory-resolver]
|
||||||
@@ -245,7 +275,7 @@ default-features = false
|
|||||||
|
|
||||||
# Used for conduit::Error type
|
# Used for conduit::Error type
|
||||||
[workspace.dependencies.thiserror]
|
[workspace.dependencies.thiserror]
|
||||||
version = "1.0.61"
|
version = "1.0.63"
|
||||||
|
|
||||||
# Used when hashing the state
|
# Used when hashing the state
|
||||||
[workspace.dependencies.ring]
|
[workspace.dependencies.ring]
|
||||||
@@ -265,7 +295,7 @@ version = "2.1.1"
|
|||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
||||||
[workspace.dependencies.async-trait]
|
[workspace.dependencies.async-trait]
|
||||||
version = "0.1.80"
|
version = "0.1.81"
|
||||||
|
|
||||||
[workspace.dependencies.lru-cache]
|
[workspace.dependencies.lru-cache]
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -274,7 +304,7 @@ version = "0.1.2"
|
|||||||
[workspace.dependencies.ruma]
|
[workspace.dependencies.ruma]
|
||||||
git = "https://github.com/girlbossceo/ruwuma"
|
git = "https://github.com/girlbossceo/ruwuma"
|
||||||
#branch = "conduwuit-changes"
|
#branch = "conduwuit-changes"
|
||||||
rev = "c51ccb2c68d2e3557eb12b1a49036531711ec0e5"
|
rev = "c76e2873c1593a3308d4ba3e0e4a1db65acf8536"
|
||||||
features = [
|
features = [
|
||||||
"compat",
|
"compat",
|
||||||
"rand",
|
"rand",
|
||||||
@@ -287,6 +317,7 @@ features = [
|
|||||||
"server-util",
|
"server-util",
|
||||||
"unstable-exhaustive-types",
|
"unstable-exhaustive-types",
|
||||||
"ring-compat",
|
"ring-compat",
|
||||||
|
"identifiers-validation",
|
||||||
"unstable-unspecified",
|
"unstable-unspecified",
|
||||||
"unstable-msc2448",
|
"unstable-msc2448",
|
||||||
"unstable-msc2666",
|
"unstable-msc2666",
|
||||||
@@ -301,17 +332,12 @@ features = [
|
|||||||
"unstable-extensible-events",
|
"unstable-extensible-events",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.ruma-identifiers-validation]
|
|
||||||
git = "https://github.com/girlbossceo/ruwuma"
|
|
||||||
rev = "fd686e77950680462377c9105dfb4136dd49c7a0"
|
|
||||||
|
|
||||||
[workspace.dependencies.rust-rocksdb]
|
[workspace.dependencies.rust-rocksdb]
|
||||||
path = "deps/rust-rocksdb"
|
path = "deps/rust-rocksdb"
|
||||||
package = "rust-rocksdb-uwu"
|
package = "rust-rocksdb-uwu"
|
||||||
features = [
|
features = [
|
||||||
"multi-threaded-cf",
|
"multi-threaded-cf",
|
||||||
"mt_static",
|
"mt_static",
|
||||||
"snappy",
|
|
||||||
"lz4",
|
"lz4",
|
||||||
"zstd",
|
"zstd",
|
||||||
"zlib",
|
"zlib",
|
||||||
@@ -334,7 +360,7 @@ version = "0.21.0"
|
|||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
||||||
[workspace.dependencies.tracing-opentelemetry]
|
[workspace.dependencies.tracing-opentelemetry]
|
||||||
version = "0.22.0"
|
version = "0.25.0"
|
||||||
|
|
||||||
[workspace.dependencies.opentelemetry_sdk]
|
[workspace.dependencies.opentelemetry_sdk]
|
||||||
version = "0.21.2"
|
version = "0.21.2"
|
||||||
@@ -367,28 +393,26 @@ version = "0.34.0"
|
|||||||
version = "0.34.0"
|
version = "0.34.0"
|
||||||
|
|
||||||
# jemalloc usage
|
# jemalloc usage
|
||||||
|
# locked to 0.5.4 due to static binary linking breakage
|
||||||
[workspace.dependencies.tikv-jemalloc-sys]
|
[workspace.dependencies.tikv-jemalloc-sys]
|
||||||
version = "0.5.4"
|
version = "=0.5.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["stats", "unprefixed_malloc_on_supported_platforms"]
|
features = ["stats", "unprefixed_malloc_on_supported_platforms"]
|
||||||
[workspace.dependencies.tikv-jemallocator]
|
[workspace.dependencies.tikv-jemallocator]
|
||||||
version = "0.5.4"
|
version = "=0.5.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["stats", "unprefixed_malloc_on_supported_platforms"]
|
features = ["stats", "unprefixed_malloc_on_supported_platforms"]
|
||||||
[workspace.dependencies.tikv-jemalloc-ctl]
|
[workspace.dependencies.tikv-jemalloc-ctl]
|
||||||
version = "0.5.4"
|
version = "=0.5.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["use_std"]
|
features = ["use_std"]
|
||||||
|
|
||||||
[workspace.dependencies.tokio-metrics]
|
|
||||||
version = "0.3.1"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[workspace.dependencies.console-subscriber]
|
[workspace.dependencies.console-subscriber]
|
||||||
version = "0.3"
|
version = "0.4"
|
||||||
|
|
||||||
[workspace.dependencies.nix]
|
[workspace.dependencies.nix]
|
||||||
version = "0.29.0"
|
version = "0.29.0"
|
||||||
|
default-features = false
|
||||||
features = ["resource"]
|
features = ["resource"]
|
||||||
|
|
||||||
[workspace.dependencies.sd-notify]
|
[workspace.dependencies.sd-notify]
|
||||||
@@ -405,11 +429,26 @@ features = [
|
|||||||
|
|
||||||
[workspace.dependencies.rustyline-async]
|
[workspace.dependencies.rustyline-async]
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[workspace.dependencies.termimad]
|
[workspace.dependencies.termimad]
|
||||||
version = "0.29.4"
|
version = "0.29.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
[workspace.dependencies.checked_ops]
|
||||||
|
version = "0.1"
|
||||||
|
|
||||||
|
[workspace.dependencies.syn]
|
||||||
|
version = "2.0.72"
|
||||||
|
default-features = false
|
||||||
|
features = ["full", "extra-traits"]
|
||||||
|
|
||||||
|
[workspace.dependencies.quote]
|
||||||
|
version = "1.0.36"
|
||||||
|
|
||||||
|
[workspace.dependencies.proc-macro2]
|
||||||
|
version = "1.0.86"
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Patches
|
# Patches
|
||||||
@@ -420,16 +459,16 @@ default-features = false
|
|||||||
# https://github.com/girlbossceo/tracing/commit/b348dca742af641c47bc390261f60711c2af573c
|
# https://github.com/girlbossceo/tracing/commit/b348dca742af641c47bc390261f60711c2af573c
|
||||||
[patch.crates-io.tracing-subscriber]
|
[patch.crates-io.tracing-subscriber]
|
||||||
git = "https://github.com/girlbossceo/tracing"
|
git = "https://github.com/girlbossceo/tracing"
|
||||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||||
[patch.crates-io.tracing]
|
[patch.crates-io.tracing]
|
||||||
git = "https://github.com/girlbossceo/tracing"
|
git = "https://github.com/girlbossceo/tracing"
|
||||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||||
[patch.crates-io.tracing-core]
|
[patch.crates-io.tracing-core]
|
||||||
git = "https://github.com/girlbossceo/tracing"
|
git = "https://github.com/girlbossceo/tracing"
|
||||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||||
[patch.crates-io.tracing-log]
|
[patch.crates-io.tracing-log]
|
||||||
git = "https://github.com/girlbossceo/tracing"
|
git = "https://github.com/girlbossceo/tracing"
|
||||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||||
|
|
||||||
# fixes hyper graceful shutdowns [https://github.com/programatik29/axum-server/issues/114]
|
# fixes hyper graceful shutdowns [https://github.com/programatik29/axum-server/issues/114]
|
||||||
# https://github.com/girlbossceo/axum-server/commit/8e3368d899079818934e61cc9c839abcbbcada8a
|
# https://github.com/girlbossceo/axum-server/commit/8e3368d899079818934e61cc9c839abcbbcada8a
|
||||||
@@ -437,6 +476,12 @@ rev = "b348dca742af641c47bc390261f60711c2af573c"
|
|||||||
git = "https://github.com/girlbossceo/axum-server"
|
git = "https://github.com/girlbossceo/axum-server"
|
||||||
rev = "8e3368d899079818934e61cc9c839abcbbcada8a"
|
rev = "8e3368d899079818934e61cc9c839abcbbcada8a"
|
||||||
|
|
||||||
|
# adds a tab completion callback: https://github.com/girlbossceo/rustyline-async/commit/de26100b0db03e419a3d8e1dd26895d170d1fe50
|
||||||
|
# adds event for CTRL+\: https://github.com/girlbossceo/rustyline-async/commit/67d8c49aeac03a5ef4e818f663eaa94dd7bf339b
|
||||||
|
[patch.crates-io.rustyline-async]
|
||||||
|
git = "https://github.com/girlbossceo/rustyline-async"
|
||||||
|
rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Our crates
|
# Our crates
|
||||||
#
|
#
|
||||||
@@ -471,6 +516,11 @@ package = "conduit_core"
|
|||||||
path = "src/core"
|
path = "src/core"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
[workspace.dependencies.conduit-macros]
|
||||||
|
package = "conduit_macros"
|
||||||
|
path = "src/macros"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# Release profiles
|
# Release profiles
|
||||||
@@ -519,7 +569,17 @@ lto = "fat"
|
|||||||
[profile.release-max-perf.build-override]
|
[profile.release-max-perf.build-override]
|
||||||
inherits = "release-max-perf"
|
inherits = "release-max-perf"
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
|
codegen-units = 32
|
||||||
#rustflags = [
|
#rustflags = [
|
||||||
|
# '-Crelocation-model=pic',
|
||||||
|
# '-Ctarget-feature=-crt-static',
|
||||||
|
# '-Clink-arg=-Wl,--no-gc-sections',
|
||||||
|
#]
|
||||||
|
|
||||||
|
[profile.release-max-perf.package.conduit_macros]
|
||||||
|
inherits = "release-max-perf.build-override"
|
||||||
|
#rustflags = [
|
||||||
|
# '-Crelocation-model=pic',
|
||||||
# '-Ctarget-feature=-crt-static',
|
# '-Ctarget-feature=-crt-static',
|
||||||
#]
|
#]
|
||||||
|
|
||||||
@@ -648,7 +708,16 @@ opt-level = 'z'
|
|||||||
# '-Clink-arg=-Wl,-z,nodelete',
|
# '-Clink-arg=-Wl,-z,nodelete',
|
||||||
#]
|
#]
|
||||||
|
|
||||||
|
# primarily used for CI
|
||||||
[profile.test]
|
[profile.test]
|
||||||
|
inherits = "dev"
|
||||||
|
codegen-units = 16
|
||||||
|
incremental = false
|
||||||
|
|
||||||
|
[profile.test.package.'*']
|
||||||
|
inherits = "dev"
|
||||||
|
debug = 0
|
||||||
|
codegen-units = 16
|
||||||
incremental = false
|
incremental = false
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -710,47 +779,39 @@ variant_size_differences = "allow"
|
|||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
|
|
||||||
###################
|
###################
|
||||||
cargo = "warn"
|
cargo = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
## some sadness
|
## some sadness
|
||||||
multiple_crate_versions = { level = "allow", priority = 1 }
|
multiple_crate_versions = { level = "allow", priority = 1 }
|
||||||
|
|
||||||
###################
|
###################
|
||||||
complexity = "warn"
|
complexity = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
###################
|
###################
|
||||||
correctness = "warn"
|
correctness = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
###################
|
###################
|
||||||
nursery = "warn"
|
nursery = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
## some sadness
|
## some sadness
|
||||||
missing_const_for_fn = { level = "allow", priority = 1 } # TODO
|
missing_const_for_fn = { level = "allow", priority = 1 } # TODO
|
||||||
needless_collect = { level = "allow", priority = 1 } # TODO
|
|
||||||
option_if_let_else = { level = "allow", priority = 1 } # TODO
|
option_if_let_else = { level = "allow", priority = 1 } # TODO
|
||||||
redundant_pub_crate = { level = "allow", priority = 1 } # TODO
|
redundant_pub_crate = { level = "allow", priority = 1 } # TODO
|
||||||
significant_drop_in_scrutinee = { level = "allow", priority = 1 } # TODO
|
significant_drop_in_scrutinee = { level = "allow", priority = 1 } # TODO
|
||||||
significant_drop_tightening = { level = "allow", priority = 1 } # TODO
|
significant_drop_tightening = { level = "allow", priority = 1 } # TODO
|
||||||
|
|
||||||
###################
|
###################
|
||||||
pedantic = "warn"
|
pedantic = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
## some sadness
|
## some sadness
|
||||||
cast_possible_truncation = { level = "allow", priority = 1 }
|
|
||||||
cast_precision_loss = { level = "allow", priority = 1 }
|
|
||||||
cast_sign_loss = { level = "allow", priority = 1 }
|
|
||||||
doc_markdown = { level = "allow", priority = 1 }
|
doc_markdown = { level = "allow", priority = 1 }
|
||||||
error_impl_error = { level = "allow", priority = 1 }
|
enum_glob_use = { level = "allow", priority = 1 }
|
||||||
expect_used = { level = "allow", priority = 1 }
|
|
||||||
if_not_else = { level = "allow", priority = 1 }
|
if_not_else = { level = "allow", priority = 1 }
|
||||||
if_then_some_else_none = { level = "allow", priority = 1 }
|
if_then_some_else_none = { level = "allow", priority = 1 }
|
||||||
implicit_return = { level = "allow", priority = 1 }
|
|
||||||
inline_always = { level = "allow", priority = 1 }
|
inline_always = { level = "allow", priority = 1 }
|
||||||
map_err_ignore = { level = "allow", priority = 1 }
|
|
||||||
missing_docs_in_private_items = { level = "allow", priority = 1 }
|
missing_docs_in_private_items = { level = "allow", priority = 1 }
|
||||||
missing_errors_doc = { level = "allow", priority = 1 }
|
missing_errors_doc = { level = "allow", priority = 1 }
|
||||||
missing_panics_doc = { level = "allow", priority = 1 }
|
missing_panics_doc = { level = "allow", priority = 1 }
|
||||||
mod_module_files = { level = "allow", priority = 1 }
|
|
||||||
module_name_repetitions = { level = "allow", priority = 1 }
|
module_name_repetitions = { level = "allow", priority = 1 }
|
||||||
no_effect_underscore_binding = { level = "allow", priority = 1 }
|
no_effect_underscore_binding = { level = "allow", priority = 1 }
|
||||||
similar_names = { level = "allow", priority = 1 }
|
similar_names = { level = "allow", priority = 1 }
|
||||||
@@ -759,13 +820,15 @@ unnecessary_wraps = { level = "allow", priority = 1 }
|
|||||||
unused_async = { level = "allow", priority = 1 }
|
unused_async = { level = "allow", priority = 1 }
|
||||||
|
|
||||||
###################
|
###################
|
||||||
perf = "warn"
|
perf = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
###################
|
###################
|
||||||
#restriction = "warn"
|
#restriction = "warn"
|
||||||
|
|
||||||
#arithmetic_side_effects = "warn" # TODO
|
#allow_attributes = "warn" # UNSTABLE
|
||||||
#as_conversions = "warn" # TODO
|
arithmetic_side_effects = "warn"
|
||||||
|
as_conversions = "warn"
|
||||||
|
as_underscore = "warn"
|
||||||
assertions_on_result_states = "warn"
|
assertions_on_result_states = "warn"
|
||||||
dbg_macro = "warn"
|
dbg_macro = "warn"
|
||||||
default_union_representation = "warn"
|
default_union_representation = "warn"
|
||||||
@@ -779,7 +842,6 @@ fn_to_numeric_cast_any = "warn"
|
|||||||
format_push_string = "warn"
|
format_push_string = "warn"
|
||||||
get_unwrap = "warn"
|
get_unwrap = "warn"
|
||||||
impl_trait_in_params = "warn"
|
impl_trait_in_params = "warn"
|
||||||
let_underscore_must_use = "warn"
|
|
||||||
let_underscore_untyped = "warn"
|
let_underscore_untyped = "warn"
|
||||||
lossy_float_literal = "warn"
|
lossy_float_literal = "warn"
|
||||||
mem_forget = "warn"
|
mem_forget = "warn"
|
||||||
@@ -793,6 +855,7 @@ rest_pat_in_fully_bound_structs = "warn"
|
|||||||
semicolon_outside_block = "warn"
|
semicolon_outside_block = "warn"
|
||||||
str_to_string = "warn"
|
str_to_string = "warn"
|
||||||
string_lit_chars_any = "warn"
|
string_lit_chars_any = "warn"
|
||||||
|
string_slice = "warn"
|
||||||
string_to_string = "warn"
|
string_to_string = "warn"
|
||||||
suspicious_xor_used_as_pow = "warn"
|
suspicious_xor_used_as_pow = "warn"
|
||||||
tests_outside_test_module = "warn"
|
tests_outside_test_module = "warn"
|
||||||
@@ -803,17 +866,19 @@ unnecessary_safety_doc = "warn"
|
|||||||
unnecessary_self_imports = "warn"
|
unnecessary_self_imports = "warn"
|
||||||
unneeded_field_pattern = "warn"
|
unneeded_field_pattern = "warn"
|
||||||
unseparated_literal_suffix = "warn"
|
unseparated_literal_suffix = "warn"
|
||||||
|
#unwrap_used = "warn" # TODO
|
||||||
verbose_file_reads = "warn"
|
verbose_file_reads = "warn"
|
||||||
|
|
||||||
###################
|
###################
|
||||||
style = "warn"
|
style = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
## some sadness
|
## some sadness
|
||||||
# trivial assertions are quite alright
|
# trivial assertions are quite alright
|
||||||
assertions_on_constants = { level = "allow", priority = 1 }
|
assertions_on_constants = { level = "allow", priority = 1 }
|
||||||
|
module_inception = { level = "allow", priority = 1 }
|
||||||
|
|
||||||
###################
|
###################
|
||||||
suspicious = "warn"
|
suspicious = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
## some sadness
|
## some sadness
|
||||||
let_underscore_future = { level = "allow", priority = 1 }
|
let_underscore_future = { level = "allow", priority = 1 }
|
||||||
|
|||||||
+12
-5
@@ -7,7 +7,7 @@ set -euo pipefail
|
|||||||
# The `COMPLEMENT_SRC` environment variable is set in the Nix dev shell, which
|
# The `COMPLEMENT_SRC` environment variable is set in the Nix dev shell, which
|
||||||
# points to a store path containing the Complement source code. It's likely you
|
# points to a store path containing the Complement source code. It's likely you
|
||||||
# want to just pass that as the first argument to use it here.
|
# want to just pass that as the first argument to use it here.
|
||||||
COMPLEMENT_SRC="$1"
|
COMPLEMENT_SRC="${COMPLEMENT_SRC:-$1}"
|
||||||
|
|
||||||
# A `.jsonl` file to write test logs to
|
# A `.jsonl` file to write test logs to
|
||||||
LOG_FILE="$2"
|
LOG_FILE="$2"
|
||||||
@@ -17,12 +17,19 @@ RESULTS_FILE="$3"
|
|||||||
|
|
||||||
OCI_IMAGE="complement-conduit:main"
|
OCI_IMAGE="complement-conduit:main"
|
||||||
|
|
||||||
# Complement tests that are skipped due to flakiness/reliability issues (likely
|
# Complement tests that are skipped due to flakiness/reliability issues
|
||||||
# Complement itself induced based on various open issues)
|
|
||||||
#
|
|
||||||
# According to Go docs, these are separated by forward slashes and not pipes (why)
|
|
||||||
SKIPPED_COMPLEMENT_TESTS='-skip=TestClientSpacesSummary.*|TestJoinFederatedRoomFromApplicationServiceBridgeUser.*|TestJumpToDateEndpoint.*'
|
SKIPPED_COMPLEMENT_TESTS='-skip=TestClientSpacesSummary.*|TestJoinFederatedRoomFromApplicationServiceBridgeUser.*|TestJumpToDateEndpoint.*'
|
||||||
|
|
||||||
|
# $COMPLEMENT_SRC needs to be a directory to Complement source code
|
||||||
|
if [ -f "$COMPLEMENT_SRC" ]; then
|
||||||
|
echo "\$COMPLEMENT_SRC must be a directory/path to Complement source code"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# quick test to make sure we can actually write to $LOG_FILE and $RESULTS_FILE
|
||||||
|
touch $LOG_FILE && rm -v $LOG_FILE
|
||||||
|
touch $RESULTS_FILE && rm -v $RESULTS_FILE
|
||||||
|
|
||||||
toplevel="$(git rev-parse --show-toplevel)"
|
toplevel="$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
pushd "$toplevel" > /dev/null
|
pushd "$toplevel" > /dev/null
|
||||||
|
|||||||
+18
-4
@@ -52,11 +52,22 @@
|
|||||||
|
|
||||||
# Performance monitoring/tracing sample rate for Sentry.io
|
# Performance monitoring/tracing sample rate for Sentry.io
|
||||||
#
|
#
|
||||||
# Note that too high values may impact performance, and can be disabled by setting it to 0.0
|
# Note that too high values may impact performance, and can be disabled by setting it to 0.0 (0%)
|
||||||
|
# This value is read as a percentage to Sentry, represented as a decimal
|
||||||
#
|
#
|
||||||
# Defaults to 0.15
|
# Defaults to 15% of traces (0.15)
|
||||||
#sentry_traces_sample_rate = 0.15
|
#sentry_traces_sample_rate = 0.15
|
||||||
|
|
||||||
|
# Whether to attach a stacktrace to Sentry reports.
|
||||||
|
#sentry_attach_stacktrace = false
|
||||||
|
|
||||||
|
# Send panics to sentry. This is true by default, but sentry has to be enabled.
|
||||||
|
#sentry_send_panic = true
|
||||||
|
|
||||||
|
# Send errors to sentry. This is true by default, but sentry has to be enabled. This option is
|
||||||
|
# only effective in release-mode; forced to false in debug-mode.
|
||||||
|
#sentry_send_error = true
|
||||||
|
|
||||||
|
|
||||||
### Database configuration
|
### Database configuration
|
||||||
|
|
||||||
@@ -391,7 +402,7 @@ allow_profile_lookup_federation_requests = true
|
|||||||
# setting this to false may reduce startup time.
|
# setting this to false may reduce startup time.
|
||||||
#
|
#
|
||||||
# Enabled by default.
|
# Enabled by default.
|
||||||
#media_statup_check = true
|
#media_startup_check = true
|
||||||
|
|
||||||
# OpenID token expiration/TTL in seconds
|
# OpenID token expiration/TTL in seconds
|
||||||
#
|
#
|
||||||
@@ -411,8 +422,11 @@ allow_profile_lookup_federation_requests = true
|
|||||||
|
|
||||||
# Set this to any float value to multiply conduwuit's in-memory LRU caches with.
|
# Set this to any float value to multiply conduwuit's in-memory LRU caches with.
|
||||||
# May be useful if you have significant memory to spare to increase performance.
|
# May be useful if you have significant memory to spare to increase performance.
|
||||||
|
#
|
||||||
|
# This was previously called `conduit_cache_capacity_modifier`
|
||||||
|
#
|
||||||
# Defaults to 1.0.
|
# Defaults to 1.0.
|
||||||
#conduit_cache_capacity_modifier = 1.0
|
#cache_capacity_modifier = 1.0
|
||||||
|
|
||||||
# Set this to any float value in megabytes for conduwuit to tell the database engine that this much memory is available for database-related caches.
|
# Set this to any float value in megabytes for conduwuit to tell the database engine that this much memory is available for database-related caches.
|
||||||
# May be useful if you have significant memory to spare to increase performance.
|
# May be useful if you have significant memory to spare to increase performance.
|
||||||
|
|||||||
Vendored
+2
-2
@@ -10,7 +10,7 @@ repository.workspace = true
|
|||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["snappy", "lz4", "zstd", "zlib", "bzip2"]
|
default = ["lz4", "zstd", "zlib", "bzip2"]
|
||||||
jemalloc = ["rust-rocksdb/jemalloc"]
|
jemalloc = ["rust-rocksdb/jemalloc"]
|
||||||
io-uring = ["rust-rocksdb/io-uring"]
|
io-uring = ["rust-rocksdb/io-uring"]
|
||||||
valgrind = ["rust-rocksdb/valgrind"]
|
valgrind = ["rust-rocksdb/valgrind"]
|
||||||
@@ -27,7 +27,7 @@ malloc-usable-size = ["rust-rocksdb/malloc-usable-size"]
|
|||||||
|
|
||||||
[dependencies.rust-rocksdb]
|
[dependencies.rust-rocksdb]
|
||||||
git = "https://github.com/zaidoon1/rust-rocksdb"
|
git = "https://github.com/zaidoon1/rust-rocksdb"
|
||||||
rev = "b4887edfb84771336930855727390edec07d63fa"
|
rev = "4056a3b0f823013fec49f6d0b3e5698856e6476a"
|
||||||
#branch = "master"
|
#branch = "master"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# conduwuit - Behind Traefik Reverse Proxy
|
# conduwuit - Behind Traefik Reverse Proxy
|
||||||
version: '2.4' # uses '2.4' for cpuset
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
homeserver:
|
homeserver:
|
||||||
@@ -24,7 +23,7 @@ services:
|
|||||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||||
#CONDUWUIT_LOG: warn,state_res=warn
|
#CONDUWUIT_LOG: warn,state_res=warn
|
||||||
CONDUWUIT_ADDRESS: 0.0.0.0
|
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||||
#CONDUWUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
|
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||||
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
|
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
|
||||||
|
|
||||||
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
|
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# conduwuit - Traefik Reverse Proxy Labels
|
# conduwuit - Traefik Reverse Proxy Labels
|
||||||
version: '2.4' # uses '2.4' for cpuset
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
homeserver:
|
homeserver:
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
# This compose file uses caddy-docker-proxy as the reverse proxy for conduwuit!
|
||||||
|
# For more info, visit https://github.com/lucaslorentz/caddy-docker-proxy
|
||||||
|
image: lucaslorentz/caddy-docker-proxy:ci-alpine
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
- 443:443
|
||||||
|
environment:
|
||||||
|
- CADDY_INGRESS_NETWORKS=caddy
|
||||||
|
networks:
|
||||||
|
- caddy
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./data:/data
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
caddy: example.com
|
||||||
|
caddy.0_respond: /.well-known/matrix/server {"m.server":"matrix.example.com:443"}
|
||||||
|
caddy.1_respond: /.well-known/matrix/client {"m.server":{"base_url":"https://matrix.example.com"},"m.homeserver":{"base_url":"https://matrix.example.com"},"org.matrix.msc3575.proxy":{"url":"https://matrix.example.com"}}
|
||||||
|
|
||||||
|
homeserver:
|
||||||
|
### If you already built the conduwuit image with 'docker build' or want to use a registry image,
|
||||||
|
### then you are ready to go.
|
||||||
|
image: girlbossceo/conduwuit:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- db:/var/lib/conduwuit
|
||||||
|
#- ./conduwuit.toml:/etc/conduwuit.toml
|
||||||
|
environment:
|
||||||
|
CONDUWUIT_SERVER_NAME: example.com # EDIT THIS
|
||||||
|
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
|
||||||
|
CONDUWUIT_DATABASE_BACKEND: rocksdb
|
||||||
|
CONDUWUIT_PORT: 6167
|
||||||
|
CONDUWUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||||
|
CONDUWUIT_ALLOW_REGISTRATION: 'true'
|
||||||
|
CONDUWUIT_ALLOW_FEDERATION: 'true'
|
||||||
|
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||||
|
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||||
|
#CONDUWUIT_LOG: warn,state_res=warn
|
||||||
|
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||||
|
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||||
|
networks:
|
||||||
|
- caddy
|
||||||
|
labels:
|
||||||
|
caddy: matrix.example.com
|
||||||
|
caddy.reverse_proxy: "{{upstreams 6167}}"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
caddy:
|
||||||
|
external: true
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
# conduwuit - Behind Traefik Reverse Proxy
|
# conduwuit - Behind Traefik Reverse Proxy
|
||||||
version: '2.4' # uses '2.4' for cpuset
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
homeserver:
|
homeserver:
|
||||||
@@ -16,7 +15,7 @@ services:
|
|||||||
CONDUWUIT_SERVER_NAME: your.server.name # EDIT THIS
|
CONDUWUIT_SERVER_NAME: your.server.name # EDIT THIS
|
||||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||||
CONDUWUIT_ALLOW_REGISTRATION : 'true'
|
CONDUWUIT_ALLOW_REGISTRATION : 'true'
|
||||||
#CONDUWUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
|
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||||
### Uncomment and change values as desired
|
### Uncomment and change values as desired
|
||||||
# CONDUWUIT_ADDRESS: 0.0.0.0
|
# CONDUWUIT_ADDRESS: 0.0.0.0
|
||||||
# CONDUWUIT_PORT: 6167
|
# CONDUWUIT_PORT: 6167
|
||||||
@@ -28,7 +27,6 @@ services:
|
|||||||
# CONDUWUIT_DATABASE_PATH: /srv/conduwuit/.local/share/conduwuit
|
# CONDUWUIT_DATABASE_PATH: /srv/conduwuit/.local/share/conduwuit
|
||||||
# CONDUWUIT_WORKERS: 10
|
# CONDUWUIT_WORKERS: 10
|
||||||
# CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
# CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||||
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
|
|
||||||
|
|
||||||
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
|
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
|
||||||
# to serve those two as static files. If you want to use a different way, delete or comment the below service, here
|
# to serve those two as static files. If you want to use a different way, delete or comment the below service, here
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# conduwuit
|
# conduwuit
|
||||||
version: '2.4' # uses '2.4' for cpuset
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
homeserver:
|
homeserver:
|
||||||
@@ -24,8 +23,7 @@ services:
|
|||||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||||
#CONDUWUIT_LOG: warn,state_res=warn
|
#CONDUWUIT_LOG: warn,state_res=warn
|
||||||
CONDUWUIT_ADDRESS: 0.0.0.0
|
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||||
#CONDUWUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
|
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||||
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
|
|
||||||
#
|
#
|
||||||
### Uncomment if you want to use your own Element-Web App.
|
### Uncomment if you want to use your own Element-Web App.
|
||||||
### Note: You need to provide a config.json for Element and you also need a second
|
### Note: You need to provide a config.json for Element and you also need a second
|
||||||
|
|||||||
@@ -59,13 +59,22 @@ If the `docker run` command is not for you or your setup, you can also use one o
|
|||||||
Depending on your proxy setup, you can use one of the following files;
|
Depending on your proxy setup, you can use one of the following files;
|
||||||
|
|
||||||
- If you already have a `traefik` instance set up, use [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml)
|
- If you already have a `traefik` instance set up, use [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml)
|
||||||
- If you don't have a `traefik` instance set up (or any other reverse proxy), use [`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)
|
- If you don't have a `traefik` instance set up and would like to use it, use [`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)
|
||||||
|
- If you want a setup that works out of the box with `caddy-docker-proxy`, use [`docker-compose.with-caddy.yml`](docker-compose.with-caddy.yml) and replace all `example.com` placeholders with your own domain
|
||||||
- For any other reverse proxy, use [`docker-compose.yml`](docker-compose.yml)
|
- For any other reverse proxy, use [`docker-compose.yml`](docker-compose.yml)
|
||||||
|
|
||||||
When picking the traefik-related compose file, rename it so it matches `docker-compose.yml`, and
|
When picking the traefik-related compose file, rename it so it matches `docker-compose.yml`, and
|
||||||
rename the override file to `docker-compose.override.yml`. Edit the latter with the values you want
|
rename the override file to `docker-compose.override.yml`. Edit the latter with the values you want
|
||||||
for your server.
|
for your server.
|
||||||
|
|
||||||
|
When picking the `caddy-docker-proxy` compose file, it's important to first create the `caddy` network before spinning up the containers:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker network create caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, you can rename it so it matches `docker-compose.yml` and spin up the containers!
|
||||||
|
|
||||||
Additional info about deploying conduwuit can be found [here](generic.md).
|
Additional info about deploying conduwuit can be found [here](generic.md).
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Otherwise, follow standard Rust project build guides (installing git and cloning
|
|||||||
While conduwuit can run as any user it is better to use dedicated users for different services. This also allows
|
While conduwuit can run as any user it is better to use dedicated users for different services. This also allows
|
||||||
you to make sure that the file permissions are correctly set up.
|
you to make sure that the file permissions are correctly set up.
|
||||||
|
|
||||||
In Debian or RHEL, you can use this command to create a conduwuit user:
|
In Debian or Fedora/RHEL, you can use this command to create a conduwuit user:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo adduser --system conduwuit --group --disabled-login --no-create-home
|
sudo adduser --system conduwuit --group --disabled-login --no-create-home
|
||||||
@@ -53,13 +53,11 @@ RocksDB is the only supported database backend.
|
|||||||
|
|
||||||
## Setting the correct file permissions
|
## Setting the correct file permissions
|
||||||
|
|
||||||
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this command on
|
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this:
|
||||||
|
|
||||||
Debian or RHEL:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo chown -R root:root /etc/conduwuit
|
sudo chown -R root:root /etc/conduwuit
|
||||||
sudo chmod 755 /etc/conduwuit
|
sudo chmod -R 755 /etc/conduwuit
|
||||||
```
|
```
|
||||||
|
|
||||||
If you use the default database path you also need to run this:
|
If you use the default database path you also need to run this:
|
||||||
|
|||||||
+22
-2
@@ -60,6 +60,11 @@ name = "markdownlint"
|
|||||||
group = "versions"
|
group = "versions"
|
||||||
script = "markdownlint --version"
|
script = "markdownlint --version"
|
||||||
|
|
||||||
|
[[task]]
|
||||||
|
name = "dpkg"
|
||||||
|
group = "versions"
|
||||||
|
script = "dpkg --version"
|
||||||
|
|
||||||
[[task]]
|
[[task]]
|
||||||
name = "cargo-audit"
|
name = "cargo-audit"
|
||||||
group = "security"
|
group = "security"
|
||||||
@@ -68,7 +73,9 @@ script = "cargo audit -D warnings -D unmaintained -D unsound -D yanked"
|
|||||||
[[task]]
|
[[task]]
|
||||||
name = "cargo-fmt"
|
name = "cargo-fmt"
|
||||||
group = "lints"
|
group = "lints"
|
||||||
script = "cargo fmt --check -- --color=always"
|
script = """
|
||||||
|
cargo fmt --check -- --color=always
|
||||||
|
"""
|
||||||
|
|
||||||
[[task]]
|
[[task]]
|
||||||
name = "cargo-doc"
|
name = "cargo-doc"
|
||||||
@@ -79,6 +86,7 @@ env DIRENV_DEVSHELL=all-features \
|
|||||||
direnv exec . \
|
direnv exec . \
|
||||||
cargo doc \
|
cargo doc \
|
||||||
--workspace \
|
--workspace \
|
||||||
|
--profile test \
|
||||||
--all-features \
|
--all-features \
|
||||||
--no-deps \
|
--no-deps \
|
||||||
--document-private-items \
|
--document-private-items \
|
||||||
@@ -91,6 +99,7 @@ group = "lints"
|
|||||||
script = """
|
script = """
|
||||||
cargo clippy \
|
cargo clippy \
|
||||||
--workspace \
|
--workspace \
|
||||||
|
--profile test \
|
||||||
--all-targets \
|
--all-targets \
|
||||||
--color=always \
|
--color=always \
|
||||||
-- \
|
-- \
|
||||||
@@ -105,6 +114,7 @@ env DIRENV_DEVSHELL=all-features \
|
|||||||
direnv exec . \
|
direnv exec . \
|
||||||
cargo clippy \
|
cargo clippy \
|
||||||
--workspace \
|
--workspace \
|
||||||
|
--profile test \
|
||||||
--all-targets \
|
--all-targets \
|
||||||
--all-features \
|
--all-features \
|
||||||
--color=always \
|
--color=always \
|
||||||
@@ -118,6 +128,7 @@ group = "lints"
|
|||||||
script = """
|
script = """
|
||||||
cargo clippy \
|
cargo clippy \
|
||||||
--workspace \
|
--workspace \
|
||||||
|
--profile test \
|
||||||
--features jemalloc \
|
--features jemalloc \
|
||||||
--all-targets \
|
--all-targets \
|
||||||
--color=always \
|
--color=always \
|
||||||
@@ -156,6 +167,7 @@ env DIRENV_DEVSHELL=all-features \
|
|||||||
direnv exec . \
|
direnv exec . \
|
||||||
cargo test \
|
cargo test \
|
||||||
--workspace \
|
--workspace \
|
||||||
|
--profile test \
|
||||||
--all-targets \
|
--all-targets \
|
||||||
--all-features \
|
--all-features \
|
||||||
--color=always \
|
--color=always \
|
||||||
@@ -169,6 +181,7 @@ group = "tests"
|
|||||||
script = """
|
script = """
|
||||||
cargo test \
|
cargo test \
|
||||||
--workspace \
|
--workspace \
|
||||||
|
--profile test \
|
||||||
--all-targets \
|
--all-targets \
|
||||||
--color=always \
|
--color=always \
|
||||||
-- \
|
-- \
|
||||||
@@ -184,5 +197,12 @@ cargo test \
|
|||||||
name = "nix-default"
|
name = "nix-default"
|
||||||
group = "tests"
|
group = "tests"
|
||||||
script = """
|
script = """
|
||||||
nix run .#default -- --help
|
env DIRENV_DEVSHELL=dynamic \
|
||||||
|
CARGO_PROFILE="test" \
|
||||||
|
direnv exec . \
|
||||||
|
bin/nix-build-and-cache just .#default-test
|
||||||
|
env DIRENV_DEVSHELL=dynamic \
|
||||||
|
CARGO_PROFILE="test" \
|
||||||
|
direnv exec . \
|
||||||
|
nix run -L .#default-test -- --help && nix run -L .#default-test -- --version
|
||||||
"""
|
"""
|
||||||
|
|||||||
Generated
+25
-25
@@ -9,11 +9,11 @@
|
|||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1717279440,
|
"lastModified": 1720542474,
|
||||||
"narHash": "sha256-kH04ReTjxOpQumgWnqy40vvQLSnLGxWP6RF3nq5Esrk=",
|
"narHash": "sha256-aKjJ/4l2I9+wNGTaOGRsuS3M1+IoTibqgEMPDikXm04=",
|
||||||
"owner": "zhaofengli",
|
"owner": "zhaofengli",
|
||||||
"repo": "attic",
|
"repo": "attic",
|
||||||
"rev": "717cc95983cdc357bc347d70be20ced21f935843",
|
"rev": "6139576a3ce6bb992e0f6c3022528ec233e45f00",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -81,11 +81,11 @@
|
|||||||
"complement": {
|
"complement": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1719903368,
|
"lastModified": 1720637557,
|
||||||
"narHash": "sha256-PPzgxM4Bir+Zh9FUV/v+RBxEYeJxYVmi/BYo3uqt268=",
|
"narHash": "sha256-oZz6nCmFmdJZpC+K1iOG2KkzTI6rlAmndxANPDVU7X0=",
|
||||||
"owner": "matrix-org",
|
"owner": "matrix-org",
|
||||||
"repo": "complement",
|
"repo": "complement",
|
||||||
"rev": "bc97f1ddc1cd7485faf80c8935ee2641f3e1b57c",
|
"rev": "0d14432e010482ea9e13a6f7c47c1533c0c9d62f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -123,11 +123,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716569590,
|
"lastModified": 1720546058,
|
||||||
"narHash": "sha256-5eDbq8TuXFGGO3mqJFzhUbt5zHVTf5zilQoyW5jnJwo=",
|
"narHash": "sha256-iU2yVaPIZm5vMGdlT0+57vdB/aPq/V5oZFBRwYw+HBM=",
|
||||||
"owner": "ipetkov",
|
"owner": "ipetkov",
|
||||||
"repo": "crane",
|
"repo": "crane",
|
||||||
"rev": "109987da061a1bf452f435f1653c47511587d919",
|
"rev": "2d83156f23c43598cf44e152c33a59d3892f8b29",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -209,11 +209,11 @@
|
|||||||
"rust-analyzer-src": "rust-analyzer-src"
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716359173,
|
"lastModified": 1720852044,
|
||||||
"narHash": "sha256-pYcjP6Gy7i6jPWrjiWAVV0BCQp+DdmGaI/k65lBb/kM=",
|
"narHash": "sha256-3NBYz8VuXuKU+8ONd9NFafCNjPEGHIZQ2Mdoam1a4mY=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "fenix",
|
"repo": "fenix",
|
||||||
"rev": "b6fc5035b28e36a98370d0eac44f4ef3fd323df6",
|
"rev": "5087b12a595ee73131a944d922f24d81dae05725",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -381,11 +381,11 @@
|
|||||||
"liburing": {
|
"liburing": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1719025212,
|
"lastModified": 1720798442,
|
||||||
"narHash": "sha256-kD0yhjNStqC6uFqC1AxBwUpc/HlSFtiKrV+gwDyroDc=",
|
"narHash": "sha256-gtPppAoksMLW4GuruQ36nf4EAqIA1Bs6V9Xcx8dBxrQ=",
|
||||||
"owner": "axboe",
|
"owner": "axboe",
|
||||||
"repo": "liburing",
|
"repo": "liburing",
|
||||||
"rev": "7b3245583069bd481190c9da18f22e9fc8c3a805",
|
"rev": "1d674f83b7d0f07553ac44d99a401b05853d9dbe",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -606,11 +606,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716330097,
|
"lastModified": 1720768451,
|
||||||
"narHash": "sha256-8BO3B7e3BiyIDsaKA0tY8O88rClYRTjvAp66y+VBUeU=",
|
"narHash": "sha256-EYekUHJE2gxeo2pM/zM9Wlqw1Uw2XTJXOSAO79ksc4Y=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "5710852ba686cc1fd0d3b8e22b3117d43ba374c2",
|
"rev": "7e7c39ea35c5cdd002cd4588b03a3fb9ece6fad9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -673,16 +673,16 @@
|
|||||||
"rocksdb": {
|
"rocksdb": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1719949653,
|
"lastModified": 1720900786,
|
||||||
"narHash": "sha256-DYx7XHH2GEh17GukKhXs6laM6l+eugCmRkF0adpi9wk=",
|
"narHash": "sha256-Vta9Um/RRuWwZ46BjXftV06iWLm/j/9MX39emXUvSAY=",
|
||||||
"owner": "girlbossceo",
|
"owner": "girlbossceo",
|
||||||
"repo": "rocksdb",
|
"repo": "rocksdb",
|
||||||
"rev": "a935c0273e1ba44eacf88ce3685a9b9831486155",
|
"rev": "911f4243e69c2e320a7a209bf1f5f3ff5f825495",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "girlbossceo",
|
"owner": "girlbossceo",
|
||||||
"ref": "v9.3.1",
|
"ref": "v9.4.0",
|
||||||
"repo": "rocksdb",
|
"repo": "rocksdb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -705,11 +705,11 @@
|
|||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716107283,
|
"lastModified": 1720717809,
|
||||||
"narHash": "sha256-NJgrwLiLGHDrCia5AeIvZUHUY7xYGVryee0/9D3Ir1I=",
|
"narHash": "sha256-6I+fm+nTLF/iaj7ffiFGlSY7POmubwUaPA/Wq0Bm53M=",
|
||||||
"owner": "rust-lang",
|
"owner": "rust-lang",
|
||||||
"repo": "rust-analyzer",
|
"repo": "rust-analyzer",
|
||||||
"rev": "21ec8f523812b88418b2bfc64240c62b3dd967bd",
|
"rev": "ffbc5ad993d5cd2f3b8bcf9a511165470944ab91",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
flake-utils.url = "github:numtide/flake-utils?ref=main";
|
flake-utils.url = "github:numtide/flake-utils?ref=main";
|
||||||
nix-filter.url = "github:numtide/nix-filter?ref=main";
|
nix-filter.url = "github:numtide/nix-filter?ref=main";
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
||||||
# https://github.com/girlbossceo/rocksdb/commit/db6df0b185774778457dabfcbd822cb81760cade
|
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.4.0"; flake = false; };
|
||||||
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.3.1"; flake = false; };
|
|
||||||
liburing = { url = "github:axboe/liburing?ref=master"; flake = false; };
|
liburing = { url = "github:axboe/liburing?ref=master"; flake = false; };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@
|
|||||||
file = ./rust-toolchain.toml;
|
file = ./rust-toolchain.toml;
|
||||||
|
|
||||||
# See also `rust-toolchain.toml`
|
# See also `rust-toolchain.toml`
|
||||||
sha256 = "sha256-+syqAd2kX8KVa8/U2gz3blIQTTsYYt3U63xBWaGOSc8";
|
sha256 = "sha256-6eN/GKzjVSjEhGO9FhWObkRFaE1Jf+uqMSdQnb8lcB4=";
|
||||||
};
|
};
|
||||||
|
|
||||||
mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
|
mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
|
||||||
@@ -42,6 +41,37 @@
|
|||||||
"v"
|
"v"
|
||||||
(builtins.fromJSON (builtins.readFile ./flake.lock))
|
(builtins.fromJSON (builtins.readFile ./flake.lock))
|
||||||
.nodes.rocksdb.original.ref;
|
.nodes.rocksdb.original.ref;
|
||||||
|
# we have this already at https://github.com/girlbossceo/rocksdb/commit/a935c0273e1ba44eacf88ce3685a9b9831486155
|
||||||
|
# unsetting this so i don't have to revert it and make this nix exclusive
|
||||||
|
patches = [];
|
||||||
|
cmakeFlags = pkgs.lib.subtractLists
|
||||||
|
[
|
||||||
|
# no real reason to have snappy, no one uses this
|
||||||
|
"-DWITH_SNAPPY=1"
|
||||||
|
# we dont need to use ldb or sst_dump (core_tools)
|
||||||
|
"-DWITH_CORE_TOOLS=1"
|
||||||
|
# we dont need to build rocksdb tests
|
||||||
|
"-DWITH_TESTS=1"
|
||||||
|
# we use rust-rocksdb via C interface and dont need C++ RTTI
|
||||||
|
"-DUSE_RTTI=1"
|
||||||
|
]
|
||||||
|
old.cmakeFlags
|
||||||
|
++ [
|
||||||
|
# we dont need to use ldb or sst_dump (core_tools)
|
||||||
|
"-DWITH_CORE_TOOLS=0"
|
||||||
|
# we dont need trace tools
|
||||||
|
"-DWITH_TRACE_TOOLS=0"
|
||||||
|
# we dont need to build rocksdb tests
|
||||||
|
"-DWITH_TESTS=0"
|
||||||
|
# we use rust-rocksdb via C interface and dont need C++ RTTI
|
||||||
|
"-DUSE_RTTI=0"
|
||||||
|
];
|
||||||
|
|
||||||
|
# outputs has "tools" which we dont need or use
|
||||||
|
outputs = [ "out" ];
|
||||||
|
|
||||||
|
# preInstall hooks has stuff for messing with ldb/sst_dump which we dont need or use
|
||||||
|
preInstall = "";
|
||||||
});
|
});
|
||||||
# TODO: remove once https://github.com/NixOS/nixpkgs/pull/314945 is available
|
# TODO: remove once https://github.com/NixOS/nixpkgs/pull/314945 is available
|
||||||
liburing = pkgs.liburing.overrideAttrs (old: {
|
liburing = pkgs.liburing.overrideAttrs (old: {
|
||||||
@@ -50,16 +80,6 @@
|
|||||||
configureFlags = pkgs.lib.subtractLists
|
configureFlags = pkgs.lib.subtractLists
|
||||||
[ "--enable-static" "--disable-shared" ]
|
[ "--enable-static" "--disable-shared" ]
|
||||||
old.configureFlags;
|
old.configureFlags;
|
||||||
|
|
||||||
postInstall = old.postInstall + ''
|
|
||||||
# we remove the extra outputs
|
|
||||||
#
|
|
||||||
# we need to do this to prevent rocksdb from trying to link the
|
|
||||||
# static library in a dynamic stdenv
|
|
||||||
rm $out/lib/liburing*${
|
|
||||||
if pkgs.stdenv.hostPlatform.isStatic then ".so*" else ".a"
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -99,6 +119,9 @@
|
|||||||
# Needed for producing Debian packages
|
# Needed for producing Debian packages
|
||||||
cargo-deb
|
cargo-deb
|
||||||
|
|
||||||
|
# Needed for CI to check validity of produced Debian packages (dpkg-deb)
|
||||||
|
dpkg
|
||||||
|
|
||||||
# Needed for Complement
|
# Needed for Complement
|
||||||
go
|
go
|
||||||
|
|
||||||
@@ -113,6 +136,8 @@
|
|||||||
|
|
||||||
# Useful for editing the book locally
|
# Useful for editing the book locally
|
||||||
mdbook
|
mdbook
|
||||||
|
|
||||||
|
sccache
|
||||||
])
|
])
|
||||||
++ scope.main.buildInputs
|
++ scope.main.buildInputs
|
||||||
++ scope.main.propagatedBuildInputs
|
++ scope.main.propagatedBuildInputs
|
||||||
@@ -124,9 +149,64 @@
|
|||||||
{
|
{
|
||||||
packages = {
|
packages = {
|
||||||
default = scopeHost.main;
|
default = scopeHost.main;
|
||||||
|
default-debug = scopeHost.main.override {
|
||||||
|
profile = "dev";
|
||||||
|
# debug build users expect full logs
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
};
|
||||||
|
default-test = scopeHost.main.override {
|
||||||
|
profile = "test";
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
};
|
||||||
|
all-features = scopeHost.main.override {
|
||||||
|
all_features = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
all-features-debug = scopeHost.main.override {
|
||||||
|
profile = "dev";
|
||||||
|
all_features = true;
|
||||||
|
# debug build users expect full logs
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
|
};
|
||||||
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
|
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
|
||||||
|
|
||||||
oci-image = scopeHost.oci-image;
|
oci-image = scopeHost.oci-image;
|
||||||
|
oci-image-all-features = scopeHost.oci-image.override {
|
||||||
|
main = scopeHost.main.override {
|
||||||
|
all_features = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
oci-image-all-features-debug = scopeHost.oci-image.override {
|
||||||
|
main = scopeHost.main.override {
|
||||||
|
profile = "dev";
|
||||||
|
all_features = true;
|
||||||
|
# debug build users expect full logs
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
oci-image-hmalloc = scopeHost.oci-image.override {
|
oci-image-hmalloc = scopeHost.oci-image.override {
|
||||||
main = scopeHost.main.override {
|
main = scopeHost.main.override {
|
||||||
features = ["hardened_malloc"];
|
features = ["hardened_malloc"];
|
||||||
@@ -161,6 +241,57 @@
|
|||||||
value = scopeCrossStatic.main;
|
value = scopeCrossStatic.main;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# An output for a statically-linked unstripped debug ("dev") binary
|
||||||
|
{
|
||||||
|
name = "${binaryName}-debug";
|
||||||
|
value = scopeCrossStatic.main.override {
|
||||||
|
profile = "dev";
|
||||||
|
# debug build users expect full logs
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# An output for a statically-linked unstripped debug binary with the
|
||||||
|
# "test" profile (for CI usage only)
|
||||||
|
{
|
||||||
|
name = "${binaryName}-test";
|
||||||
|
value = scopeCrossStatic.main.override {
|
||||||
|
profile = "test";
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# An output for a statically-linked binary with `--all-features`
|
||||||
|
{
|
||||||
|
name = "${binaryName}-all-features";
|
||||||
|
value = scopeCrossStatic.main.override {
|
||||||
|
all_features = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# An output for a statically-linked unstripped debug ("dev") binary with `--all-features`
|
||||||
|
{
|
||||||
|
name = "${binaryName}-all-features-debug";
|
||||||
|
value = scopeCrossStatic.main.override {
|
||||||
|
profile = "dev";
|
||||||
|
all_features = true;
|
||||||
|
# debug build users expect full logs
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
# An output for a statically-linked binary with hardened_malloc
|
# An output for a statically-linked binary with hardened_malloc
|
||||||
{
|
{
|
||||||
name = "${binaryName}-hmalloc";
|
name = "${binaryName}-hmalloc";
|
||||||
@@ -175,6 +306,53 @@
|
|||||||
value = scopeCrossStatic.oci-image;
|
value = scopeCrossStatic.oci-image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# An output for an OCI image based on that unstripped debug ("dev") binary
|
||||||
|
{
|
||||||
|
name = "oci-image-${crossSystem}-debug";
|
||||||
|
value = scopeCrossStatic.oci-image.override {
|
||||||
|
main = scopeCrossStatic.main.override {
|
||||||
|
profile = "dev";
|
||||||
|
# debug build users expect full logs
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# An output for an OCI image based on that binary with `--all-features`
|
||||||
|
{
|
||||||
|
name = "oci-image-${crossSystem}-all-features";
|
||||||
|
value = scopeCrossStatic.oci-image.override {
|
||||||
|
main = scopeCrossStatic.main.override {
|
||||||
|
all_features = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# An output for an OCI image based on that unstripped debug ("dev") binary with `--all-features`
|
||||||
|
{
|
||||||
|
name = "oci-image-${crossSystem}-all-features-debug";
|
||||||
|
value = scopeCrossStatic.oci-image.override {
|
||||||
|
main = scopeCrossStatic.main.override {
|
||||||
|
profile = "dev";
|
||||||
|
all_features = true;
|
||||||
|
# debug build users expect full logs
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
# An output for an OCI image based on that binary with hardened_malloc
|
# An output for an OCI image based on that binary with hardened_malloc
|
||||||
{
|
{
|
||||||
name = "oci-image-${crossSystem}-hmalloc";
|
name = "oci-image-${crossSystem}-hmalloc";
|
||||||
@@ -196,7 +374,15 @@
|
|||||||
devShells.default = mkDevShell scopeHostStatic;
|
devShells.default = mkDevShell scopeHostStatic;
|
||||||
devShells.all-features = mkDevShell
|
devShells.all-features = mkDevShell
|
||||||
(scopeHostStatic.overrideScope (final: prev: {
|
(scopeHostStatic.overrideScope (final: prev: {
|
||||||
main = prev.main.override { all_features = true; };
|
main = prev.main.override {
|
||||||
|
all_features = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
|
};
|
||||||
}));
|
}));
|
||||||
devShells.no-features = mkDevShell
|
devShells.no-features = mkDevShell
|
||||||
(scopeHostStatic.overrideScope (final: prev: {
|
(scopeHostStatic.overrideScope (final: prev: {
|
||||||
|
|||||||
@@ -5,13 +5,17 @@ allow_guest_registration = true
|
|||||||
allow_public_room_directory_over_federation = true
|
allow_public_room_directory_over_federation = true
|
||||||
allow_public_room_directory_without_auth = true
|
allow_public_room_directory_without_auth = true
|
||||||
allow_registration = true
|
allow_registration = true
|
||||||
allow_unstable_room_versions = true
|
|
||||||
database_backend = "rocksdb"
|
|
||||||
database_path = "/database"
|
database_path = "/database"
|
||||||
log = "trace"
|
log = "trace,h2=warn,hyper=warn"
|
||||||
port = [8008, 8448]
|
port = [8008, 8448]
|
||||||
trusted_servers = []
|
trusted_servers = []
|
||||||
|
query_trusted_key_servers_first = false
|
||||||
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
||||||
|
ip_range_denylist = []
|
||||||
|
url_preview_domain_contains_allowlist = ["*"]
|
||||||
|
media_compat_file_link = false
|
||||||
|
media_startup_check = false
|
||||||
|
rocksdb_direct_io = false
|
||||||
|
|
||||||
[global.tls]
|
[global.tls]
|
||||||
certs = "/certificate.crt"
|
certs = "/certificate.crt"
|
||||||
|
|||||||
@@ -14,8 +14,15 @@
|
|||||||
|
|
||||||
let
|
let
|
||||||
main' = main.override {
|
main' = main.override {
|
||||||
profile = "dev";
|
profile = "test";
|
||||||
features = ["axum_dual_protocol"];
|
all_features = true;
|
||||||
|
disable_release_max_log_level = true;
|
||||||
|
disable_features = [
|
||||||
|
# this is non-functional on nix for some reason
|
||||||
|
"hardened_malloc"
|
||||||
|
# dont include experimental features
|
||||||
|
"experimental"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
start = writeShellScriptBin "start" ''
|
start = writeShellScriptBin "start" ''
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ lib.optionalAttrs stdenv.hostPlatform.isStatic {
|
|||||||
lib.concatStringsSep
|
lib.concatStringsSep
|
||||||
" "
|
" "
|
||||||
([]
|
([]
|
||||||
|
++ lib.optionals
|
||||||
|
stdenv.targetPlatform.isx86_64
|
||||||
|
[ "-C" "target-cpu=x86-64-v2" ]
|
||||||
|
++ lib.optionals
|
||||||
|
stdenv.targetPlatform.isAarch64
|
||||||
|
[ "-C" "target-cpu=cortex-a55" ] # cortex-a55 == ARMv8.2-a
|
||||||
# This disables PIE for static builds, which isn't great in terms
|
# This disables PIE for static builds, which isn't great in terms
|
||||||
# of security. Unfortunately, my hand is forced because nixpkgs'
|
# of security. Unfortunately, my hand is forced because nixpkgs'
|
||||||
# `libstdc++.a` is built without `-fPIE`, which precludes us from
|
# `libstdc++.a` is built without `-fPIE`, which precludes us from
|
||||||
|
|||||||
+38
-10
@@ -25,11 +25,7 @@ let
|
|||||||
# on the nix side depend on feature values.
|
# on the nix side depend on feature values.
|
||||||
crateFeatures = path:
|
crateFeatures = path:
|
||||||
let manifest = lib.importTOML "${path}/Cargo.toml"; in
|
let manifest = lib.importTOML "${path}/Cargo.toml"; in
|
||||||
lib.remove "default" (lib.attrNames manifest.features) ++
|
lib.remove "default" (lib.attrNames manifest.features);
|
||||||
lib.attrNames
|
|
||||||
(lib.filterAttrs
|
|
||||||
(_: dependency: dependency.optional or false)
|
|
||||||
manifest.dependencies);
|
|
||||||
crateDefaultFeatures = path:
|
crateDefaultFeatures = path:
|
||||||
(lib.importTOML "${path}/Cargo.toml").features.default;
|
(lib.importTOML "${path}/Cargo.toml").features.default;
|
||||||
allDefaultFeatures = crateDefaultFeatures "${inputs.self}/src/main";
|
allDefaultFeatures = crateDefaultFeatures "${inputs.self}/src/main";
|
||||||
@@ -43,7 +39,7 @@ features'' = lib.subtractLists disable_features' features';
|
|||||||
|
|
||||||
featureEnabled = feature : builtins.elem feature features'';
|
featureEnabled = feature : builtins.elem feature features'';
|
||||||
|
|
||||||
enableLiburing = featureEnabled "io_uring" && stdenv.isLinux;
|
enableLiburing = featureEnabled "io_uring" && !stdenv.isDarwin;
|
||||||
|
|
||||||
# This derivation will set the JEMALLOC_OVERRIDE variable, causing the
|
# This derivation will set the JEMALLOC_OVERRIDE variable, causing the
|
||||||
# tikv-jemalloc-sys crate to use the nixpkgs jemalloc instead of building it's
|
# tikv-jemalloc-sys crate to use the nixpkgs jemalloc instead of building it's
|
||||||
@@ -55,6 +51,10 @@ rust-jemalloc-sys' = (rust-jemalloc-sys.override {
|
|||||||
unprefixed = true;
|
unprefixed = true;
|
||||||
}).overrideAttrs (old: {
|
}).overrideAttrs (old: {
|
||||||
configureFlags = old.configureFlags ++
|
configureFlags = old.configureFlags ++
|
||||||
|
# we dont need docs
|
||||||
|
[ "--disable-doc" ] ++
|
||||||
|
# we dont need cxx/C++ integration
|
||||||
|
[ "--disable-cxx" ] ++
|
||||||
# tikv-jemalloc-sys/profiling feature
|
# tikv-jemalloc-sys/profiling feature
|
||||||
lib.optional (featureEnabled "jemalloc_prof") "--enable-prof";
|
lib.optional (featureEnabled "jemalloc_prof") "--enable-prof";
|
||||||
});
|
});
|
||||||
@@ -70,12 +70,34 @@ buildDepsOnlyEnv =
|
|||||||
#
|
#
|
||||||
# [1]: https://github.com/tikv/jemallocator/blob/ab0676d77e81268cd09b059260c75b38dbef2d51/jemalloc-sys/src/env.rs#L17
|
# [1]: https://github.com/tikv/jemallocator/blob/ab0676d77e81268cd09b059260c75b38dbef2d51/jemalloc-sys/src/env.rs#L17
|
||||||
enableJemalloc = featureEnabled "jemalloc" && !stdenv.isDarwin;
|
enableJemalloc = featureEnabled "jemalloc" && !stdenv.isDarwin;
|
||||||
|
|
||||||
|
# for some reason enableLiburing in nixpkgs rocksdb is default true
|
||||||
|
# which breaks Darwin entirely
|
||||||
|
enableLiburing = enableLiburing;
|
||||||
}).overrideAttrs (old: {
|
}).overrideAttrs (old: {
|
||||||
# TODO: static rocksdb fails to build on darwin
|
# TODO: static rocksdb fails to build on darwin, also see <https://github.com/NixOS/nixpkgs/issues/320448>
|
||||||
# build log at <https://girlboss.ceo/~strawberry/pb/JjGH>
|
# build log at <https://girlboss.ceo/~strawberry/pb/JjGH>
|
||||||
meta.broken = stdenv.hostPlatform.isStatic && stdenv.isDarwin;
|
meta.broken = stdenv.hostPlatform.isStatic && stdenv.isDarwin;
|
||||||
# TODO: switch to enableUring option once https://github.com/NixOS/nixpkgs/pull/314945 is available
|
|
||||||
buildInputs = old.buildInputs ++ lib.optional enableLiburing liburing;
|
enableLiburing = enableLiburing;
|
||||||
|
|
||||||
|
sse42Support = stdenv.targetPlatform.isx86_64;
|
||||||
|
|
||||||
|
cmakeFlags = if stdenv.targetPlatform.isx86_64
|
||||||
|
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
|
||||||
|
++ lib.optionals stdenv.targetPlatform.isx86_64 [
|
||||||
|
"-DPORTABLE=x86-64-v2"
|
||||||
|
"-DUSE_SSE=1"
|
||||||
|
"-DHAVE_SSE=1"
|
||||||
|
"-DHAVE_SSE42=1"
|
||||||
|
]
|
||||||
|
else if stdenv.targetPlatform.isAarch64
|
||||||
|
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
|
||||||
|
++ lib.optionals stdenv.targetPlatform.isAarch64 [
|
||||||
|
# cortex-a55 == ARMv8.2-a
|
||||||
|
"-DPORTABLE=armv8.2-a"
|
||||||
|
]
|
||||||
|
else old.cmakeFlags;
|
||||||
});
|
});
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -102,7 +124,11 @@ buildPackageEnv = {
|
|||||||
# Only needed in static stdenv because these are transitive dependencies of rocksdb
|
# Only needed in static stdenv because these are transitive dependencies of rocksdb
|
||||||
CARGO_BUILD_RUSTFLAGS = buildDepsOnlyEnv.CARGO_BUILD_RUSTFLAGS
|
CARGO_BUILD_RUSTFLAGS = buildDepsOnlyEnv.CARGO_BUILD_RUSTFLAGS
|
||||||
+ lib.optionalString (enableLiburing && stdenv.hostPlatform.isStatic)
|
+ lib.optionalString (enableLiburing && stdenv.hostPlatform.isStatic)
|
||||||
" -L${lib.getLib liburing}/lib -luring";
|
" -L${lib.getLib liburing}/lib -luring"
|
||||||
|
+ lib.optionalString stdenv.targetPlatform.isx86_64
|
||||||
|
" -Ctarget-cpu=x86-64-v2"
|
||||||
|
+ lib.optionalString stdenv.targetPlatform.isAarch64
|
||||||
|
" -Ctarget-cpu=cortex-a55"; # cortex-a55 == ARMv8.2-a
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -127,6 +153,8 @@ commonAttrs = {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dontStrip = profile == "dev" || profile == "test";
|
||||||
|
|
||||||
buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
|
buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
# If you're having trouble making the relevant changes, bug a maintainer.
|
# If you're having trouble making the relevant changes, bug a maintainer.
|
||||||
|
|
||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.77.0"
|
channel = "1.80.0"
|
||||||
components = [
|
components = [
|
||||||
# For rust-analyzer
|
# For rust-analyzer
|
||||||
"rust-src",
|
"rust-src",
|
||||||
|
|||||||
@@ -29,15 +29,13 @@ release_max_log_level = [
|
|||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
conduit-api.workspace = true
|
conduit-api.workspace = true
|
||||||
conduit-core.workspace = true
|
conduit-core.workspace = true
|
||||||
conduit-database.workspace = true
|
conduit-macros.workspace = true
|
||||||
conduit-service.workspace = true
|
conduit-service.workspace = true
|
||||||
|
const-str.workspace = true
|
||||||
futures-util.workspace = true
|
futures-util.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
loole.workspace = true
|
|
||||||
regex.workspace = true
|
|
||||||
ruma.workspace = true
|
ruma.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
serde.workspace = true
|
|
||||||
serde_yaml.workspace = true
|
serde_yaml.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
use conduit::Result;
|
||||||
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
appservice, appservice::AppserviceCommand, check, check::CheckCommand, command::Command, debug,
|
||||||
|
debug::DebugCommand, federation, federation::FederationCommand, media, media::MediaCommand, query,
|
||||||
|
query::QueryCommand, room, room::RoomCommand, server, server::ServerCommand, user, user::UserCommand,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
|
||||||
|
pub(super) enum AdminCommand {
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// - Commands for managing appservices
|
||||||
|
Appservices(AppserviceCommand),
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// - Commands for managing local users
|
||||||
|
Users(UserCommand),
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// - Commands for managing rooms
|
||||||
|
Rooms(RoomCommand),
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// - Commands for managing federation
|
||||||
|
Federation(FederationCommand),
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// - Commands for managing the server
|
||||||
|
Server(ServerCommand),
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// - Commands for managing media
|
||||||
|
Media(MediaCommand),
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// - Commands for checking integrity
|
||||||
|
Check(CheckCommand),
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// - Commands for debugging things
|
||||||
|
Debug(DebugCommand),
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// - Low-level queries for database getters and iterators
|
||||||
|
Query(QueryCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, name = "command")]
|
||||||
|
pub(super) async fn process(command: AdminCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
use AdminCommand::*;
|
||||||
|
|
||||||
|
Ok(match command {
|
||||||
|
Appservices(command) => appservice::process(command, context).await?,
|
||||||
|
Media(command) => media::process(command, context).await?,
|
||||||
|
Users(command) => user::process(command, context).await?,
|
||||||
|
Rooms(command) => room::process(command, context).await?,
|
||||||
|
Federation(command) => federation::process(command, context).await?,
|
||||||
|
Server(command) => server::process(command, context).await?,
|
||||||
|
Debug(command) => debug::process(command, context).await?,
|
||||||
|
Query(command) => query::process(command, context).await?,
|
||||||
|
Check(command) => check::process(command, context).await?,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,18 +1,20 @@
|
|||||||
use ruma::{api::appservice::Registration, events::room::message::RoomMessageEventContent};
|
use ruma::{api::appservice::Registration, events::room::message::RoomMessageEventContent};
|
||||||
|
|
||||||
use crate::{services, Result};
|
use crate::{admin_command, Result};
|
||||||
|
|
||||||
pub(super) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
pub(super) async fn register(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let appservice_config = body[1..body.len().checked_sub(1).unwrap()].join("\n");
|
let appservice_config = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
||||||
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
|
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
|
||||||
match parsed_config {
|
match parsed_config {
|
||||||
Ok(yaml) => match services().appservice.register_appservice(yaml).await {
|
Ok(yaml) => match self.services.appservice.register_appservice(yaml).await {
|
||||||
Ok(id) => Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(id) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Appservice registered with ID: {id}."
|
"Appservice registered with ID: {id}."
|
||||||
))),
|
))),
|
||||||
@@ -26,8 +28,10 @@ pub(super) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn unregister(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
match services()
|
pub(super) async fn unregister(&self, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||||
|
match self
|
||||||
|
.services
|
||||||
.appservice
|
.appservice
|
||||||
.unregister_appservice(&appservice_identifier)
|
.unregister_appservice(&appservice_identifier)
|
||||||
.await
|
.await
|
||||||
@@ -39,8 +43,10 @@ pub(super) async fn unregister(_body: Vec<&str>, appservice_identifier: String)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn show(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
match services()
|
pub(super) async fn show_appservice_config(&self, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||||
|
match self
|
||||||
|
.services
|
||||||
.appservice
|
.appservice
|
||||||
.get_registration(&appservice_identifier)
|
.get_registration(&appservice_identifier)
|
||||||
.await
|
.await
|
||||||
@@ -54,8 +60,9 @@ pub(super) async fn show(_body: Vec<&str>, appservice_identifier: String) -> Res
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let appservices = services().appservice.iter_ids().await;
|
pub(super) async fn list_registered(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let appservices = self.services.appservice.iter_ids().await;
|
||||||
let output = format!("Appservices ({}): {}", appservices.len(), appservices.join(", "));
|
let output = format!("Appservices ({}): {}", appservices.len(), appservices.join(", "));
|
||||||
Ok(RoomMessageEventContent::text_plain(output))
|
Ok(RoomMessageEventContent::text_plain(output))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ mod commands;
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
|
||||||
|
|
||||||
use self::commands::*;
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[derive(Debug, Subcommand)]
|
||||||
#[derive(Subcommand)]
|
#[admin_command_dispatch]
|
||||||
pub(super) enum AppserviceCommand {
|
pub(super) enum AppserviceCommand {
|
||||||
/// - Register an appservice using its registration YAML
|
/// - Register an appservice using its registration YAML
|
||||||
///
|
///
|
||||||
@@ -29,24 +28,13 @@ pub(super) enum AppserviceCommand {
|
|||||||
/// - Show an appservice's config using its ID
|
/// - Show an appservice's config using its ID
|
||||||
///
|
///
|
||||||
/// You can find the ID using the `list-appservices` command.
|
/// You can find the ID using the `list-appservices` command.
|
||||||
Show {
|
#[clap(alias("show"))]
|
||||||
|
ShowAppserviceConfig {
|
||||||
/// The appservice to show
|
/// The appservice to show
|
||||||
appservice_identifier: String,
|
appservice_identifier: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// - List all the currently registered appservices
|
/// - List all the currently registered appservices
|
||||||
List,
|
#[clap(alias("list"))]
|
||||||
}
|
ListRegistered,
|
||||||
|
|
||||||
pub(super) async fn process(command: AppserviceCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
Ok(match command {
|
|
||||||
AppserviceCommand::Register => register(body).await?,
|
|
||||||
AppserviceCommand::Unregister {
|
|
||||||
appservice_identifier,
|
|
||||||
} => unregister(body, appservice_identifier).await?,
|
|
||||||
AppserviceCommand::Show {
|
|
||||||
appservice_identifier,
|
|
||||||
} => show(body, appservice_identifier).await?,
|
|
||||||
AppserviceCommand::List => list(body).await?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
|
use conduit_macros::implement;
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
use crate::services;
|
use crate::Command;
|
||||||
|
|
||||||
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
|
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
|
||||||
/// every user in our database (remote and local). Reports total count, any
|
/// every user in our database (remote and local). Reports total count, any
|
||||||
/// errors if there were any, etc
|
/// errors if there were any, etc
|
||||||
pub(super) async fn check_all_users(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[implement(Command, params = "<'_>")]
|
||||||
|
pub(super) async fn check_all_users(&self) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().users.db.iter();
|
let results = self.services.users.db.iter();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
let users = results.collect::<Vec<_>>();
|
let users = results.collect::<Vec<_>>();
|
||||||
|
|||||||
@@ -4,16 +4,15 @@ use clap::Subcommand;
|
|||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
use self::commands::*;
|
use crate::Command;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[derive(Debug, Subcommand)]
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub(super) enum CheckCommand {
|
pub(super) enum CheckCommand {
|
||||||
AllUsers,
|
AllUsers,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: CheckCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(command: CheckCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
Ok(match command {
|
Ok(match command {
|
||||||
CheckCommand::AllUsers => check_all_users(body).await?,
|
CheckCommand::AllUsers => context.check_all_users().await?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
use service::Services;
|
||||||
|
|
||||||
|
pub(crate) struct Command<'a> {
|
||||||
|
pub(crate) services: &'a Services,
|
||||||
|
pub(crate) body: &'a [&'a str],
|
||||||
|
}
|
||||||
+284
-106
@@ -1,33 +1,37 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
|
fmt::Write,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
time::Instant,
|
time::{Instant, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use api::client::validate_and_add_event_id;
|
use api::client::validate_and_add_event_id;
|
||||||
use conduit::{
|
use conduit::{
|
||||||
debug, info, log,
|
debug, debug_error, err, info, log,
|
||||||
log::{capture, Capture},
|
log::{capture, Capture},
|
||||||
warn, Error, Result,
|
utils, warn, Error, PduEvent, Result,
|
||||||
};
|
};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{client::error::ErrorKind, federation::event::get_room_state},
|
api::{client::error::ErrorKind, federation::event::get_room_state},
|
||||||
events::room::message::RoomMessageEventContent,
|
events::room::message::RoomMessageEventContent,
|
||||||
CanonicalJsonObject, EventId, OwnedRoomOrAliasId, RoomId, RoomVersionId, ServerName,
|
CanonicalJsonObject, EventId, OwnedRoomOrAliasId, RoomId, RoomVersionId, ServerName,
|
||||||
};
|
};
|
||||||
use service::{rooms::event_handler::parse_incoming_pdu, sending::resolve_actual_dest, services, PduEvent};
|
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
pub(super) async fn echo(_body: Vec<&str>, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
use crate::admin_command;
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn echo(&self, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
||||||
let message = message.join(" ");
|
let message = message.join(" ");
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_plain(message))
|
Ok(RoomMessageEventContent::notice_plain(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn get_auth_chain(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn get_auth_chain(&self, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||||
let event_id = Arc::<EventId>::from(event_id);
|
let event_id = Arc::<EventId>::from(event_id);
|
||||||
if let Some(event) = services().rooms.timeline.get_pdu_json(&event_id)? {
|
if let Some(event) = self.services.rooms.timeline.get_pdu_json(&event_id)? {
|
||||||
let room_id_str = event
|
let room_id_str = event
|
||||||
.get("room_id")
|
.get("room_id")
|
||||||
.and_then(|val| val.as_str())
|
.and_then(|val| val.as_str())
|
||||||
@@ -35,13 +39,16 @@ pub(super) async fn get_auth_chain(_body: Vec<&str>, event_id: Box<EventId>) ->
|
|||||||
|
|
||||||
let room_id = <&RoomId>::try_from(room_id_str)
|
let room_id = <&RoomId>::try_from(room_id_str)
|
||||||
.map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
|
.map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let count = services()
|
let count = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.auth_chain
|
.auth_chain
|
||||||
.event_ids_iter(room_id, vec![event_id])
|
.event_ids_iter(room_id, vec![event_id])
|
||||||
.await?
|
.await?
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
let elapsed = start.elapsed();
|
let elapsed = start.elapsed();
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Loaded auth chain with length {count} in {elapsed:?}"
|
"Loaded auth chain with length {count} in {elapsed:?}"
|
||||||
@@ -51,14 +58,16 @@ pub(super) async fn get_auth_chain(_body: Vec<&str>, event_id: Box<EventId>) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn parse_pdu(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
pub(super) async fn parse_pdu(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let string = body[1..body.len() - 1].join("\n");
|
let string = self.body[1..self.body.len().saturating_sub(1)].join("\n");
|
||||||
match serde_json::from_str(&string) {
|
match serde_json::from_str(&string) {
|
||||||
Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
||||||
Ok(hash) => {
|
Ok(hash) => {
|
||||||
@@ -79,15 +88,17 @@ pub(super) async fn parse_pdu(body: Vec<&str>) -> Result<RoomMessageEventContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn get_pdu(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn get_pdu(&self, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||||
let mut outlier = false;
|
let mut outlier = false;
|
||||||
let mut pdu_json = services()
|
let mut pdu_json = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_non_outlier_pdu_json(&event_id)?;
|
.get_non_outlier_pdu_json(&event_id)?;
|
||||||
if pdu_json.is_none() {
|
if pdu_json.is_none() {
|
||||||
outlier = true;
|
outlier = true;
|
||||||
pdu_json = services().rooms.timeline.get_pdu_json(&event_id)?;
|
pdu_json = self.services.rooms.timeline.get_pdu_json(&event_id)?;
|
||||||
}
|
}
|
||||||
match pdu_json {
|
match pdu_json {
|
||||||
Some(json) => {
|
Some(json) => {
|
||||||
@@ -106,38 +117,42 @@ pub(super) async fn get_pdu(_body: Vec<&str>, event_id: Box<EventId>) -> Result<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
pub(super) async fn get_remote_pdu_list(
|
pub(super) async fn get_remote_pdu_list(
|
||||||
body: Vec<&str>, server: Box<ServerName>, force: bool,
|
&self, server: Box<ServerName>, force: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !services().globals.config.allow_federation {
|
if !self.services.globals.config.allow_federation {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Federation is disabled on this homeserver.",
|
"Federation is disabled on this homeserver.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if server == services().globals.server_name() {
|
if server == self.services.globals.server_name() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs from \
|
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs from \
|
||||||
the database.",
|
the database.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let list = body
|
let list = self
|
||||||
.clone()
|
.body
|
||||||
.drain(1..body.len().checked_sub(1).unwrap())
|
.iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.drain(1..self.body.len().saturating_sub(1))
|
||||||
.filter_map(|pdu| EventId::parse(pdu).ok())
|
.filter_map(|pdu| EventId::parse(pdu).ok())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for pdu in list {
|
for pdu in list {
|
||||||
if force {
|
if force {
|
||||||
if let Err(e) = get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await {
|
if let Err(e) = self.get_remote_pdu(Box::from(pdu), server.clone()).await {
|
||||||
services()
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to get remote PDU, ignoring error: {e}"
|
"Failed to get remote PDU, ignoring error: {e}"
|
||||||
@@ -146,29 +161,31 @@ pub(super) async fn get_remote_pdu_list(
|
|||||||
warn!(%e, "Failed to get remote PDU, ignoring error");
|
warn!(%e, "Failed to get remote PDU, ignoring error");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await?;
|
self.get_remote_pdu(Box::from(pdu), server.clone()).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain("Fetched list of remote PDUs."))
|
Ok(RoomMessageEventContent::text_plain("Fetched list of remote PDUs."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
pub(super) async fn get_remote_pdu(
|
pub(super) async fn get_remote_pdu(
|
||||||
_body: Vec<&str>, event_id: Box<EventId>, server: Box<ServerName>,
|
&self, event_id: Box<EventId>, server: Box<ServerName>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !services().globals.config.allow_federation {
|
if !self.services.globals.config.allow_federation {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Federation is disabled on this homeserver.",
|
"Federation is disabled on this homeserver.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if server == services().globals.server_name() {
|
if server == self.services.globals.server_name() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
|
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
match services()
|
match self
|
||||||
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
&server,
|
&server,
|
||||||
@@ -189,7 +206,11 @@ pub(super) async fn get_remote_pdu(
|
|||||||
|
|
||||||
debug!("Attempting to parse PDU: {:?}", &response.pdu);
|
debug!("Attempting to parse PDU: {:?}", &response.pdu);
|
||||||
let parsed_pdu = {
|
let parsed_pdu = {
|
||||||
let parsed_result = parse_incoming_pdu(&response.pdu);
|
let parsed_result = self
|
||||||
|
.services
|
||||||
|
.rooms
|
||||||
|
.event_handler
|
||||||
|
.parse_incoming_pdu(&response.pdu);
|
||||||
let (event_id, value, room_id) = match parsed_result {
|
let (event_id, value, room_id) = match parsed_result {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -207,9 +228,8 @@ pub(super) async fn get_remote_pdu(
|
|||||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||||
|
|
||||||
debug!("Attempting to fetch homeserver signing keys for {server}");
|
debug!("Attempting to fetch homeserver signing keys for {server}");
|
||||||
services()
|
self.services
|
||||||
.rooms
|
.server_keys
|
||||||
.event_handler
|
|
||||||
.fetch_required_signing_keys(parsed_pdu.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
|
.fetch_required_signing_keys(parsed_pdu.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
@@ -217,7 +237,7 @@ pub(super) async fn get_remote_pdu(
|
|||||||
});
|
});
|
||||||
|
|
||||||
info!("Attempting to handle event ID {event_id} as backfilled PDU");
|
info!("Attempting to handle event ID {event_id} as backfilled PDU");
|
||||||
services()
|
self.services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.backfill_pdu(&server, response.pdu, &pub_key_map)
|
.backfill_pdu(&server, response.pdu, &pub_key_map)
|
||||||
@@ -236,9 +256,11 @@ pub(super) async fn get_remote_pdu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn get_room_state(_body: Vec<&str>, room: OwnedRoomOrAliasId) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let room_id = services().rooms.alias.resolve(&room).await?;
|
pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result<RoomMessageEventContent> {
|
||||||
let room_state = services()
|
let room_id = self.services.rooms.alias.resolve(&room).await?;
|
||||||
|
let room_state = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_full(&room_id)
|
.room_state_full(&room_id)
|
||||||
@@ -263,8 +285,9 @@ pub(super) async fn get_room_state(_body: Vec<&str>, room: OwnedRoomOrAliasId) -
|
|||||||
Ok(RoomMessageEventContent::notice_markdown(format!("```json\n{json}\n```")))
|
Ok(RoomMessageEventContent::notice_markdown(format!("```json\n{json}\n```")))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
if server == services().globals.server_name() {
|
pub(super) async fn ping(&self, server: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
||||||
|
if server == self.services.globals.server_name() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to send federation requests to ourselves.",
|
"Not allowed to send federation requests to ourselves.",
|
||||||
));
|
));
|
||||||
@@ -272,7 +295,8 @@ pub(super) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<Ro
|
|||||||
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
|
|
||||||
match services()
|
match self
|
||||||
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(&server, ruma::api::federation::discovery::get_server_version::v1::Request {})
|
.send_federation_request(&server, ruma::api::federation::discovery::get_server_version::v1::Request {})
|
||||||
.await
|
.await
|
||||||
@@ -301,21 +325,23 @@ pub(super) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<Ro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn force_device_list_updates(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn force_device_list_updates(&self) -> Result<RoomMessageEventContent> {
|
||||||
// Force E2EE device list updates for all users
|
// Force E2EE device list updates for all users
|
||||||
for user_id in services().users.iter().filter_map(Result::ok) {
|
for user_id in self.services.users.iter().filter_map(Result::ok) {
|
||||||
services().users.mark_device_key_update(&user_id)?;
|
self.services.users.mark_device_key_update(&user_id)?;
|
||||||
}
|
}
|
||||||
Ok(RoomMessageEventContent::text_plain(
|
Ok(RoomMessageEventContent::text_plain(
|
||||||
"Marked all devices for all users as having new keys to update",
|
"Marked all devices for all users as having new keys to update",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn change_log_level(
|
#[admin_command]
|
||||||
_body: Vec<&str>, filter: Option<String>, reset: bool,
|
pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool) -> Result<RoomMessageEventContent> {
|
||||||
) -> Result<RoomMessageEventContent> {
|
let handles = &["console"];
|
||||||
|
|
||||||
if reset {
|
if reset {
|
||||||
let old_filter_layer = match EnvFilter::try_new(&services().globals.config.log) {
|
let old_filter_layer = match EnvFilter::try_new(&self.services.globals.config.log) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
@@ -324,11 +350,17 @@ pub(super) async fn change_log_level(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match services().server.log.reload.reload(&old_filter_layer) {
|
match self
|
||||||
|
.services
|
||||||
|
.server
|
||||||
|
.log
|
||||||
|
.reload
|
||||||
|
.reload(&old_filter_layer, Some(handles))
|
||||||
|
{
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Successfully changed log level back to config value {}",
|
"Successfully changed log level back to config value {}",
|
||||||
services().globals.config.log
|
self.services.globals.config.log
|
||||||
)));
|
)));
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -349,7 +381,13 @@ pub(super) async fn change_log_level(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match services().server.log.reload.reload(&new_filter_layer) {
|
match self
|
||||||
|
.services
|
||||||
|
.server
|
||||||
|
.log
|
||||||
|
.reload
|
||||||
|
.reload(&new_filter_layer, Some(handles))
|
||||||
|
{
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain("Successfully changed log level"));
|
return Ok(RoomMessageEventContent::text_plain("Successfully changed log level"));
|
||||||
},
|
},
|
||||||
@@ -364,19 +402,21 @@ pub(super) async fn change_log_level(
|
|||||||
Ok(RoomMessageEventContent::text_plain("No log level was specified."))
|
Ok(RoomMessageEventContent::text_plain("No log level was specified."))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
pub(super) async fn sign_json(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
|
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
||||||
match serde_json::from_str(&string) {
|
match serde_json::from_str(&string) {
|
||||||
Ok(mut value) => {
|
Ok(mut value) => {
|
||||||
ruma::signatures::sign_json(
|
ruma::signatures::sign_json(
|
||||||
services().globals.server_name().as_str(),
|
self.services.globals.server_name().as_str(),
|
||||||
services().globals.keypair(),
|
self.services.globals.keypair(),
|
||||||
&mut value,
|
&mut value,
|
||||||
)
|
)
|
||||||
.expect("our request json is what ruma expects");
|
.expect("our request json is what ruma expects");
|
||||||
@@ -387,21 +427,22 @@ pub(super) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
pub(super) async fn verify_json(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
|
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
||||||
match serde_json::from_str(&string) {
|
match serde_json::from_str(&string) {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||||
|
|
||||||
services()
|
self.services
|
||||||
.rooms
|
.server_keys
|
||||||
.event_handler
|
|
||||||
.fetch_required_signing_keys([&value], &pub_key_map)
|
.fetch_required_signing_keys([&value], &pub_key_map)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -417,19 +458,22 @@ pub(super) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventConte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(_body))]
|
#[admin_command]
|
||||||
pub(super) async fn first_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
#[tracing::instrument(skip(self))]
|
||||||
if !services()
|
pub(super) async fn first_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||||
|
if !self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.server_in_room(&services().globals.config.server_name, &room_id)?
|
.server_in_room(&self.services.globals.config.server_name, &room_id)?
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"We are not participating in the room / we don't know about the room ID.",
|
"We are not participating in the room / we don't know about the room ID.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let first_pdu = services()
|
let first_pdu = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.first_pdu_in_room(&room_id)?
|
.first_pdu_in_room(&room_id)?
|
||||||
@@ -438,19 +482,22 @@ pub(super) async fn first_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) ->
|
|||||||
Ok(RoomMessageEventContent::text_plain(format!("{first_pdu:?}")))
|
Ok(RoomMessageEventContent::text_plain(format!("{first_pdu:?}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(_body))]
|
#[admin_command]
|
||||||
pub(super) async fn latest_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
#[tracing::instrument(skip(self))]
|
||||||
if !services()
|
pub(super) async fn latest_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||||
|
if !self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.server_in_room(&services().globals.config.server_name, &room_id)?
|
.server_in_room(&self.services.globals.config.server_name, &room_id)?
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"We are not participating in the room / we don't know about the room ID.",
|
"We are not participating in the room / we don't know about the room ID.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let latest_pdu = services()
|
let latest_pdu = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.latest_pdu_in_room(&room_id)?
|
.latest_pdu_in_room(&room_id)?
|
||||||
@@ -459,32 +506,36 @@ pub(super) async fn latest_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -
|
|||||||
Ok(RoomMessageEventContent::text_plain(format!("{latest_pdu:?}")))
|
Ok(RoomMessageEventContent::text_plain(format!("{latest_pdu:?}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(_body))]
|
#[admin_command]
|
||||||
|
#[tracing::instrument(skip(self))]
|
||||||
pub(super) async fn force_set_room_state_from_server(
|
pub(super) async fn force_set_room_state_from_server(
|
||||||
_body: Vec<&str>, server_name: Box<ServerName>, room_id: Box<RoomId>,
|
&self, room_id: Box<RoomId>, server_name: Box<ServerName>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !services()
|
if !self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.server_in_room(&services().globals.config.server_name, &room_id)?
|
.server_in_room(&self.services.globals.config.server_name, &room_id)?
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"We are not participating in the room / we don't know about the room ID.",
|
"We are not participating in the room / we don't know about the room ID.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let first_pdu = services()
|
let first_pdu = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.latest_pdu_in_room(&room_id)?
|
.latest_pdu_in_room(&room_id)?
|
||||||
.ok_or_else(|| Error::bad_database("Failed to find the latest PDU in database"))?;
|
.ok_or_else(|| Error::bad_database("Failed to find the latest PDU in database"))?;
|
||||||
|
|
||||||
let room_version = services().rooms.state.get_room_version(&room_id)?;
|
let room_version = self.services.rooms.state.get_room_version(&room_id)?;
|
||||||
|
|
||||||
let mut state: HashMap<u64, Arc<EventId>> = HashMap::new();
|
let mut state: HashMap<u64, Arc<EventId>> = HashMap::new();
|
||||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||||
|
|
||||||
let remote_state_response = services()
|
let remote_state_response = self
|
||||||
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
&server_name,
|
&server_name,
|
||||||
@@ -498,7 +549,7 @@ pub(super) async fn force_set_room_state_from_server(
|
|||||||
let mut events = Vec::with_capacity(remote_state_response.pdus.len());
|
let mut events = Vec::with_capacity(remote_state_response.pdus.len());
|
||||||
|
|
||||||
for pdu in remote_state_response.pdus.clone() {
|
for pdu in remote_state_response.pdus.clone() {
|
||||||
events.push(match parse_incoming_pdu(&pdu) {
|
events.push(match self.services.rooms.event_handler.parse_incoming_pdu(&pdu) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Could not parse PDU, ignoring: {e}");
|
warn!("Could not parse PDU, ignoring: {e}");
|
||||||
@@ -508,9 +559,8 @@ pub(super) async fn force_set_room_state_from_server(
|
|||||||
}
|
}
|
||||||
|
|
||||||
info!("Fetching required signing keys for all the state events we got");
|
info!("Fetching required signing keys for all the state events we got");
|
||||||
services()
|
self.services
|
||||||
.rooms
|
.server_keys
|
||||||
.event_handler
|
|
||||||
.fetch_required_signing_keys(events.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
|
.fetch_required_signing_keys(events.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -518,23 +568,24 @@ pub(super) async fn force_set_room_state_from_server(
|
|||||||
for result in remote_state_response
|
for result in remote_state_response
|
||||||
.pdus
|
.pdus
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pdu| validate_and_add_event_id(pdu, &room_version, &pub_key_map))
|
.map(|pdu| validate_and_add_event_id(self.services, pdu, &room_version, &pub_key_map))
|
||||||
{
|
{
|
||||||
let Ok((event_id, value)) = result.await else {
|
let Ok((event_id, value)) = result.await else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| {
|
let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| {
|
||||||
warn!("Invalid PDU in fetching remote room state PDUs response: {} {:?}", e, value);
|
debug_error!("Invalid PDU in fetching remote room state PDUs response: {value:#?}");
|
||||||
Error::BadServerResponse("Invalid PDU in send_join response.")
|
err!(BadServerResponse(debug_error!("Invalid PDU in send_join response: {e:?}")))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
services()
|
self.services
|
||||||
.rooms
|
.rooms
|
||||||
.outlier
|
.outlier
|
||||||
.add_pdu_outlier(&event_id, &value)?;
|
.add_pdu_outlier(&event_id, &value)?;
|
||||||
if let Some(state_key) = &pdu.state_key {
|
if let Some(state_key) = &pdu.state_key {
|
||||||
let shortstatekey = services()
|
let shortstatekey = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.short
|
.short
|
||||||
.get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)?;
|
.get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)?;
|
||||||
@@ -546,32 +597,34 @@ pub(super) async fn force_set_room_state_from_server(
|
|||||||
for result in remote_state_response
|
for result in remote_state_response
|
||||||
.auth_chain
|
.auth_chain
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pdu| validate_and_add_event_id(pdu, &room_version, &pub_key_map))
|
.map(|pdu| validate_and_add_event_id(self.services, pdu, &room_version, &pub_key_map))
|
||||||
{
|
{
|
||||||
let Ok((event_id, value)) = result.await else {
|
let Ok((event_id, value)) = result.await else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
services()
|
self.services
|
||||||
.rooms
|
.rooms
|
||||||
.outlier
|
.outlier
|
||||||
.add_pdu_outlier(&event_id, &value)?;
|
.add_pdu_outlier(&event_id, &value)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_room_state = services()
|
let new_room_state = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.event_handler
|
.event_handler
|
||||||
.resolve_state(room_id.clone().as_ref(), &room_version, state)
|
.resolve_state(room_id.clone().as_ref(), &room_version, state)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
info!("Forcing new room state");
|
info!("Forcing new room state");
|
||||||
let (short_state_hash, new, removed) = services()
|
let (short_state_hash, new, removed) = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_compressor
|
.state_compressor
|
||||||
.save_state(room_id.clone().as_ref(), new_room_state)?;
|
.save_state(room_id.clone().as_ref(), new_room_state)?;
|
||||||
|
|
||||||
let state_lock = services().globals.roomid_mutex_state.lock(&room_id).await;
|
let state_lock = self.services.rooms.state.mutex.lock(&room_id).await;
|
||||||
services()
|
self.services
|
||||||
.rooms
|
.rooms
|
||||||
.state
|
.state
|
||||||
.force_state(room_id.clone().as_ref(), short_state_hash, new, removed, &state_lock)
|
.force_state(room_id.clone().as_ref(), short_state_hash, new, removed, &state_lock)
|
||||||
@@ -581,7 +634,10 @@ pub(super) async fn force_set_room_state_from_server(
|
|||||||
"Updating joined counts for room just in case (e.g. we may have found a difference in the room's \
|
"Updating joined counts for room just in case (e.g. we may have found a difference in the room's \
|
||||||
m.room.member state"
|
m.room.member state"
|
||||||
);
|
);
|
||||||
services().rooms.state_cache.update_joined_count(&room_id)?;
|
self.services
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.update_joined_count(&room_id)?;
|
||||||
|
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
|
|
||||||
@@ -590,16 +646,63 @@ pub(super) async fn force_set_room_state_from_server(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn resolve_true_destination(
|
#[admin_command]
|
||||||
_body: Vec<&str>, server_name: Box<ServerName>, no_cache: bool,
|
pub(super) async fn get_signing_keys(
|
||||||
|
&self, server_name: Option<Box<ServerName>>, _cached: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !services().globals.config.allow_federation {
|
let server_name = server_name.unwrap_or_else(|| self.services.server.config.server_name.clone().into());
|
||||||
|
let signing_keys = self.services.globals.signing_keys_for(&server_name)?;
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
|
"```rs\n{signing_keys:#?}\n```"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) async fn get_verify_keys(
|
||||||
|
&self, server_name: Option<Box<ServerName>>, cached: bool,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
let server_name = server_name.unwrap_or_else(|| self.services.server.config.server_name.clone().into());
|
||||||
|
let mut out = String::new();
|
||||||
|
|
||||||
|
if cached {
|
||||||
|
writeln!(out, "| Key ID | VerifyKey |")?;
|
||||||
|
writeln!(out, "| --- | --- |")?;
|
||||||
|
for (key_id, verify_key) in self.services.globals.verify_keys_for(&server_name)? {
|
||||||
|
writeln!(out, "| {key_id} | {verify_key:?} |")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(RoomMessageEventContent::notice_markdown(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
let signature_ids: Vec<String> = Vec::new();
|
||||||
|
let keys = self
|
||||||
|
.services
|
||||||
|
.server_keys
|
||||||
|
.fetch_signing_keys_for_server(&server_name, signature_ids)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
writeln!(out, "| Key ID | Public Key |")?;
|
||||||
|
writeln!(out, "| --- | --- |")?;
|
||||||
|
for (key_id, key) in keys {
|
||||||
|
writeln!(out, "| {key_id} | {key} |")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn resolve_true_destination(
|
||||||
|
&self, server_name: Box<ServerName>, no_cache: bool,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
if !self.services.globals.config.allow_federation {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Federation is disabled on this homeserver.",
|
"Federation is disabled on this homeserver.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if server_name == services().globals.config.server_name {
|
if server_name == self.services.globals.config.server_name {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
|
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
|
||||||
));
|
));
|
||||||
@@ -611,12 +714,16 @@ pub(super) async fn resolve_true_destination(
|
|||||||
&& matches!(data.span_name(), "actual" | "well-known" | "srv")
|
&& matches!(data.span_name(), "actual" | "well-known" | "srv")
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = &services().server.log.capture;
|
let state = &self.services.server.log.capture;
|
||||||
let logs = Arc::new(Mutex::new(String::new()));
|
let logs = Arc::new(Mutex::new(String::new()));
|
||||||
let capture = Capture::new(state, Some(filter), capture::fmt_markdown(logs.clone()));
|
let capture = Capture::new(state, Some(filter), capture::fmt_markdown(logs.clone()));
|
||||||
|
|
||||||
let capture_scope = capture.start();
|
let capture_scope = capture.start();
|
||||||
let actual = resolve_actual_dest(&server_name, !no_cache).await?;
|
let actual = self
|
||||||
|
.services
|
||||||
|
.resolver
|
||||||
|
.resolve_actual_dest(&server_name, !no_cache)
|
||||||
|
.await?;
|
||||||
drop(capture_scope);
|
drop(capture_scope);
|
||||||
|
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
@@ -628,16 +735,87 @@ pub(super) async fn resolve_true_destination(
|
|||||||
Ok(RoomMessageEventContent::text_markdown(msg))
|
Ok(RoomMessageEventContent::text_markdown(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[admin_command]
|
||||||
pub(super) fn memory_stats() -> RoomMessageEventContent {
|
pub(super) async fn memory_stats(&self) -> Result<RoomMessageEventContent> {
|
||||||
let html_body = conduit::alloc::memory_stats();
|
let html_body = conduit::alloc::memory_stats();
|
||||||
|
|
||||||
if html_body.is_empty() {
|
if html_body.is_none() {
|
||||||
return RoomMessageEventContent::text_plain("malloc stats are not supported on your compiled malloc.");
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
"malloc stats are not supported on your compiled malloc.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomMessageEventContent::text_html(
|
Ok(RoomMessageEventContent::text_html(
|
||||||
"This command's output can only be viewed by clients that render HTML.".to_owned(),
|
"This command's output can only be viewed by clients that render HTML.".to_owned(),
|
||||||
html_body,
|
html_body.expect("string result"),
|
||||||
)
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(tokio_unstable)]
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn runtime_metrics(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let out = self.services.server.metrics.runtime_metrics().map_or_else(
|
||||||
|
|| "Runtime metrics are not available.".to_owned(),
|
||||||
|
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::text_markdown(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tokio_unstable))]
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn runtime_metrics(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
Ok(RoomMessageEventContent::text_markdown(
|
||||||
|
"Runtime metrics require building with `tokio_unstable`.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(tokio_unstable)]
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn runtime_interval(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let out = self.services.server.metrics.runtime_interval().map_or_else(
|
||||||
|
|| "Runtime metrics are not available.".to_owned(),
|
||||||
|
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::text_markdown(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tokio_unstable))]
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn runtime_interval(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
Ok(RoomMessageEventContent::text_markdown(
|
||||||
|
"Runtime metrics require building with `tokio_unstable`.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn time(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let now = SystemTime::now();
|
||||||
|
Ok(RoomMessageEventContent::text_markdown(utils::time::format(now, "%+")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn list_dependencies(&self, names: bool) -> Result<RoomMessageEventContent> {
|
||||||
|
if names {
|
||||||
|
let out = info::cargo::dependencies_names().join(" ");
|
||||||
|
return Ok(RoomMessageEventContent::notice_markdown(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
let deps = info::cargo::dependencies();
|
||||||
|
let mut out = String::new();
|
||||||
|
writeln!(out, "| name | version | features |")?;
|
||||||
|
writeln!(out, "| ---- | ------- | -------- |")?;
|
||||||
|
for (name, dep) in deps {
|
||||||
|
let version = dep.try_req().unwrap_or("*");
|
||||||
|
let feats = dep.req_features();
|
||||||
|
let feats = if !feats.is_empty() {
|
||||||
|
feats.join(" ")
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
writeln!(out, "{name} | {version} | {feats}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-57
@@ -3,13 +3,13 @@ pub(crate) mod tester;
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, OwnedRoomOrAliasId, RoomId, ServerName};
|
use ruma::{EventId, OwnedRoomOrAliasId, RoomId, ServerName};
|
||||||
use tester::TesterCommand;
|
|
||||||
|
|
||||||
use self::commands::*;
|
use self::tester::TesterCommand;
|
||||||
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[admin_command_dispatch]
|
||||||
#[derive(Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub(super) enum DebugCommand {
|
pub(super) enum DebugCommand {
|
||||||
/// - Echo input of admin command
|
/// - Echo input of admin command
|
||||||
Echo {
|
Echo {
|
||||||
@@ -76,6 +76,14 @@ pub(super) enum DebugCommand {
|
|||||||
room_id: OwnedRoomOrAliasId,
|
room_id: OwnedRoomOrAliasId,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// - Get and display signing keys from local cache or remote server.
|
||||||
|
GetSigningKeys {
|
||||||
|
server_name: Option<Box<ServerName>>,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
cached: bool,
|
||||||
|
},
|
||||||
|
|
||||||
/// - Sends a federation request to the remote server's
|
/// - Sends a federation request to the remote server's
|
||||||
/// `/_matrix/federation/v1/version` endpoint and measures the latency it
|
/// `/_matrix/federation/v1/version` endpoint and measures the latency it
|
||||||
/// took for the server to respond
|
/// took for the server to respond
|
||||||
@@ -160,59 +168,24 @@ pub(super) enum DebugCommand {
|
|||||||
/// - Print extended memory usage
|
/// - Print extended memory usage
|
||||||
MemoryStats,
|
MemoryStats,
|
||||||
|
|
||||||
|
/// - Print general tokio runtime metric totals.
|
||||||
|
RuntimeMetrics,
|
||||||
|
|
||||||
|
/// - Print detailed tokio runtime metrics accumulated since last command
|
||||||
|
/// invocation.
|
||||||
|
RuntimeInterval,
|
||||||
|
|
||||||
|
/// - Print the current time
|
||||||
|
Time,
|
||||||
|
|
||||||
|
/// - List dependencies
|
||||||
|
ListDependencies {
|
||||||
|
#[arg(short, long)]
|
||||||
|
names: bool,
|
||||||
|
},
|
||||||
|
|
||||||
/// - Developer test stubs
|
/// - Developer test stubs
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
Tester(TesterCommand),
|
Tester(TesterCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
Ok(match command {
|
|
||||||
DebugCommand::Echo {
|
|
||||||
message,
|
|
||||||
} => echo(body, message).await?,
|
|
||||||
DebugCommand::GetAuthChain {
|
|
||||||
event_id,
|
|
||||||
} => get_auth_chain(body, event_id).await?,
|
|
||||||
DebugCommand::ParsePdu => parse_pdu(body).await?,
|
|
||||||
DebugCommand::GetPdu {
|
|
||||||
event_id,
|
|
||||||
} => get_pdu(body, event_id).await?,
|
|
||||||
DebugCommand::GetRemotePdu {
|
|
||||||
event_id,
|
|
||||||
server,
|
|
||||||
} => get_remote_pdu(body, event_id, server).await?,
|
|
||||||
DebugCommand::GetRoomState {
|
|
||||||
room_id,
|
|
||||||
} => get_room_state(body, room_id).await?,
|
|
||||||
DebugCommand::Ping {
|
|
||||||
server,
|
|
||||||
} => ping(body, server).await?,
|
|
||||||
DebugCommand::ForceDeviceListUpdates => force_device_list_updates(body).await?,
|
|
||||||
DebugCommand::ChangeLogLevel {
|
|
||||||
filter,
|
|
||||||
reset,
|
|
||||||
} => change_log_level(body, filter, reset).await?,
|
|
||||||
DebugCommand::SignJson => sign_json(body).await?,
|
|
||||||
DebugCommand::VerifyJson => verify_json(body).await?,
|
|
||||||
DebugCommand::FirstPduInRoom {
|
|
||||||
room_id,
|
|
||||||
} => first_pdu_in_room(body, room_id).await?,
|
|
||||||
DebugCommand::LatestPduInRoom {
|
|
||||||
room_id,
|
|
||||||
} => latest_pdu_in_room(body, room_id).await?,
|
|
||||||
DebugCommand::GetRemotePduList {
|
|
||||||
server,
|
|
||||||
force,
|
|
||||||
} => get_remote_pdu_list(body, server, force).await?,
|
|
||||||
DebugCommand::ForceSetRoomStateFromServer {
|
|
||||||
room_id,
|
|
||||||
server_name,
|
|
||||||
} => force_set_room_state_from_server(body, server_name, room_id).await?,
|
|
||||||
DebugCommand::ResolveTrueDestination {
|
|
||||||
server_name,
|
|
||||||
no_cache,
|
|
||||||
} => resolve_true_destination(body, server_name, no_cache).await?,
|
|
||||||
DebugCommand::MemoryStats => memory_stats(),
|
|
||||||
DebugCommand::Tester(command) => tester::process(command, body).await?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,34 +1,29 @@
|
|||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
use crate::Result;
|
use crate::{admin_command, admin_command_dispatch, Result};
|
||||||
|
|
||||||
#[derive(clap::Subcommand)]
|
#[admin_command_dispatch]
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[derive(Debug, clap::Subcommand)]
|
||||||
pub(crate) enum TesterCommand {
|
pub(crate) enum TesterCommand {
|
||||||
Tester,
|
Tester,
|
||||||
Timer,
|
Timer,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: TesterCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
match command {
|
|
||||||
TesterCommand::Tester => tester(body).await,
|
|
||||||
TesterCommand::Timer => timer(body).await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
async fn tester(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
async fn tester(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_plain("completed"))
|
Ok(RoomMessageEventContent::notice_plain("completed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
async fn timer(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
async fn timer(&self) -> Result<RoomMessageEventContent> {
|
||||||
let started = std::time::Instant::now();
|
let started = std::time::Instant::now();
|
||||||
timed(&body);
|
timed(self.body);
|
||||||
|
|
||||||
let elapsed = started.elapsed();
|
let elapsed = started.elapsed();
|
||||||
Ok(RoomMessageEventContent::notice_plain(format!("completed in {elapsed:#?}")))
|
Ok(RoomMessageEventContent::notice_plain(format!("completed in {elapsed:#?}")))
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use conduit::Result;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId, ServerName, UserId};
|
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId, ServerName, UserId};
|
||||||
|
|
||||||
use crate::{escape_html, get_room_info, services, Result};
|
use crate::{admin_command, escape_html, get_room_info};
|
||||||
|
|
||||||
pub(super) async fn disable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
services().rooms.metadata.disable_room(&room_id, true)?;
|
pub(super) async fn disable_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||||
|
self.services.rooms.metadata.disable_room(&room_id, true)?;
|
||||||
Ok(RoomMessageEventContent::text_plain("Room disabled."))
|
Ok(RoomMessageEventContent::text_plain("Room disabled."))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn enable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
services().rooms.metadata.disable_room(&room_id, false)?;
|
pub(super) async fn enable_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||||
|
self.services.rooms.metadata.disable_room(&room_id, false)?;
|
||||||
Ok(RoomMessageEventContent::text_plain("Room enabled."))
|
Ok(RoomMessageEventContent::text_plain("Room enabled."))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn incoming_federation(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let map = services()
|
pub(super) async fn incoming_federation(&self) -> Result<RoomMessageEventContent> {
|
||||||
.globals
|
let map = self
|
||||||
.roomid_federationhandletime
|
.services
|
||||||
|
.rooms
|
||||||
|
.event_handler
|
||||||
|
.federation_handletime
|
||||||
.read()
|
.read()
|
||||||
.expect("locked");
|
.expect("locked");
|
||||||
let mut msg = format!("Handling {} incoming pdus:\n", map.len());
|
let mut msg = format!("Handling {} incoming pdus:\n", map.len());
|
||||||
@@ -30,11 +36,10 @@ pub(super) async fn incoming_federation(_body: Vec<&str>) -> Result<RoomMessageE
|
|||||||
Ok(RoomMessageEventContent::text_plain(&msg))
|
Ok(RoomMessageEventContent::text_plain(&msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn fetch_support_well_known(
|
#[admin_command]
|
||||||
_body: Vec<&str>, server_name: Box<ServerName>,
|
pub(super) async fn fetch_support_well_known(&self, server_name: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
||||||
) -> Result<RoomMessageEventContent> {
|
let response = self
|
||||||
let response = services()
|
.services
|
||||||
.globals
|
|
||||||
.client
|
.client
|
||||||
.default
|
.default
|
||||||
.get(format!("https://{server_name}/.well-known/matrix/support"))
|
.get(format!("https://{server_name}/.well-known/matrix/support"))
|
||||||
@@ -72,25 +77,27 @@ pub(super) async fn fetch_support_well_known(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn remote_user_in_rooms(_body: Vec<&str>, user_id: Box<UserId>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
if user_id.server_name() == services().globals.config.server_name {
|
pub(super) async fn remote_user_in_rooms(&self, user_id: Box<UserId>) -> Result<RoomMessageEventContent> {
|
||||||
|
if user_id.server_name() == self.services.globals.config.server_name {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"User belongs to our server, please use `list-joined-rooms` user admin command instead.",
|
"User belongs to our server, please use `list-joined-rooms` user admin command instead.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().users.exists(&user_id)? {
|
if !self.services.users.exists(&user_id)? {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Remote user does not exist in our database.",
|
"Remote user does not exist in our database.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rooms: Vec<(OwnedRoomId, u64, String)> = services()
|
let mut rooms: Vec<(OwnedRoomId, u64, String)> = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(&user_id)
|
.rooms_joined(&user_id)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.map(|room_id| get_room_info(&room_id))
|
.map(|room_id| get_room_info(self.services, &room_id))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if rooms.is_empty() {
|
if rooms.is_empty() {
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ mod commands;
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId};
|
use ruma::{RoomId, ServerName, UserId};
|
||||||
|
|
||||||
use self::commands::*;
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[admin_command_dispatch]
|
||||||
#[derive(Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub(super) enum FederationCommand {
|
pub(super) enum FederationCommand {
|
||||||
/// - List all rooms we are currently handling an incoming pdu from
|
/// - List all rooms we are currently handling an incoming pdu from
|
||||||
IncomingFederation,
|
IncomingFederation,
|
||||||
@@ -40,21 +40,3 @@ pub(super) enum FederationCommand {
|
|||||||
user_id: Box<UserId>,
|
user_id: Box<UserId>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: FederationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
Ok(match command {
|
|
||||||
FederationCommand::DisableRoom {
|
|
||||||
room_id,
|
|
||||||
} => disable_room(body, room_id).await?,
|
|
||||||
FederationCommand::EnableRoom {
|
|
||||||
room_id,
|
|
||||||
} => enable_room(body, room_id).await?,
|
|
||||||
FederationCommand::IncomingFederation => incoming_federation(body).await?,
|
|
||||||
FederationCommand::FetchSupportWellKnown {
|
|
||||||
server_name,
|
|
||||||
} => fetch_support_well_known(body, server_name).await?,
|
|
||||||
FederationCommand::RemoteUserInRooms {
|
|
||||||
user_id,
|
|
||||||
} => remote_user_in_rooms(body, user_id).await?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
+147
-106
@@ -1,171 +1,212 @@
|
|||||||
use std::time::Instant;
|
use std::{panic::AssertUnwindSafe, sync::Arc, time::Instant};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::{CommandFactory, Parser};
|
||||||
use conduit::trace;
|
use conduit::{checked, error, trace, utils::string::common_prefix, Error, Result};
|
||||||
use ruma::events::{
|
use futures_util::future::FutureExt;
|
||||||
|
use ruma::{
|
||||||
|
events::{
|
||||||
relation::InReplyTo,
|
relation::InReplyTo,
|
||||||
room::message::{Relation::Reply, RoomMessageEventContent},
|
room::message::{Relation::Reply, RoomMessageEventContent},
|
||||||
|
},
|
||||||
|
OwnedEventId,
|
||||||
|
};
|
||||||
|
use service::{
|
||||||
|
admin::{CommandInput, CommandOutput, CommandResult, HandlerResult},
|
||||||
|
Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern crate conduit_service as service;
|
use crate::{admin, admin::AdminCommand, Command};
|
||||||
|
|
||||||
use conduit::Result;
|
|
||||||
pub(crate) use service::admin::{Command, Service};
|
|
||||||
use service::admin::{CommandOutput, CommandResult, HandlerResult};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
appservice, appservice::AppserviceCommand, check, check::CheckCommand, debug, debug::DebugCommand, federation,
|
|
||||||
federation::FederationCommand, media, media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand,
|
|
||||||
server, server::ServerCommand, services, user, user::UserCommand,
|
|
||||||
};
|
|
||||||
pub(crate) const PAGE_SIZE: usize = 100;
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Parser)]
|
|
||||||
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
|
|
||||||
pub(crate) enum AdminCommand {
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Commands for managing appservices
|
|
||||||
Appservices(AppserviceCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Commands for managing local users
|
|
||||||
Users(UserCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Commands for managing rooms
|
|
||||||
Rooms(RoomCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Commands for managing federation
|
|
||||||
Federation(FederationCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Commands for managing the server
|
|
||||||
Server(ServerCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Commands for managing media
|
|
||||||
Media(MediaCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Commands for checking integrity
|
|
||||||
Check(CheckCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Commands for debugging things
|
|
||||||
Debug(DebugCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Low-level queries for database getters and iterators
|
|
||||||
Query(QueryCommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn handle(command: Command) -> HandlerResult { Box::pin(handle_command(command)) }
|
pub(super) fn complete(line: &str) -> String { complete_command(AdminCommand::command(), line) }
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub(super) fn handle(services: Arc<Services>, command: CommandInput) -> HandlerResult {
|
||||||
|
Box::pin(handle_command(services, command))
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, name = "admin")]
|
#[tracing::instrument(skip_all, name = "admin")]
|
||||||
async fn handle_command(command: Command) -> CommandResult {
|
async fn handle_command(services: Arc<Services>, command: CommandInput) -> CommandResult {
|
||||||
let Some(mut content) = process_admin_message(command.command).await else {
|
AssertUnwindSafe(Box::pin(process_command(services, &command)))
|
||||||
return Ok(None);
|
.catch_unwind()
|
||||||
};
|
.await
|
||||||
|
.map_err(Error::from_panic)
|
||||||
|
.or_else(|error| handle_panic(&error, command))
|
||||||
|
}
|
||||||
|
|
||||||
content.relates_to = command.reply_id.map(|event_id| Reply {
|
async fn process_command(services: Arc<Services>, command: &CommandInput) -> CommandOutput {
|
||||||
|
process(services, &command.command)
|
||||||
|
.await
|
||||||
|
.and_then(|content| reply(content, command.reply_id.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_panic(error: &Error, command: CommandInput) -> CommandResult {
|
||||||
|
let link = "Please submit a [bug report](https://github.com/girlbossceo/conduwuit/issues/new). 🥺";
|
||||||
|
let msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
|
||||||
|
let content = RoomMessageEventContent::notice_markdown(msg);
|
||||||
|
error!("Panic while processing command: {error:?}");
|
||||||
|
Ok(reply(content, command.reply_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reply(mut content: RoomMessageEventContent, reply_id: Option<OwnedEventId>) -> Option<RoomMessageEventContent> {
|
||||||
|
content.relates_to = reply_id.map(|event_id| Reply {
|
||||||
in_reply_to: InReplyTo {
|
in_reply_to: InReplyTo {
|
||||||
event_id,
|
event_id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Some(content))
|
Some(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse and process a message from the admin room
|
// Parse and process a message from the admin room
|
||||||
async fn process_admin_message(msg: String) -> CommandOutput {
|
async fn process(services: Arc<Services>, msg: &str) -> CommandOutput {
|
||||||
let mut lines = msg.lines().filter(|l| !l.trim().is_empty());
|
let lines = msg.lines().filter(|l| !l.trim().is_empty());
|
||||||
let command = lines.next().expect("each string has at least one line");
|
let command = lines
|
||||||
let body = lines.collect::<Vec<_>>();
|
.clone()
|
||||||
let parsed = match parse_admin_command(command) {
|
.next()
|
||||||
|
.expect("each string has at least one line");
|
||||||
|
let (parsed, body) = match parse_command(command) {
|
||||||
Ok(parsed) => parsed,
|
Ok(parsed) => parsed,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let server_name = services().globals.server_name();
|
let server_name = services.globals.server_name();
|
||||||
let message = error.replace("server.name", server_name.as_str());
|
let message = error.replace("server.name", server_name.as_str());
|
||||||
return Some(RoomMessageEventContent::notice_markdown(message));
|
return Some(RoomMessageEventContent::notice_markdown(message));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let body = parse_body(AdminCommand::command(), &body, lines.skip(1).collect()).expect("trailing body parsed");
|
||||||
|
let context = Command {
|
||||||
|
services: &services,
|
||||||
|
body: &body,
|
||||||
|
};
|
||||||
let timer = Instant::now();
|
let timer = Instant::now();
|
||||||
let result = process_admin_command(parsed, body).await;
|
let result = Box::pin(admin::process(parsed, &context)).await;
|
||||||
let elapsed = timer.elapsed();
|
let elapsed = timer.elapsed();
|
||||||
conduit::debug!(?command, ok = result.is_ok(), "command processed in {elapsed:?}");
|
conduit::debug!(?command, ok = result.is_ok(), "command processed in {elapsed:?}");
|
||||||
match result {
|
match result {
|
||||||
Ok(reply) => Some(reply),
|
Ok(reply) => Some(reply),
|
||||||
Err(error) => Some(RoomMessageEventContent::notice_markdown(format!(
|
Err(error) => Some(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Encountered an error while handling the command:\n```\n{error}\n```"
|
"Encountered an error while handling the command:\n```\n{error:#?}\n```"
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse chat messages from the admin room into an AdminCommand object
|
// Parse chat messages from the admin room into an AdminCommand object
|
||||||
fn parse_admin_command(command_line: &str) -> Result<AdminCommand, String> {
|
fn parse_command(command_line: &str) -> Result<(AdminCommand, Vec<String>), String> {
|
||||||
let mut argv = command_line.split_whitespace().collect::<Vec<_>>();
|
let argv = parse_line(command_line);
|
||||||
|
let com = AdminCommand::try_parse_from(&argv).map_err(|error| error.to_string())?;
|
||||||
|
Ok((com, argv))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_body<'a>(mut cmd: clap::Command, body: &'a [String], lines: Vec<&'a str>) -> Result<Vec<&'a str>> {
|
||||||
|
let mut start = 1;
|
||||||
|
'token: for token in body.iter().skip(1) {
|
||||||
|
let cmd_ = cmd.clone();
|
||||||
|
for sub in cmd_.get_subcommands() {
|
||||||
|
if sub.get_name() == *token {
|
||||||
|
start = checked!(start + 1)?;
|
||||||
|
cmd = sub.clone();
|
||||||
|
continue 'token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// positional arguments have to be skipped too
|
||||||
|
let num_posargs = cmd_.get_positionals().count();
|
||||||
|
start = checked!(start + num_posargs)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(body
|
||||||
|
.iter()
|
||||||
|
.skip(start)
|
||||||
|
.map(String::as_str)
|
||||||
|
.chain(lines)
|
||||||
|
.collect::<Vec<&'a str>>())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_command(mut cmd: clap::Command, line: &str) -> String {
|
||||||
|
let argv = parse_line(line);
|
||||||
|
let mut ret = Vec::<String>::with_capacity(argv.len().saturating_add(1));
|
||||||
|
|
||||||
|
'token: for token in argv.into_iter().skip(1) {
|
||||||
|
let cmd_ = cmd.clone();
|
||||||
|
let mut choice = Vec::new();
|
||||||
|
|
||||||
|
for sub in cmd_.get_subcommands() {
|
||||||
|
let name = sub.get_name();
|
||||||
|
if *name == token {
|
||||||
|
// token already complete; recurse to subcommand
|
||||||
|
ret.push(token);
|
||||||
|
cmd.clone_from(sub);
|
||||||
|
continue 'token;
|
||||||
|
} else if name.starts_with(&token) {
|
||||||
|
// partial match; add to choices
|
||||||
|
choice.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if choice.len() == 1 {
|
||||||
|
// One choice. Add extra space because it's complete
|
||||||
|
let choice = *choice.first().expect("only choice");
|
||||||
|
ret.push(choice.to_owned());
|
||||||
|
ret.push(String::new());
|
||||||
|
} else if choice.is_empty() {
|
||||||
|
// Nothing found, return original string
|
||||||
|
ret.push(token);
|
||||||
|
} else {
|
||||||
|
// Find the common prefix
|
||||||
|
ret.push(common_prefix(&choice).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return from completion
|
||||||
|
return ret.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return from no completion. Needs a space though.
|
||||||
|
ret.push(String::new());
|
||||||
|
ret.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse chat messages from the admin room into an AdminCommand object
|
||||||
|
fn parse_line(command_line: &str) -> Vec<String> {
|
||||||
|
let mut argv = command_line
|
||||||
|
.split_whitespace()
|
||||||
|
.map(str::to_owned)
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
// Remove any escapes that came with a server-side escape command
|
// Remove any escapes that came with a server-side escape command
|
||||||
if !argv.is_empty() && argv[0].ends_with("admin") {
|
if !argv.is_empty() && argv[0].ends_with("admin") {
|
||||||
argv[0] = argv[0].trim_start_matches('\\');
|
argv[0] = argv[0].trim_start_matches('\\').into();
|
||||||
}
|
}
|
||||||
|
|
||||||
// First indice has to be "admin" but for console convenience we add it here
|
// First indice has to be "admin" but for console convenience we add it here
|
||||||
let server_user = services().globals.server_user.as_str();
|
if !argv.is_empty() && !argv[0].ends_with("admin") && !argv[0].starts_with('@') {
|
||||||
if !argv.is_empty() && !argv[0].ends_with("admin") && !argv[0].starts_with(server_user) {
|
argv.insert(0, "admin".to_owned());
|
||||||
argv.insert(0, "admin");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace `help command` with `command --help`
|
// Replace `help command` with `command --help`
|
||||||
// Clap has a help subcommand, but it omits the long help description.
|
// Clap has a help subcommand, but it omits the long help description.
|
||||||
if argv.len() > 1 && argv[1] == "help" {
|
if argv.len() > 1 && argv[1] == "help" {
|
||||||
argv.remove(1);
|
argv.remove(1);
|
||||||
argv.push("--help");
|
argv.push("--help".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backwards compatibility with `register_appservice`-style commands
|
// Backwards compatibility with `register_appservice`-style commands
|
||||||
let command_with_dashes_argv1;
|
|
||||||
if argv.len() > 1 && argv[1].contains('_') {
|
if argv.len() > 1 && argv[1].contains('_') {
|
||||||
command_with_dashes_argv1 = argv[1].replace('_', "-");
|
argv[1] = argv[1].replace('_', "-");
|
||||||
argv[1] = &command_with_dashes_argv1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backwards compatibility with `register_appservice`-style commands
|
// Backwards compatibility with `register_appservice`-style commands
|
||||||
let command_with_dashes_argv2;
|
|
||||||
if argv.len() > 2 && argv[2].contains('_') {
|
if argv.len() > 2 && argv[2].contains('_') {
|
||||||
command_with_dashes_argv2 = argv[2].replace('_', "-");
|
argv[2] = argv[2].replace('_', "-");
|
||||||
argv[2] = &command_with_dashes_argv2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the user is using the `query` command (argv[1]), replace the database
|
// if the user is using the `query` command (argv[1]), replace the database
|
||||||
// function/table calls with underscores to match the codebase
|
// function/table calls with underscores to match the codebase
|
||||||
let command_with_dashes_argv3;
|
|
||||||
if argv.len() > 3 && argv[1].eq("query") {
|
if argv.len() > 3 && argv[1].eq("query") {
|
||||||
command_with_dashes_argv3 = argv[3].replace('_', "-");
|
argv[3] = argv[3].replace('_', "-");
|
||||||
argv[3] = &command_with_dashes_argv3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(?command_line, ?argv, "parse");
|
trace!(?command_line, ?argv, "parse");
|
||||||
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
|
argv
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, name = "command")]
|
|
||||||
async fn process_admin_command(command: AdminCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
let reply_message_content = match command {
|
|
||||||
AdminCommand::Appservices(command) => appservice::process(command, body).await?,
|
|
||||||
AdminCommand::Media(command) => media::process(command, body).await?,
|
|
||||||
AdminCommand::Users(command) => user::process(command, body).await?,
|
|
||||||
AdminCommand::Rooms(command) => room::process(command, body).await?,
|
|
||||||
AdminCommand::Federation(command) => federation::process(command, body).await?,
|
|
||||||
AdminCommand::Server(command) => server::process(command, body).await?,
|
|
||||||
AdminCommand::Debug(command) => debug::process(command, body).await?,
|
|
||||||
AdminCommand::Query(command) => query::process(command, body).await?,
|
|
||||||
AdminCommand::Check(command) => check::process(command, body).await?,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(reply_message_content)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-17
@@ -1,11 +1,11 @@
|
|||||||
use conduit::Result;
|
use conduit::{debug, info, Result};
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
|
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
|
||||||
use tracing::{debug, info};
|
|
||||||
|
|
||||||
use crate::services;
|
use crate::admin_command;
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
pub(super) async fn delete(
|
pub(super) async fn delete(
|
||||||
_body: Vec<&str>, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
|
&self, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if event_id.is_some() && mxc.is_some() {
|
if event_id.is_some() && mxc.is_some() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
@@ -15,7 +15,7 @@ pub(super) async fn delete(
|
|||||||
|
|
||||||
if let Some(mxc) = mxc {
|
if let Some(mxc) = mxc {
|
||||||
debug!("Got MXC URL: {mxc}");
|
debug!("Got MXC URL: {mxc}");
|
||||||
services().media.delete(mxc.as_ref()).await?;
|
self.services.media.delete(mxc.as_ref()).await?;
|
||||||
|
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Deleted the MXC from our database and on our filesystem.",
|
"Deleted the MXC from our database and on our filesystem.",
|
||||||
@@ -27,7 +27,7 @@ pub(super) async fn delete(
|
|||||||
let mut mxc_deletion_count: usize = 0;
|
let mut mxc_deletion_count: usize = 0;
|
||||||
|
|
||||||
// parsing the PDU for any MXC URLs begins here
|
// parsing the PDU for any MXC URLs begins here
|
||||||
if let Some(event_json) = services().rooms.timeline.get_pdu_json(&event_id)? {
|
if let Some(event_json) = self.services.rooms.timeline.get_pdu_json(&event_id)? {
|
||||||
if let Some(content_key) = event_json.get("content") {
|
if let Some(content_key) = event_json.get("content") {
|
||||||
debug!("Event ID has \"content\".");
|
debug!("Event ID has \"content\".");
|
||||||
let content_obj = content_key.as_object();
|
let content_obj = content_key.as_object();
|
||||||
@@ -123,7 +123,7 @@ pub(super) async fn delete(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for mxc_url in mxc_urls {
|
for mxc_url in mxc_urls {
|
||||||
services().media.delete(&mxc_url).await?;
|
self.services.media.delete(&mxc_url).await?;
|
||||||
mxc_deletion_count = mxc_deletion_count.saturating_add(1);
|
mxc_deletion_count = mxc_deletion_count.saturating_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,23 +138,26 @@ pub(super) async fn delete(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mxc_list = body
|
let mxc_list = self
|
||||||
.clone()
|
.body
|
||||||
.drain(1..body.len().checked_sub(1).unwrap())
|
.to_vec()
|
||||||
|
.drain(1..self.body.len().checked_sub(1).unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut mxc_deletion_count: usize = 0;
|
let mut mxc_deletion_count: usize = 0;
|
||||||
|
|
||||||
for mxc in mxc_list {
|
for mxc in mxc_list {
|
||||||
debug!("Deleting MXC {mxc} in bulk");
|
debug!("Deleting MXC {mxc} in bulk");
|
||||||
services().media.delete(mxc).await?;
|
self.services.media.delete(mxc).await?;
|
||||||
mxc_deletion_count = mxc_deletion_count
|
mxc_deletion_count = mxc_deletion_count
|
||||||
.checked_add(1)
|
.checked_add(1)
|
||||||
.expect("mxc_deletion_count should not get this high");
|
.expect("mxc_deletion_count should not get this high");
|
||||||
@@ -165,10 +168,10 @@ pub(super) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventConte
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn delete_past_remote_media(
|
#[admin_command]
|
||||||
_body: Vec<&str>, duration: String, force: bool,
|
pub(super) async fn delete_past_remote_media(&self, duration: String, force: bool) -> Result<RoomMessageEventContent> {
|
||||||
) -> Result<RoomMessageEventContent> {
|
let deleted_count = self
|
||||||
let deleted_count = services()
|
.services
|
||||||
.media
|
.media
|
||||||
.delete_all_remote_media_at_after_time(duration, force)
|
.delete_all_remote_media_at_after_time(duration, force)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
+4
-18
@@ -2,12 +2,12 @@ mod commands;
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
|
use ruma::{EventId, MxcUri};
|
||||||
|
|
||||||
use self::commands::*;
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[admin_command_dispatch]
|
||||||
#[derive(Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub(super) enum MediaCommand {
|
pub(super) enum MediaCommand {
|
||||||
/// - Deletes a single media file from our database and on the filesystem
|
/// - Deletes a single media file from our database and on the filesystem
|
||||||
/// via a single MXC URL
|
/// via a single MXC URL
|
||||||
@@ -37,17 +37,3 @@ pub(super) enum MediaCommand {
|
|||||||
force: bool,
|
force: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: MediaCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
Ok(match command {
|
|
||||||
MediaCommand::Delete {
|
|
||||||
mxc,
|
|
||||||
event_id,
|
|
||||||
} => delete(body, mxc, event_id).await?,
|
|
||||||
MediaCommand::DeleteList => delete_list(body).await?,
|
|
||||||
MediaCommand::DeletePastRemoteMedia {
|
|
||||||
duration,
|
|
||||||
force,
|
|
||||||
} => delete_past_remote_media(body, duration, force).await?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
+32
-31
@@ -1,56 +1,57 @@
|
|||||||
|
#![recursion_limit = "192"]
|
||||||
#![allow(clippy::wildcard_imports)]
|
#![allow(clippy::wildcard_imports)]
|
||||||
|
#![allow(clippy::enum_glob_use)]
|
||||||
|
|
||||||
|
pub(crate) mod admin;
|
||||||
|
pub(crate) mod command;
|
||||||
|
pub(crate) mod handler;
|
||||||
|
mod tests;
|
||||||
|
pub(crate) mod utils;
|
||||||
|
|
||||||
pub(crate) mod appservice;
|
pub(crate) mod appservice;
|
||||||
pub(crate) mod check;
|
pub(crate) mod check;
|
||||||
pub(crate) mod debug;
|
pub(crate) mod debug;
|
||||||
pub(crate) mod federation;
|
pub(crate) mod federation;
|
||||||
pub(crate) mod handler;
|
|
||||||
pub(crate) mod media;
|
pub(crate) mod media;
|
||||||
pub(crate) mod query;
|
pub(crate) mod query;
|
||||||
pub(crate) mod room;
|
pub(crate) mod room;
|
||||||
pub(crate) mod server;
|
pub(crate) mod server;
|
||||||
pub(crate) mod user;
|
pub(crate) mod user;
|
||||||
pub(crate) mod utils;
|
|
||||||
|
|
||||||
extern crate conduit_api as api;
|
extern crate conduit_api as api;
|
||||||
extern crate conduit_core as conduit;
|
extern crate conduit_core as conduit;
|
||||||
extern crate conduit_service as service;
|
extern crate conduit_service as service;
|
||||||
|
|
||||||
pub(crate) use conduit::{mod_ctor, mod_dtor, Result};
|
pub(crate) use conduit::Result;
|
||||||
pub use handler::handle;
|
pub(crate) use conduit_macros::{admin_command, admin_command_dispatch};
|
||||||
pub(crate) use service::{services, user_is_local};
|
|
||||||
|
|
||||||
pub(crate) use crate::{
|
pub(crate) use crate::{
|
||||||
handler::Service,
|
command::Command,
|
||||||
utils::{escape_html, get_room_info},
|
utils::{escape_html, get_room_info},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod_ctor! {}
|
pub(crate) const PAGE_SIZE: usize = 100;
|
||||||
mod_dtor! {}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
conduit::mod_ctor! {}
|
||||||
mod test {
|
conduit::mod_dtor! {}
|
||||||
use clap::Parser;
|
conduit::rustc_flags_capture! {}
|
||||||
|
|
||||||
use crate::handler::AdminCommand;
|
/// Install the admin command handler
|
||||||
|
pub async fn init(admin_service: &service::admin::Service) {
|
||||||
#[test]
|
_ = admin_service
|
||||||
fn get_help_short() { get_help_inner("-h"); }
|
.complete
|
||||||
|
.write()
|
||||||
#[test]
|
.expect("locked for writing")
|
||||||
fn get_help_long() { get_help_inner("--help"); }
|
.insert(handler::complete);
|
||||||
|
_ = admin_service.handle.write().await.insert(handler::handle);
|
||||||
#[test]
|
|
||||||
fn get_help_subcommand() { get_help_inner("help"); }
|
|
||||||
|
|
||||||
fn get_help_inner(input: &str) {
|
|
||||||
let error = AdminCommand::try_parse_from(["argv[0] doesn't matter", input])
|
|
||||||
.unwrap_err()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
// Search for a handful of keywords that suggest the help printed properly
|
|
||||||
assert!(error.contains("Usage:"));
|
|
||||||
assert!(error.contains("Commands:"));
|
|
||||||
assert!(error.contains("Options:"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Uninstall the admin command handler
|
||||||
|
pub async fn fini(admin_service: &service::admin::Service) {
|
||||||
|
_ = admin_service.handle.write().await.take();
|
||||||
|
_ = admin_service
|
||||||
|
.complete
|
||||||
|
.write()
|
||||||
|
.expect("locked for writing")
|
||||||
|
.take();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,48 @@
|
|||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
|
use ruma::{
|
||||||
|
events::{room::message::RoomMessageEventContent, RoomAccountDataEventType},
|
||||||
|
RoomId, UserId,
|
||||||
|
};
|
||||||
|
|
||||||
use super::AccountData;
|
use crate::Command;
|
||||||
use crate::{services, Result};
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||||
|
pub(crate) enum AccountDataCommand {
|
||||||
|
/// - Returns all changes to the account data that happened after `since`.
|
||||||
|
ChangesSince {
|
||||||
|
/// Full user ID
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
/// UNIX timestamp since (u64)
|
||||||
|
since: u64,
|
||||||
|
/// Optional room ID of the account data
|
||||||
|
room_id: Option<Box<RoomId>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Searches the account data for a specific kind.
|
||||||
|
Get {
|
||||||
|
/// Full user ID
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
/// Account data event type
|
||||||
|
kind: RoomAccountDataEventType,
|
||||||
|
/// Optional room ID of the account data
|
||||||
|
room_id: Option<Box<RoomId>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||||
pub(super) async fn account_data(subcommand: AccountData) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(subcommand: AccountDataCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
AccountData::ChangesSince {
|
AccountDataCommand::ChangesSince {
|
||||||
user_id,
|
user_id,
|
||||||
since,
|
since,
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services()
|
let results = services
|
||||||
.account_data
|
.account_data
|
||||||
.changes_since(room_id.as_deref(), &user_id, since)?;
|
.changes_since(room_id.as_deref(), &user_id, since)?;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
@@ -21,13 +51,13 @@ pub(super) async fn account_data(subcommand: AccountData) -> Result<RoomMessageE
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
AccountData::Get {
|
AccountDataCommand::Get {
|
||||||
user_id,
|
user_id,
|
||||||
kind,
|
kind,
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services()
|
let results = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(room_id.as_deref(), &user_id, kind)?;
|
.get(room_id.as_deref(), &user_id, kind)?;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|||||||
@@ -1,16 +1,32 @@
|
|||||||
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
use super::Appservice;
|
use crate::Command;
|
||||||
use crate::{services, Result};
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||||
|
pub(crate) enum AppserviceCommand {
|
||||||
|
/// - Gets the appservice registration info/details from the ID as a string
|
||||||
|
GetRegistration {
|
||||||
|
/// Appservice registration ID
|
||||||
|
appservice_id: Box<str>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Gets all appservice registrations with their ID and registration info
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
||||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||||
pub(super) async fn appservice(subcommand: Appservice) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(subcommand: AppserviceCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
Appservice::GetRegistration {
|
AppserviceCommand::GetRegistration {
|
||||||
appservice_id,
|
appservice_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services()
|
let results = services
|
||||||
.appservice
|
.appservice
|
||||||
.db
|
.db
|
||||||
.get_registration(appservice_id.as_ref());
|
.get_registration(appservice_id.as_ref());
|
||||||
@@ -20,9 +36,9 @@ pub(super) async fn appservice(subcommand: Appservice) -> Result<RoomMessageEven
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Appservice::All => {
|
AppserviceCommand::All => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().appservice.all();
|
let results = services.appservice.all();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
|
|||||||
+35
-14
@@ -1,52 +1,73 @@
|
|||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
|
use ruma::{events::room::message::RoomMessageEventContent, ServerName};
|
||||||
|
|
||||||
use super::Globals;
|
use crate::Command;
|
||||||
use crate::{services, Result};
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
/// All the getters and iterators from src/database/key_value/globals.rs
|
||||||
|
pub(crate) enum GlobalsCommand {
|
||||||
|
DatabaseVersion,
|
||||||
|
|
||||||
|
CurrentCount,
|
||||||
|
|
||||||
|
LastCheckForUpdatesId,
|
||||||
|
|
||||||
|
LoadKeypair,
|
||||||
|
|
||||||
|
/// - This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
|
||||||
|
/// for the server.
|
||||||
|
SigningKeysFor {
|
||||||
|
origin: Box<ServerName>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// All the getters and iterators from src/database/key_value/globals.rs
|
/// All the getters and iterators from src/database/key_value/globals.rs
|
||||||
pub(super) async fn globals(subcommand: Globals) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(subcommand: GlobalsCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
Globals::DatabaseVersion => {
|
GlobalsCommand::DatabaseVersion => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().globals.db.database_version();
|
let results = services.globals.db.database_version();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Globals::CurrentCount => {
|
GlobalsCommand::CurrentCount => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().globals.db.current_count();
|
let results = services.globals.db.current_count();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Globals::LastCheckForUpdatesId => {
|
GlobalsCommand::LastCheckForUpdatesId => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().globals.db.last_check_for_updates_id();
|
let results = services.updates.last_check_for_updates_id();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Globals::LoadKeypair => {
|
GlobalsCommand::LoadKeypair => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().globals.db.load_keypair();
|
let results = services.globals.db.load_keypair();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Globals::SigningKeysFor {
|
GlobalsCommand::SigningKeysFor {
|
||||||
origin,
|
origin,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().globals.db.signing_keys_for(&origin);
|
let results = services.globals.db.verify_keys_for(&origin);
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
|
|||||||
+15
-278
@@ -10,314 +10,51 @@ mod users;
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use room_state_cache::room_state_cache;
|
|
||||||
use ruma::{
|
|
||||||
events::{room::message::RoomMessageEventContent, RoomAccountDataEventType},
|
|
||||||
OwnedServerName, RoomAliasId, RoomId, ServerName, UserId,
|
|
||||||
};
|
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
account_data::account_data, appservice::appservice, globals::globals, presence::presence, resolver::resolver,
|
account_data::AccountDataCommand, appservice::AppserviceCommand, globals::GlobalsCommand,
|
||||||
room_alias::room_alias, sending::sending, users::users,
|
presence::PresenceCommand, resolver::ResolverCommand, room_alias::RoomAliasCommand,
|
||||||
|
room_state_cache::RoomStateCacheCommand, sending::SendingCommand, users::UsersCommand,
|
||||||
};
|
};
|
||||||
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[admin_command_dispatch]
|
||||||
#[derive(Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
/// Query tables from database
|
/// Query tables from database
|
||||||
pub(super) enum QueryCommand {
|
pub(super) enum QueryCommand {
|
||||||
/// - account_data.rs iterators and getters
|
/// - account_data.rs iterators and getters
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
AccountData(AccountData),
|
AccountData(AccountDataCommand),
|
||||||
|
|
||||||
/// - appservice.rs iterators and getters
|
/// - appservice.rs iterators and getters
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Appservice(Appservice),
|
Appservice(AppserviceCommand),
|
||||||
|
|
||||||
/// - presence.rs iterators and getters
|
/// - presence.rs iterators and getters
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Presence(Presence),
|
Presence(PresenceCommand),
|
||||||
|
|
||||||
/// - rooms/alias.rs iterators and getters
|
/// - rooms/alias.rs iterators and getters
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
RoomAlias(RoomAlias),
|
RoomAlias(RoomAliasCommand),
|
||||||
|
|
||||||
/// - rooms/state_cache iterators and getters
|
/// - rooms/state_cache iterators and getters
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
RoomStateCache(RoomStateCache),
|
RoomStateCache(RoomStateCacheCommand),
|
||||||
|
|
||||||
/// - globals.rs iterators and getters
|
/// - globals.rs iterators and getters
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Globals(Globals),
|
Globals(GlobalsCommand),
|
||||||
|
|
||||||
/// - sending.rs iterators and getters
|
/// - sending.rs iterators and getters
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Sending(Sending),
|
Sending(SendingCommand),
|
||||||
|
|
||||||
/// - users.rs iterators and getters
|
/// - users.rs iterators and getters
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Users(Users),
|
Users(UsersCommand),
|
||||||
|
|
||||||
/// - resolver service
|
/// - resolver service
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Resolver(Resolver),
|
Resolver(ResolverCommand),
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
|
||||||
pub(super) enum AccountData {
|
|
||||||
/// - Returns all changes to the account data that happened after `since`.
|
|
||||||
ChangesSince {
|
|
||||||
/// Full user ID
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
/// UNIX timestamp since (u64)
|
|
||||||
since: u64,
|
|
||||||
/// Optional room ID of the account data
|
|
||||||
room_id: Option<Box<RoomId>>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Searches the account data for a specific kind.
|
|
||||||
Get {
|
|
||||||
/// Full user ID
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
/// Account data event type
|
|
||||||
kind: RoomAccountDataEventType,
|
|
||||||
/// Optional room ID of the account data
|
|
||||||
room_id: Option<Box<RoomId>>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
|
||||||
pub(super) enum Appservice {
|
|
||||||
/// - Gets the appservice registration info/details from the ID as a string
|
|
||||||
GetRegistration {
|
|
||||||
/// Appservice registration ID
|
|
||||||
appservice_id: Box<str>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Gets all appservice registrations with their ID and registration info
|
|
||||||
All,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
/// All the getters and iterators from src/database/key_value/presence.rs
|
|
||||||
pub(super) enum Presence {
|
|
||||||
/// - Returns the latest presence event for the given user.
|
|
||||||
GetPresence {
|
|
||||||
/// Full user ID
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Iterator of the most recent presence updates that happened after the
|
|
||||||
/// event with id `since`.
|
|
||||||
PresenceSince {
|
|
||||||
/// UNIX timestamp since (u64)
|
|
||||||
since: u64,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
/// All the getters and iterators from src/database/key_value/rooms/alias.rs
|
|
||||||
pub(super) enum RoomAlias {
|
|
||||||
ResolveLocalAlias {
|
|
||||||
/// Full room alias
|
|
||||||
alias: Box<RoomAliasId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Iterator of all our local room aliases for the room ID
|
|
||||||
LocalAliasesForRoom {
|
|
||||||
/// Full room ID
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Iterator of all our local aliases in our database with their room IDs
|
|
||||||
AllLocalAliases,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub(super) enum RoomStateCache {
|
|
||||||
ServerInRoom {
|
|
||||||
server: Box<ServerName>,
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
RoomServers {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
ServerRooms {
|
|
||||||
server: Box<ServerName>,
|
|
||||||
},
|
|
||||||
|
|
||||||
RoomMembers {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
LocalUsersInRoom {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
ActiveLocalUsersInRoom {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
RoomJoinedCount {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
RoomInvitedCount {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
RoomUserOnceJoined {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
RoomMembersInvited {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
GetInviteCount {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
GetLeftCount {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
RoomsJoined {
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
RoomsLeft {
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
RoomsInvited {
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
InviteState {
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
/// All the getters and iterators from src/database/key_value/globals.rs
|
|
||||||
pub(super) enum Globals {
|
|
||||||
DatabaseVersion,
|
|
||||||
|
|
||||||
CurrentCount,
|
|
||||||
|
|
||||||
LastCheckForUpdatesId,
|
|
||||||
|
|
||||||
LoadKeypair,
|
|
||||||
|
|
||||||
/// - This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
|
|
||||||
/// for the server.
|
|
||||||
SigningKeysFor {
|
|
||||||
origin: Box<ServerName>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
/// All the getters and iterators from src/database/key_value/sending.rs
|
|
||||||
pub(super) enum Sending {
|
|
||||||
/// - Queries database for all `servercurrentevent_data`
|
|
||||||
ActiveRequests,
|
|
||||||
|
|
||||||
/// - Queries database for `servercurrentevent_data` but for a specific
|
|
||||||
/// destination
|
|
||||||
///
|
|
||||||
/// This command takes only *one* format of these arguments:
|
|
||||||
///
|
|
||||||
/// appservice_id
|
|
||||||
/// server_name
|
|
||||||
/// user_id AND push_key
|
|
||||||
///
|
|
||||||
/// See src/service/sending/mod.rs for the definition of the `Destination`
|
|
||||||
/// enum
|
|
||||||
ActiveRequestsFor {
|
|
||||||
#[arg(short, long)]
|
|
||||||
appservice_id: Option<String>,
|
|
||||||
#[arg(short, long)]
|
|
||||||
server_name: Option<Box<ServerName>>,
|
|
||||||
#[arg(short, long)]
|
|
||||||
user_id: Option<Box<UserId>>,
|
|
||||||
#[arg(short, long)]
|
|
||||||
push_key: Option<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Queries database for `servernameevent_data` which are the queued up
|
|
||||||
/// requests that will eventually be sent
|
|
||||||
///
|
|
||||||
/// This command takes only *one* format of these arguments:
|
|
||||||
///
|
|
||||||
/// appservice_id
|
|
||||||
/// server_name
|
|
||||||
/// user_id AND push_key
|
|
||||||
///
|
|
||||||
/// See src/service/sending/mod.rs for the definition of the `Destination`
|
|
||||||
/// enum
|
|
||||||
QueuedRequests {
|
|
||||||
#[arg(short, long)]
|
|
||||||
appservice_id: Option<String>,
|
|
||||||
#[arg(short, long)]
|
|
||||||
server_name: Option<Box<ServerName>>,
|
|
||||||
#[arg(short, long)]
|
|
||||||
user_id: Option<Box<UserId>>,
|
|
||||||
#[arg(short, long)]
|
|
||||||
push_key: Option<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
GetLatestEduCount {
|
|
||||||
server_name: Box<ServerName>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
/// All the getters and iterators from src/database/key_value/users.rs
|
|
||||||
pub(super) enum Users {
|
|
||||||
Iter,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
/// Resolver service and caches
|
|
||||||
pub(super) enum Resolver {
|
|
||||||
/// Query the destinations cache
|
|
||||||
DestinationsCache {
|
|
||||||
server_name: Option<OwnedServerName>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Query the overrides cache
|
|
||||||
OverridesCache {
|
|
||||||
name: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Processes admin query commands
|
|
||||||
pub(super) async fn process(command: QueryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
Ok(match command {
|
|
||||||
QueryCommand::AccountData(command) => account_data(command).await?,
|
|
||||||
QueryCommand::Appservice(command) => appservice(command).await?,
|
|
||||||
QueryCommand::Presence(command) => presence(command).await?,
|
|
||||||
QueryCommand::RoomAlias(command) => room_alias(command).await?,
|
|
||||||
QueryCommand::RoomStateCache(command) => room_state_cache(command).await?,
|
|
||||||
QueryCommand::Globals(command) => globals(command).await?,
|
|
||||||
QueryCommand::Sending(command) => sending(command).await?,
|
|
||||||
QueryCommand::Users(command) => users(command).await?,
|
|
||||||
QueryCommand::Resolver(command) => resolver(command).await?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,47 @@
|
|||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
|
use ruma::{events::room::message::RoomMessageEventContent, UserId};
|
||||||
|
|
||||||
use super::Presence;
|
use crate::Command;
|
||||||
use crate::{services, Result};
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
/// All the getters and iterators from src/database/key_value/presence.rs
|
||||||
|
pub(crate) enum PresenceCommand {
|
||||||
|
/// - Returns the latest presence event for the given user.
|
||||||
|
GetPresence {
|
||||||
|
/// Full user ID
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Iterator of the most recent presence updates that happened after the
|
||||||
|
/// event with id `since`.
|
||||||
|
PresenceSince {
|
||||||
|
/// UNIX timestamp since (u64)
|
||||||
|
since: u64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// All the getters and iterators in key_value/presence.rs
|
/// All the getters and iterators in key_value/presence.rs
|
||||||
pub(super) async fn presence(subcommand: Presence) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(subcommand: PresenceCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
Presence::GetPresence {
|
PresenceCommand::GetPresence {
|
||||||
user_id,
|
user_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().presence.db.get_presence(&user_id)?;
|
let results = services.presence.db.get_presence(&user_id)?;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Presence::PresenceSince {
|
PresenceCommand::PresenceSince {
|
||||||
since,
|
since,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().presence.db.presence_since(since);
|
let results = services.presence.db.presence_since(since);
|
||||||
let presence_since: Vec<(_, _, _)> = results.collect();
|
let presence_since: Vec<(_, _, _)> = results.collect();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
|
|||||||
+27
-20
@@ -1,25 +1,29 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use clap::Subcommand;
|
||||||
use conduit::{utils::time, Result};
|
use conduit::{utils::time, Result};
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedServerName};
|
use ruma::{events::room::message::RoomMessageEventContent, OwnedServerName};
|
||||||
|
|
||||||
use super::Resolver;
|
use crate::{admin_command, admin_command_dispatch};
|
||||||
use crate::services;
|
|
||||||
|
|
||||||
/// All the getters and iterators in key_value/users.rs
|
#[admin_command_dispatch]
|
||||||
pub(super) async fn resolver(subcommand: Resolver) -> Result<RoomMessageEventContent> {
|
#[derive(Debug, Subcommand)]
|
||||||
match subcommand {
|
/// Resolver service and caches
|
||||||
Resolver::DestinationsCache {
|
pub(crate) enum ResolverCommand {
|
||||||
server_name,
|
/// Query the destinations cache
|
||||||
} => destinations_cache(server_name).await,
|
DestinationsCache {
|
||||||
Resolver::OverridesCache {
|
server_name: Option<OwnedServerName>,
|
||||||
name,
|
},
|
||||||
} => overrides_cache(name).await,
|
|
||||||
}
|
/// Query the overrides cache
|
||||||
|
OverridesCache {
|
||||||
|
name: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn destinations_cache(server_name: Option<OwnedServerName>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
use service::sending::CachedDest;
|
async fn destinations_cache(&self, server_name: Option<OwnedServerName>) -> Result<RoomMessageEventContent> {
|
||||||
|
use service::resolver::cache::CachedDest;
|
||||||
|
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
writeln!(out, "| Server Name | Destination | Hostname | Expires |")?;
|
writeln!(out, "| Server Name | Destination | Hostname | Expires |")?;
|
||||||
@@ -36,9 +40,10 @@ async fn destinations_cache(server_name: Option<OwnedServerName>) -> Result<Room
|
|||||||
writeln!(out, "| {name} | {dest} | {host} | {expire} |").expect("wrote line");
|
writeln!(out, "| {name} | {dest} | {host} | {expire} |").expect("wrote line");
|
||||||
};
|
};
|
||||||
|
|
||||||
let map = services()
|
let map = self
|
||||||
.globals
|
.services
|
||||||
.resolver
|
.resolver
|
||||||
|
.cache
|
||||||
.destinations
|
.destinations
|
||||||
.read()
|
.read()
|
||||||
.expect("locked");
|
.expect("locked");
|
||||||
@@ -52,8 +57,9 @@ async fn destinations_cache(server_name: Option<OwnedServerName>) -> Result<Room
|
|||||||
Ok(RoomMessageEventContent::notice_markdown(out))
|
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn overrides_cache(server_name: Option<String>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
use service::sending::CachedOverride;
|
async fn overrides_cache(&self, server_name: Option<String>) -> Result<RoomMessageEventContent> {
|
||||||
|
use service::resolver::cache::CachedOverride;
|
||||||
|
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
writeln!(out, "| Server Name | IP | Port | Expires |")?;
|
writeln!(out, "| Server Name | IP | Port | Expires |")?;
|
||||||
@@ -70,9 +76,10 @@ async fn overrides_cache(server_name: Option<String>) -> Result<RoomMessageEvent
|
|||||||
writeln!(out, "| {name} | {ips:?} | {port} | {expire} |").expect("wrote line");
|
writeln!(out, "| {name} | {ips:?} | {port} | {expire} |").expect("wrote line");
|
||||||
};
|
};
|
||||||
|
|
||||||
let map = services()
|
let map = self
|
||||||
.globals
|
.services
|
||||||
.resolver
|
.resolver
|
||||||
|
.cache
|
||||||
.overrides
|
.overrides
|
||||||
.read()
|
.read()
|
||||||
.expect("locked");
|
.expect("locked");
|
||||||
|
|||||||
@@ -1,27 +1,48 @@
|
|||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
|
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId, RoomId};
|
||||||
|
|
||||||
use super::RoomAlias;
|
use crate::Command;
|
||||||
use crate::{services, Result};
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
/// All the getters and iterators from src/database/key_value/rooms/alias.rs
|
||||||
|
pub(crate) enum RoomAliasCommand {
|
||||||
|
ResolveLocalAlias {
|
||||||
|
/// Full room alias
|
||||||
|
alias: Box<RoomAliasId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Iterator of all our local room aliases for the room ID
|
||||||
|
LocalAliasesForRoom {
|
||||||
|
/// Full room ID
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Iterator of all our local aliases in our database with their room IDs
|
||||||
|
AllLocalAliases,
|
||||||
|
}
|
||||||
|
|
||||||
/// All the getters and iterators in src/database/key_value/rooms/alias.rs
|
/// All the getters and iterators in src/database/key_value/rooms/alias.rs
|
||||||
pub(super) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
RoomAlias::ResolveLocalAlias {
|
RoomAliasCommand::ResolveLocalAlias {
|
||||||
alias,
|
alias,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().rooms.alias.resolve_local_alias(&alias);
|
let results = services.rooms.alias.resolve_local_alias(&alias);
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomAlias::LocalAliasesForRoom {
|
RoomAliasCommand::LocalAliasesForRoom {
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().rooms.alias.local_aliases_for_room(&room_id);
|
let results = services.rooms.alias.local_aliases_for_room(&room_id);
|
||||||
let aliases: Vec<_> = results.collect();
|
let aliases: Vec<_> = results.collect();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
@@ -29,9 +50,9 @@ pub(super) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEvent
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomAlias::AllLocalAliases => {
|
RoomAliasCommand::AllLocalAliases => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().rooms.alias.all_local_aliases();
|
let results = services.rooms.alias.all_local_aliases();
|
||||||
let aliases: Vec<_> = results.collect();
|
let aliases: Vec<_> = results.collect();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +1,136 @@
|
|||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
|
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId};
|
||||||
|
|
||||||
use super::RoomStateCache;
|
use crate::Command;
|
||||||
use crate::{services, Result};
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub(crate) enum RoomStateCacheCommand {
|
||||||
|
ServerInRoom {
|
||||||
|
server: Box<ServerName>,
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RoomServers {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
ServerRooms {
|
||||||
|
server: Box<ServerName>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RoomMembers {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalUsersInRoom {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
ActiveLocalUsersInRoom {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RoomJoinedCount {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RoomInvitedCount {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RoomUserOnceJoined {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RoomMembersInvited {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
GetInviteCount {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
GetLeftCount {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RoomsJoined {
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RoomsLeft {
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RoomsInvited {
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
InviteState {
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn process(
|
||||||
|
subcommand: RoomStateCacheCommand, context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
|
|
||||||
pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomMessageEventContent> {
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
RoomStateCache::ServerInRoom {
|
RoomStateCacheCommand::ServerInRoom {
|
||||||
server,
|
server,
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = services()
|
let result = services.rooms.state_cache.server_in_room(&server, &room_id);
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.server_in_room(&server, &room_id);
|
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::RoomServers {
|
RoomStateCacheCommand::RoomServers {
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Result<Vec<_>> = services()
|
let results: Result<Vec<_>> = services.rooms.state_cache.room_servers(&room_id).collect();
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.room_servers(&room_id)
|
|
||||||
.collect();
|
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::ServerRooms {
|
RoomStateCacheCommand::ServerRooms {
|
||||||
server,
|
server,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Result<Vec<_>> = services().rooms.state_cache.server_rooms(&server).collect();
|
let results: Result<Vec<_>> = services.rooms.state_cache.server_rooms(&server).collect();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::RoomMembers {
|
RoomStateCacheCommand::RoomMembers {
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Result<Vec<_>> = services()
|
let results: Result<Vec<_>> = services.rooms.state_cache.room_members(&room_id).collect();
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.room_members(&room_id)
|
|
||||||
.collect();
|
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::LocalUsersInRoom {
|
RoomStateCacheCommand::LocalUsersInRoom {
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services()
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.local_users_in_room(&room_id)
|
.local_users_in_room(&room_id)
|
||||||
@@ -76,11 +141,11 @@ pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::ActiveLocalUsersInRoom {
|
RoomStateCacheCommand::ActiveLocalUsersInRoom {
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services()
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.active_local_users_in_room(&room_id)
|
.active_local_users_in_room(&room_id)
|
||||||
@@ -91,33 +156,33 @@ pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::RoomJoinedCount {
|
RoomStateCacheCommand::RoomJoinedCount {
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().rooms.state_cache.room_joined_count(&room_id);
|
let results = services.rooms.state_cache.room_joined_count(&room_id);
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::RoomInvitedCount {
|
RoomStateCacheCommand::RoomInvitedCount {
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().rooms.state_cache.room_invited_count(&room_id);
|
let results = services.rooms.state_cache.room_invited_count(&room_id);
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::RoomUserOnceJoined {
|
RoomStateCacheCommand::RoomUserOnceJoined {
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Result<Vec<_>> = services()
|
let results: Result<Vec<_>> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_useroncejoined(&room_id)
|
.room_useroncejoined(&room_id)
|
||||||
@@ -128,11 +193,11 @@ pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::RoomMembersInvited {
|
RoomStateCacheCommand::RoomMembersInvited {
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Result<Vec<_>> = services()
|
let results: Result<Vec<_>> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members_invited(&room_id)
|
.room_members_invited(&room_id)
|
||||||
@@ -143,12 +208,12 @@ pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::GetInviteCount {
|
RoomStateCacheCommand::GetInviteCount {
|
||||||
room_id,
|
room_id,
|
||||||
user_id,
|
user_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services()
|
let results = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.get_invite_count(&room_id, &user_id);
|
.get_invite_count(&room_id, &user_id);
|
||||||
@@ -158,12 +223,12 @@ pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::GetLeftCount {
|
RoomStateCacheCommand::GetLeftCount {
|
||||||
room_id,
|
room_id,
|
||||||
user_id,
|
user_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services()
|
let results = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.get_left_count(&room_id, &user_id);
|
.get_left_count(&room_id, &user_id);
|
||||||
@@ -173,56 +238,45 @@ pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::RoomsJoined {
|
RoomStateCacheCommand::RoomsJoined {
|
||||||
user_id,
|
user_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Result<Vec<_>> = services()
|
let results: Result<Vec<_>> = services.rooms.state_cache.rooms_joined(&user_id).collect();
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.rooms_joined(&user_id)
|
|
||||||
.collect();
|
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::RoomsInvited {
|
RoomStateCacheCommand::RoomsInvited {
|
||||||
user_id,
|
user_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Result<Vec<_>> = services()
|
let results: Result<Vec<_>> = services.rooms.state_cache.rooms_invited(&user_id).collect();
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.rooms_invited(&user_id)
|
|
||||||
.collect();
|
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::RoomsLeft {
|
RoomStateCacheCommand::RoomsLeft {
|
||||||
user_id,
|
user_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Result<Vec<_>> = services().rooms.state_cache.rooms_left(&user_id).collect();
|
let results: Result<Vec<_>> = services.rooms.state_cache.rooms_left(&user_id).collect();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCache::InviteState {
|
RoomStateCacheCommand::InviteState {
|
||||||
user_id,
|
user_id,
|
||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services()
|
let results = services.rooms.state_cache.invite_state(&user_id, &room_id);
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.invite_state(&user_id, &room_id);
|
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
|
|||||||
+75
-16
@@ -1,14 +1,73 @@
|
|||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
|
use ruma::{events::room::message::RoomMessageEventContent, ServerName, UserId};
|
||||||
|
use service::sending::Destination;
|
||||||
|
|
||||||
use super::Sending;
|
use crate::Command;
|
||||||
use crate::{service::sending::Destination, services, Result};
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
/// All the getters and iterators from src/database/key_value/sending.rs
|
||||||
|
pub(crate) enum SendingCommand {
|
||||||
|
/// - Queries database for all `servercurrentevent_data`
|
||||||
|
ActiveRequests,
|
||||||
|
|
||||||
|
/// - Queries database for `servercurrentevent_data` but for a specific
|
||||||
|
/// destination
|
||||||
|
///
|
||||||
|
/// This command takes only *one* format of these arguments:
|
||||||
|
///
|
||||||
|
/// appservice_id
|
||||||
|
/// server_name
|
||||||
|
/// user_id AND push_key
|
||||||
|
///
|
||||||
|
/// See src/service/sending/mod.rs for the definition of the `Destination`
|
||||||
|
/// enum
|
||||||
|
ActiveRequestsFor {
|
||||||
|
#[arg(short, long)]
|
||||||
|
appservice_id: Option<String>,
|
||||||
|
#[arg(short, long)]
|
||||||
|
server_name: Option<Box<ServerName>>,
|
||||||
|
#[arg(short, long)]
|
||||||
|
user_id: Option<Box<UserId>>,
|
||||||
|
#[arg(short, long)]
|
||||||
|
push_key: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Queries database for `servernameevent_data` which are the queued up
|
||||||
|
/// requests that will eventually be sent
|
||||||
|
///
|
||||||
|
/// This command takes only *one* format of these arguments:
|
||||||
|
///
|
||||||
|
/// appservice_id
|
||||||
|
/// server_name
|
||||||
|
/// user_id AND push_key
|
||||||
|
///
|
||||||
|
/// See src/service/sending/mod.rs for the definition of the `Destination`
|
||||||
|
/// enum
|
||||||
|
QueuedRequests {
|
||||||
|
#[arg(short, long)]
|
||||||
|
appservice_id: Option<String>,
|
||||||
|
#[arg(short, long)]
|
||||||
|
server_name: Option<Box<ServerName>>,
|
||||||
|
#[arg(short, long)]
|
||||||
|
user_id: Option<Box<UserId>>,
|
||||||
|
#[arg(short, long)]
|
||||||
|
push_key: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
GetLatestEduCount {
|
||||||
|
server_name: Box<ServerName>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// All the getters and iterators in key_value/sending.rs
|
/// All the getters and iterators in key_value/sending.rs
|
||||||
pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
Sending::ActiveRequests => {
|
SendingCommand::ActiveRequests => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().sending.db.active_requests();
|
let results = services.sending.db.active_requests();
|
||||||
let active_requests: Result<Vec<(_, _, _)>> = results.collect();
|
let active_requests: Result<Vec<(_, _, _)>> = results.collect();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
@@ -16,7 +75,7 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Sending::QueuedRequests {
|
SendingCommand::QueuedRequests {
|
||||||
appservice_id,
|
appservice_id,
|
||||||
server_name,
|
server_name,
|
||||||
user_id,
|
user_id,
|
||||||
@@ -38,12 +97,12 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.sending
|
.sending
|
||||||
.db
|
.db
|
||||||
.queued_requests(&Destination::Appservice(appservice_id))
|
.queued_requests(&Destination::Appservice(appservice_id))
|
||||||
},
|
},
|
||||||
(None, Some(server_name), None, None) => services()
|
(None, Some(server_name), None, None) => services
|
||||||
.sending
|
.sending
|
||||||
.db
|
.db
|
||||||
.queued_requests(&Destination::Normal(server_name.into())),
|
.queued_requests(&Destination::Normal(server_name.into())),
|
||||||
@@ -55,7 +114,7 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.sending
|
.sending
|
||||||
.db
|
.db
|
||||||
.queued_requests(&Destination::Push(user_id.into(), push_key))
|
.queued_requests(&Destination::Push(user_id.into(), push_key))
|
||||||
@@ -81,7 +140,7 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{queued_requests:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{queued_requests:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Sending::ActiveRequestsFor {
|
SendingCommand::ActiveRequestsFor {
|
||||||
appservice_id,
|
appservice_id,
|
||||||
server_name,
|
server_name,
|
||||||
user_id,
|
user_id,
|
||||||
@@ -104,12 +163,12 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.sending
|
.sending
|
||||||
.db
|
.db
|
||||||
.active_requests_for(&Destination::Appservice(appservice_id))
|
.active_requests_for(&Destination::Appservice(appservice_id))
|
||||||
},
|
},
|
||||||
(None, Some(server_name), None, None) => services()
|
(None, Some(server_name), None, None) => services
|
||||||
.sending
|
.sending
|
||||||
.db
|
.db
|
||||||
.active_requests_for(&Destination::Normal(server_name.into())),
|
.active_requests_for(&Destination::Normal(server_name.into())),
|
||||||
@@ -121,7 +180,7 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.sending
|
.sending
|
||||||
.db
|
.db
|
||||||
.active_requests_for(&Destination::Push(user_id.into(), push_key))
|
.active_requests_for(&Destination::Push(user_id.into(), push_key))
|
||||||
@@ -147,11 +206,11 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
|||||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Sending::GetLatestEduCount {
|
SendingCommand::GetLatestEduCount {
|
||||||
server_name,
|
server_name,
|
||||||
} => {
|
} => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().sending.db.get_latest_educount(&server_name);
|
let results = services.sending.db.get_latest_educount(&server_name);
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
use super::Users;
|
use crate::Command;
|
||||||
use crate::{services, Result};
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
/// All the getters and iterators from src/database/key_value/users.rs
|
||||||
|
pub(crate) enum UsersCommand {
|
||||||
|
Iter,
|
||||||
|
}
|
||||||
|
|
||||||
/// All the getters and iterators in key_value/users.rs
|
/// All the getters and iterators in key_value/users.rs
|
||||||
pub(super) async fn users(subcommand: Users) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(subcommand: UsersCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
Users::Iter => {
|
UsersCommand::Iter => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services().users.db.iter();
|
let results = services.users.db.iter();
|
||||||
let users = results.collect::<Vec<_>>();
|
let users = results.collect::<Vec<_>>();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,49 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId};
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
|
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId, RoomId};
|
||||||
|
|
||||||
use super::RoomAliasCommand;
|
use crate::{escape_html, Command};
|
||||||
use crate::{escape_html, services, Result};
|
|
||||||
|
|
||||||
pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[derive(Debug, Subcommand)]
|
||||||
let server_user = &services().globals.server_user;
|
pub(crate) enum RoomAliasCommand {
|
||||||
|
/// - Make an alias point to a room.
|
||||||
|
Set {
|
||||||
|
#[arg(short, long)]
|
||||||
|
/// Set the alias even if a room is already using it
|
||||||
|
force: bool,
|
||||||
|
|
||||||
|
/// The room id to set the alias on
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
|
||||||
|
/// The alias localpart to use (`alias`, not `#alias:servername.tld`)
|
||||||
|
room_alias_localpart: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Remove a local alias
|
||||||
|
Remove {
|
||||||
|
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
|
||||||
|
room_alias_localpart: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Show which room is using an alias
|
||||||
|
Which {
|
||||||
|
/// The alias localpart to look up (`alias`, not
|
||||||
|
/// `#alias:servername.tld`)
|
||||||
|
room_alias_localpart: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - List aliases currently being used
|
||||||
|
List {
|
||||||
|
/// If set, only list the aliases for this room
|
||||||
|
room_id: Option<Box<RoomId>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
|
let server_user = &services.globals.server_user;
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
RoomAliasCommand::Set {
|
RoomAliasCommand::Set {
|
||||||
@@ -19,7 +56,7 @@ pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
|
|||||||
| RoomAliasCommand::Which {
|
| RoomAliasCommand::Which {
|
||||||
ref room_alias_localpart,
|
ref room_alias_localpart,
|
||||||
} => {
|
} => {
|
||||||
let room_alias_str = format!("#{}:{}", room_alias_localpart, services().globals.server_name());
|
let room_alias_str = format!("#{}:{}", room_alias_localpart, services.globals.server_name());
|
||||||
let room_alias = match RoomAliasId::parse_box(room_alias_str) {
|
let room_alias = match RoomAliasId::parse_box(room_alias_str) {
|
||||||
Ok(alias) => alias,
|
Ok(alias) => alias,
|
||||||
Err(err) => return Ok(RoomMessageEventContent::text_plain(format!("Failed to parse alias: {err}"))),
|
Err(err) => return Ok(RoomMessageEventContent::text_plain(format!("Failed to parse alias: {err}"))),
|
||||||
@@ -29,8 +66,8 @@ pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
|
|||||||
force,
|
force,
|
||||||
room_id,
|
room_id,
|
||||||
..
|
..
|
||||||
} => match (force, services().rooms.alias.resolve_local_alias(&room_alias)) {
|
} => match (force, services.rooms.alias.resolve_local_alias(&room_alias)) {
|
||||||
(true, Ok(Some(id))) => match services()
|
(true, Ok(Some(id))) => match services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.set_alias(&room_alias, &room_id, server_user)
|
.set_alias(&room_alias, &room_id, server_user)
|
||||||
@@ -43,7 +80,7 @@ pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
|
|||||||
(false, Ok(Some(id))) => Ok(RoomMessageEventContent::text_plain(format!(
|
(false, Ok(Some(id))) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Refusing to overwrite in use alias for {id}, use -f or --force to overwrite"
|
"Refusing to overwrite in use alias for {id}, use -f or --force to overwrite"
|
||||||
))),
|
))),
|
||||||
(_, Ok(None)) => match services()
|
(_, Ok(None)) => match services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.set_alias(&room_alias, &room_id, server_user)
|
.set_alias(&room_alias, &room_id, server_user)
|
||||||
@@ -55,8 +92,8 @@ pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
|
|||||||
},
|
},
|
||||||
RoomAliasCommand::Remove {
|
RoomAliasCommand::Remove {
|
||||||
..
|
..
|
||||||
} => match services().rooms.alias.resolve_local_alias(&room_alias) {
|
} => match services.rooms.alias.resolve_local_alias(&room_alias) {
|
||||||
Ok(Some(id)) => match services()
|
Ok(Some(id)) => match services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.remove_alias(&room_alias, server_user)
|
.remove_alias(&room_alias, server_user)
|
||||||
@@ -70,7 +107,7 @@ pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
|
|||||||
},
|
},
|
||||||
RoomAliasCommand::Which {
|
RoomAliasCommand::Which {
|
||||||
..
|
..
|
||||||
} => match services().rooms.alias.resolve_local_alias(&room_alias) {
|
} => match services.rooms.alias.resolve_local_alias(&room_alias) {
|
||||||
Ok(Some(id)) => Ok(RoomMessageEventContent::text_plain(format!("Alias resolves to {id}"))),
|
Ok(Some(id)) => Ok(RoomMessageEventContent::text_plain(format!("Alias resolves to {id}"))),
|
||||||
Ok(None) => Ok(RoomMessageEventContent::text_plain("Alias isn't in use.")),
|
Ok(None) => Ok(RoomMessageEventContent::text_plain("Alias isn't in use.")),
|
||||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to lookup alias: {err}"))),
|
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to lookup alias: {err}"))),
|
||||||
@@ -84,7 +121,7 @@ pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
|
|||||||
room_id,
|
room_id,
|
||||||
} => {
|
} => {
|
||||||
if let Some(room_id) = room_id {
|
if let Some(room_id) = room_id {
|
||||||
let aliases = services()
|
let aliases = services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.local_aliases_for_room(&room_id)
|
.local_aliases_for_room(&room_id)
|
||||||
@@ -109,14 +146,14 @@ pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Resu
|
|||||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to list aliases: {err}"))),
|
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to list aliases: {err}"))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let aliases = services()
|
let aliases = services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.all_local_aliases()
|
.all_local_aliases()
|
||||||
.collect::<Result<Vec<_>, _>>();
|
.collect::<Result<Vec<_>, _>>();
|
||||||
match aliases {
|
match aliases {
|
||||||
Ok(aliases) => {
|
Ok(aliases) => {
|
||||||
let server_name = services().globals.server_name();
|
let server_name = services.globals.server_name();
|
||||||
let plain_list = aliases
|
let plain_list = aliases
|
||||||
.iter()
|
.iter()
|
||||||
.fold(String::new(), |mut output, (alias, id)| {
|
.fold(String::new(), |mut output, (alias, id)| {
|
||||||
@@ -1,18 +1,51 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
|
use conduit::Result;
|
||||||
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
|
use crate::{admin_command, escape_html, get_room_info, PAGE_SIZE};
|
||||||
|
|
||||||
pub(super) async fn list(_body: Vec<&str>, page: Option<usize>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn list_rooms(
|
||||||
|
&self, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||||
let page = page.unwrap_or(1);
|
let page = page.unwrap_or(1);
|
||||||
let mut rooms = services()
|
let mut rooms = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.metadata
|
.metadata
|
||||||
.iter_ids()
|
.iter_ids()
|
||||||
.filter_map(Result::ok)
|
.filter_map(|room_id| {
|
||||||
.map(|id: OwnedRoomId| get_room_info(&id))
|
room_id
|
||||||
|
.ok()
|
||||||
|
.filter(|room_id| {
|
||||||
|
if exclude_disabled
|
||||||
|
&& self
|
||||||
|
.services
|
||||||
|
.rooms
|
||||||
|
.metadata
|
||||||
|
.is_disabled(room_id)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if exclude_banned
|
||||||
|
&& self
|
||||||
|
.services
|
||||||
|
.rooms
|
||||||
|
.metadata
|
||||||
|
.is_banned(room_id)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
})
|
||||||
|
.map(|room_id| get_room_info(self.services, &room_id))
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
rooms.sort_by_key(|r| r.1);
|
rooms.sort_by_key(|r| r.1);
|
||||||
rooms.reverse();
|
rooms.reverse();
|
||||||
@@ -1,21 +1,43 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
|
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId};
|
||||||
|
|
||||||
use super::RoomDirectoryCommand;
|
use crate::{escape_html, get_room_info, Command, PAGE_SIZE};
|
||||||
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
|
|
||||||
|
|
||||||
pub(super) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub(crate) enum RoomDirectoryCommand {
|
||||||
|
/// - Publish a room to the room directory
|
||||||
|
Publish {
|
||||||
|
/// The room id of the room to publish
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Unpublish a room to the room directory
|
||||||
|
Unpublish {
|
||||||
|
/// The room id of the room to unpublish
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - List rooms that are published
|
||||||
|
List {
|
||||||
|
page: Option<usize>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn process(command: RoomDirectoryCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||||
|
let services = context.services;
|
||||||
match command {
|
match command {
|
||||||
RoomDirectoryCommand::Publish {
|
RoomDirectoryCommand::Publish {
|
||||||
room_id,
|
room_id,
|
||||||
} => match services().rooms.directory.set_public(&room_id) {
|
} => match services.rooms.directory.set_public(&room_id) {
|
||||||
Ok(()) => Ok(RoomMessageEventContent::text_plain("Room published")),
|
Ok(()) => Ok(RoomMessageEventContent::text_plain("Room published")),
|
||||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to update room: {err}"))),
|
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to update room: {err}"))),
|
||||||
},
|
},
|
||||||
RoomDirectoryCommand::Unpublish {
|
RoomDirectoryCommand::Unpublish {
|
||||||
room_id,
|
room_id,
|
||||||
} => match services().rooms.directory.set_not_public(&room_id) {
|
} => match services.rooms.directory.set_not_public(&room_id) {
|
||||||
Ok(()) => Ok(RoomMessageEventContent::text_plain("Room unpublished")),
|
Ok(()) => Ok(RoomMessageEventContent::text_plain("Room unpublished")),
|
||||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to update room: {err}"))),
|
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to update room: {err}"))),
|
||||||
},
|
},
|
||||||
@@ -24,12 +46,12 @@ pub(super) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) ->
|
|||||||
} => {
|
} => {
|
||||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||||
let page = page.unwrap_or(1);
|
let page = page.unwrap_or(1);
|
||||||
let mut rooms = services()
|
let mut rooms = services
|
||||||
.rooms
|
.rooms
|
||||||
.directory
|
.directory
|
||||||
.public_rooms()
|
.public_rooms()
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.map(|id: OwnedRoomId| get_room_info(&id))
|
.map(|id: OwnedRoomId| get_room_info(services, &id))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
rooms.sort_by_key(|r| r.1);
|
rooms.sort_by_key(|r| r.1);
|
||||||
rooms.reverse();
|
rooms.reverse();
|
||||||
@@ -1,22 +1,30 @@
|
|||||||
|
use clap::Subcommand;
|
||||||
|
use conduit::Result;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId};
|
use ruma::{events::room::message::RoomMessageEventContent, RoomId};
|
||||||
use service::services;
|
|
||||||
|
|
||||||
use super::RoomInfoCommand;
|
use crate::{admin_command, admin_command_dispatch};
|
||||||
use crate::Result;
|
|
||||||
|
|
||||||
pub(super) async fn process(command: RoomInfoCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command_dispatch]
|
||||||
match command {
|
#[derive(Debug, Subcommand)]
|
||||||
RoomInfoCommand::ListJoinedMembers {
|
pub(crate) enum RoomInfoCommand {
|
||||||
room_id,
|
/// - List joined members in a room
|
||||||
} => list_joined_members(body, room_id).await,
|
ListJoinedMembers {
|
||||||
RoomInfoCommand::ViewRoomTopic {
|
room_id: Box<RoomId>,
|
||||||
room_id,
|
},
|
||||||
} => view_room_topic(body, room_id).await,
|
|
||||||
}
|
/// - Displays room topic
|
||||||
|
///
|
||||||
|
/// Room topics can be huge, so this is in its
|
||||||
|
/// own separate command
|
||||||
|
ViewRoomTopic {
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_joined_members(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let room_name = services()
|
async fn list_joined_members(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||||
|
let room_name = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_name(&room_id)
|
.get_name(&room_id)
|
||||||
@@ -24,7 +32,8 @@ async fn list_joined_members(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<R
|
|||||||
.flatten()
|
.flatten()
|
||||||
.unwrap_or_else(|| room_id.to_string());
|
.unwrap_or_else(|| room_id.to_string());
|
||||||
|
|
||||||
let members = services()
|
let members = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&room_id)
|
.room_members(&room_id)
|
||||||
@@ -35,7 +44,7 @@ async fn list_joined_members(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<R
|
|||||||
.map(|user_id| {
|
.map(|user_id| {
|
||||||
(
|
(
|
||||||
user_id.clone(),
|
user_id.clone(),
|
||||||
services()
|
self.services
|
||||||
.users
|
.users
|
||||||
.displayname(&user_id)
|
.displayname(&user_id)
|
||||||
.unwrap_or(None)
|
.unwrap_or(None)
|
||||||
@@ -58,12 +67,18 @@ async fn list_joined_members(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<R
|
|||||||
Ok(RoomMessageEventContent::notice_markdown(output_plain))
|
Ok(RoomMessageEventContent::notice_markdown(output_plain))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn view_room_topic(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let Some(room_topic) = services().rooms.state_accessor.get_room_topic(&room_id)? else {
|
async fn view_room_topic(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||||
|
let Some(room_topic) = self
|
||||||
|
.services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.get_room_topic(&room_id)?
|
||||||
|
else {
|
||||||
return Ok(RoomMessageEventContent::text_plain("Room does not have a room topic set."));
|
return Ok(RoomMessageEventContent::text_plain("Room does not have a room topic set."));
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"Room topic:\n\n```{room_topic}\n```"
|
"Room topic:\n```\n{room_topic}\n```"
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
+21
-158
@@ -1,21 +1,32 @@
|
|||||||
mod room_alias_commands;
|
mod alias;
|
||||||
mod room_commands;
|
mod commands;
|
||||||
mod room_directory_commands;
|
mod directory;
|
||||||
mod room_info_commands;
|
mod info;
|
||||||
mod room_moderation_commands;
|
mod moderation;
|
||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId, RoomOrAliasId};
|
|
||||||
|
|
||||||
use self::room_commands::list;
|
use self::{
|
||||||
|
alias::RoomAliasCommand, directory::RoomDirectoryCommand, info::RoomInfoCommand, moderation::RoomModerationCommand,
|
||||||
|
};
|
||||||
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[admin_command_dispatch]
|
||||||
#[derive(Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub(super) enum RoomCommand {
|
pub(super) enum RoomCommand {
|
||||||
/// - List all rooms the server knows about
|
/// - List all rooms the server knows about
|
||||||
List {
|
#[clap(alias = "list")]
|
||||||
|
ListRooms {
|
||||||
page: Option<usize>,
|
page: Option<usize>,
|
||||||
|
|
||||||
|
/// Excludes rooms that we have federation disabled with
|
||||||
|
#[arg(long)]
|
||||||
|
exclude_disabled: bool,
|
||||||
|
|
||||||
|
/// Excludes rooms that we have banned
|
||||||
|
#[arg(long)]
|
||||||
|
exclude_banned: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
@@ -34,151 +45,3 @@ pub(super) enum RoomCommand {
|
|||||||
/// - Manage the room directory
|
/// - Manage the room directory
|
||||||
Directory(RoomDirectoryCommand),
|
Directory(RoomDirectoryCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub(super) enum RoomInfoCommand {
|
|
||||||
/// - List joined members in a room
|
|
||||||
ListJoinedMembers {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Displays room topic
|
|
||||||
///
|
|
||||||
/// Room topics can be huge, so this is in its
|
|
||||||
/// own separate command
|
|
||||||
ViewRoomTopic {
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub(super) enum RoomAliasCommand {
|
|
||||||
/// - Make an alias point to a room.
|
|
||||||
Set {
|
|
||||||
#[arg(short, long)]
|
|
||||||
/// Set the alias even if a room is already using it
|
|
||||||
force: bool,
|
|
||||||
|
|
||||||
/// The room id to set the alias on
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
|
|
||||||
/// The alias localpart to use (`alias`, not `#alias:servername.tld`)
|
|
||||||
room_alias_localpart: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Remove a local alias
|
|
||||||
Remove {
|
|
||||||
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
|
|
||||||
room_alias_localpart: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Show which room is using an alias
|
|
||||||
Which {
|
|
||||||
/// The alias localpart to look up (`alias`, not
|
|
||||||
/// `#alias:servername.tld`)
|
|
||||||
room_alias_localpart: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - List aliases currently being used
|
|
||||||
List {
|
|
||||||
/// If set, only list the aliases for this room
|
|
||||||
room_id: Option<Box<RoomId>>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub(super) enum RoomDirectoryCommand {
|
|
||||||
/// - Publish a room to the room directory
|
|
||||||
Publish {
|
|
||||||
/// The room id of the room to publish
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Unpublish a room to the room directory
|
|
||||||
Unpublish {
|
|
||||||
/// The room id of the room to unpublish
|
|
||||||
room_id: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - List rooms that are published
|
|
||||||
List {
|
|
||||||
page: Option<usize>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub(super) enum RoomModerationCommand {
|
|
||||||
/// - Bans a room from local users joining and evicts all our local users
|
|
||||||
/// from the room. Also blocks any invites (local and remote) for the
|
|
||||||
/// banned room.
|
|
||||||
///
|
|
||||||
/// Server admins (users in the conduwuit admin room) will not be evicted
|
|
||||||
/// and server admins can still join the room. To evict admins too, use
|
|
||||||
/// --force (also ignores errors) To disable incoming federation of the
|
|
||||||
/// room, use --disable-federation
|
|
||||||
BanRoom {
|
|
||||||
#[arg(short, long)]
|
|
||||||
/// Evicts admins out of the room and ignores any potential errors when
|
|
||||||
/// making our local users leave the room
|
|
||||||
force: bool,
|
|
||||||
|
|
||||||
#[arg(long)]
|
|
||||||
/// Disables incoming federation of the room after banning and evicting
|
|
||||||
/// users
|
|
||||||
disable_federation: bool,
|
|
||||||
|
|
||||||
/// The room in the format of `!roomid:example.com` or a room alias in
|
|
||||||
/// the format of `#roomalias:example.com`
|
|
||||||
room: Box<RoomOrAliasId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Bans a list of rooms (room IDs and room aliases) from a newline
|
|
||||||
/// delimited codeblock similar to `user deactivate-all`
|
|
||||||
BanListOfRooms {
|
|
||||||
#[arg(short, long)]
|
|
||||||
/// Evicts admins out of the room and ignores any potential errors when
|
|
||||||
/// making our local users leave the room
|
|
||||||
force: bool,
|
|
||||||
|
|
||||||
#[arg(long)]
|
|
||||||
/// Disables incoming federation of the room after banning and evicting
|
|
||||||
/// users
|
|
||||||
disable_federation: bool,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Unbans a room to allow local users to join again
|
|
||||||
///
|
|
||||||
/// To re-enable incoming federation of the room, use --enable-federation
|
|
||||||
UnbanRoom {
|
|
||||||
#[arg(long)]
|
|
||||||
/// Enables incoming federation of the room after unbanning
|
|
||||||
enable_federation: bool,
|
|
||||||
|
|
||||||
/// The room in the format of `!roomid:example.com` or a room alias in
|
|
||||||
/// the format of `#roomalias:example.com`
|
|
||||||
room: Box<RoomOrAliasId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - List of all rooms we have banned
|
|
||||||
ListBannedRooms,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) async fn process(command: RoomCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
Ok(match command {
|
|
||||||
RoomCommand::Info(command) => room_info_commands::process(command, body).await?,
|
|
||||||
|
|
||||||
RoomCommand::Alias(command) => room_alias_commands::process(command, body).await?,
|
|
||||||
|
|
||||||
RoomCommand::Directory(command) => room_directory_commands::process(command, body).await?,
|
|
||||||
|
|
||||||
RoomCommand::Moderation(command) => room_moderation_commands::process(command, body).await?,
|
|
||||||
|
|
||||||
RoomCommand::List {
|
|
||||||
page,
|
|
||||||
} => list(body, page).await?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,39 +1,77 @@
|
|||||||
use api::client::leave_room;
|
use api::client::leave_room;
|
||||||
use ruma::{
|
use clap::Subcommand;
|
||||||
events::room::message::RoomMessageEventContent, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomOrAliasId,
|
use conduit::{debug, error, info, warn, Result};
|
||||||
};
|
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomAliasId, RoomId, RoomOrAliasId};
|
||||||
use tracing::{debug, error, info, warn};
|
|
||||||
|
|
||||||
use super::{super::Service, RoomModerationCommand};
|
use crate::{admin_command, admin_command_dispatch, get_room_info};
|
||||||
use crate::{get_room_info, services, user_is_local, Result};
|
|
||||||
|
|
||||||
pub(super) async fn process(command: RoomModerationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command_dispatch]
|
||||||
match command {
|
#[derive(Debug, Subcommand)]
|
||||||
RoomModerationCommand::BanRoom {
|
pub(crate) enum RoomModerationCommand {
|
||||||
force,
|
/// - Bans a room from local users joining and evicts all our local users
|
||||||
room,
|
/// from the room. Also blocks any invites (local and remote) for the
|
||||||
disable_federation,
|
/// banned room.
|
||||||
} => ban_room(body, force, room, disable_federation).await,
|
///
|
||||||
RoomModerationCommand::BanListOfRooms {
|
/// Server admins (users in the conduwuit admin room) will not be evicted
|
||||||
force,
|
/// and server admins can still join the room. To evict admins too, use
|
||||||
disable_federation,
|
/// --force (also ignores errors) To disable incoming federation of the
|
||||||
} => ban_list_of_rooms(body, force, disable_federation).await,
|
/// room, use --disable-federation
|
||||||
RoomModerationCommand::UnbanRoom {
|
BanRoom {
|
||||||
room,
|
#[arg(short, long)]
|
||||||
enable_federation,
|
/// Evicts admins out of the room and ignores any potential errors when
|
||||||
} => unban_room(body, room, enable_federation).await,
|
/// making our local users leave the room
|
||||||
RoomModerationCommand::ListBannedRooms => list_banned_rooms(body).await,
|
force: bool,
|
||||||
}
|
|
||||||
|
#[arg(long)]
|
||||||
|
/// Disables incoming federation of the room after banning and evicting
|
||||||
|
/// users
|
||||||
|
disable_federation: bool,
|
||||||
|
|
||||||
|
/// The room in the format of `!roomid:example.com` or a room alias in
|
||||||
|
/// the format of `#roomalias:example.com`
|
||||||
|
room: Box<RoomOrAliasId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Bans a list of rooms (room IDs and room aliases) from a newline
|
||||||
|
/// delimited codeblock similar to `user deactivate-all`
|
||||||
|
BanListOfRooms {
|
||||||
|
#[arg(short, long)]
|
||||||
|
/// Evicts admins out of the room and ignores any potential errors when
|
||||||
|
/// making our local users leave the room
|
||||||
|
force: bool,
|
||||||
|
|
||||||
|
#[arg(long)]
|
||||||
|
/// Disables incoming federation of the room after banning and evicting
|
||||||
|
/// users
|
||||||
|
disable_federation: bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Unbans a room to allow local users to join again
|
||||||
|
///
|
||||||
|
/// To re-enable incoming federation of the room, use --enable-federation
|
||||||
|
UnbanRoom {
|
||||||
|
#[arg(long)]
|
||||||
|
/// Enables incoming federation of the room after unbanning
|
||||||
|
enable_federation: bool,
|
||||||
|
|
||||||
|
/// The room in the format of `!roomid:example.com` or a room alias in
|
||||||
|
/// the format of `#roomalias:example.com`
|
||||||
|
room: Box<RoomOrAliasId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - List of all rooms we have banned
|
||||||
|
ListBannedRooms,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
async fn ban_room(
|
async fn ban_room(
|
||||||
_body: Vec<&str>, force: bool, room: Box<RoomOrAliasId>, disable_federation: bool,
|
&self, force: bool, disable_federation: bool, room: Box<RoomOrAliasId>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
debug!("Got room alias or ID: {}", room);
|
debug!("Got room alias or ID: {}", room);
|
||||||
|
|
||||||
let admin_room_alias = &services().globals.admin_alias;
|
let admin_room_alias = &self.services.globals.admin_alias;
|
||||||
|
|
||||||
if let Some(admin_room_id) = Service::get_admin_room()? {
|
if let Some(admin_room_id) = self.services.admin.get_admin_room()? {
|
||||||
if room.to_string().eq(&admin_room_id) || room.to_string().eq(admin_room_alias) {
|
if room.to_string().eq(&admin_room_id) || room.to_string().eq(admin_room_alias) {
|
||||||
return Ok(RoomMessageEventContent::text_plain("Not allowed to ban the admin room."));
|
return Ok(RoomMessageEventContent::text_plain("Not allowed to ban the admin room."));
|
||||||
}
|
}
|
||||||
@@ -52,7 +90,7 @@ async fn ban_room(
|
|||||||
|
|
||||||
debug!("Room specified is a room ID, banning room ID");
|
debug!("Room specified is a room ID, banning room ID");
|
||||||
|
|
||||||
services().rooms.metadata.ban_room(&room_id, true)?;
|
self.services.rooms.metadata.ban_room(&room_id, true)?;
|
||||||
|
|
||||||
room_id
|
room_id
|
||||||
} else if room.is_room_alias_id() {
|
} else if room.is_room_alias_id() {
|
||||||
@@ -71,12 +109,13 @@ async fn ban_room(
|
|||||||
get_alias_helper to fetch room ID remotely"
|
get_alias_helper to fetch room ID remotely"
|
||||||
);
|
);
|
||||||
|
|
||||||
let room_id = if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? {
|
let room_id = if let Some(room_id) = self.services.rooms.alias.resolve_local_alias(&room_alias)? {
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
||||||
|
|
||||||
match services()
|
match self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_alias(&room_alias, None)
|
.resolve_alias(&room_alias, None)
|
||||||
@@ -94,7 +133,7 @@ async fn ban_room(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
services().rooms.metadata.ban_room(&room_id, true)?;
|
self.services.rooms.metadata.ban_room(&room_id, true)?;
|
||||||
|
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
@@ -106,58 +145,56 @@ async fn ban_room(
|
|||||||
|
|
||||||
debug!("Making all users leave the room {}", &room);
|
debug!("Making all users leave the room {}", &room);
|
||||||
if force {
|
if force {
|
||||||
for local_user in services()
|
for local_user in self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&room_id)
|
.room_members(&room_id)
|
||||||
.filter_map(|user| {
|
.filter_map(|user| {
|
||||||
user.ok().filter(|local_user| {
|
user.ok().filter(|local_user| {
|
||||||
user_is_local(local_user)
|
self.services.globals.user_is_local(local_user)
|
||||||
// additional wrapped check here is to avoid adding remote users
|
// additional wrapped check here is to avoid adding remote users
|
||||||
// who are in the admin room to the list of local users (would
|
// who are in the admin room to the list of local users (would
|
||||||
// fail auth check)
|
// fail auth check)
|
||||||
&& (user_is_local(local_user)
|
&& (self.services.globals.user_is_local(local_user)
|
||||||
// since this is a force operation, assume user is an admin
|
// since this is a force operation, assume user is an admin
|
||||||
// if somehow this fails
|
// if somehow this fails
|
||||||
&& services()
|
&& self.services
|
||||||
.users
|
.users
|
||||||
.is_admin(local_user)
|
.is_admin(local_user)
|
||||||
.unwrap_or(true))
|
.unwrap_or(true))
|
||||||
})
|
})
|
||||||
})
|
}) {
|
||||||
.collect::<Vec<OwnedUserId>>()
|
|
||||||
{
|
|
||||||
debug!(
|
debug!(
|
||||||
"Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)",
|
"Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)",
|
||||||
&local_user, &room_id
|
&local_user, &room_id
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
|
||||||
warn!(%e, "Failed to leave room");
|
warn!(%e, "Failed to leave room");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for local_user in services()
|
for local_user in self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&room_id)
|
.room_members(&room_id)
|
||||||
.filter_map(|user| {
|
.filter_map(|user| {
|
||||||
user.ok().filter(|local_user| {
|
user.ok().filter(|local_user| {
|
||||||
local_user.server_name() == services().globals.server_name()
|
local_user.server_name() == self.services.globals.server_name()
|
||||||
// additional wrapped check here is to avoid adding remote users
|
// additional wrapped check here is to avoid adding remote users
|
||||||
// who are in the admin room to the list of local users (would fail auth check)
|
// who are in the admin room to the list of local users (would fail auth check)
|
||||||
&& (local_user.server_name()
|
&& (local_user.server_name()
|
||||||
== services().globals.server_name()
|
== self.services.globals.server_name()
|
||||||
&& !services()
|
&& !self.services
|
||||||
.users
|
.users
|
||||||
.is_admin(local_user)
|
.is_admin(local_user)
|
||||||
.unwrap_or(false))
|
.unwrap_or(false))
|
||||||
})
|
})
|
||||||
})
|
}) {
|
||||||
.collect::<Vec<OwnedUserId>>()
|
|
||||||
{
|
|
||||||
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
||||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
|
||||||
error!(
|
error!(
|
||||||
"Error attempting to make local user {} leave room {} during room banning: {}",
|
"Error attempting to make local user {} leave room {} during room banning: {}",
|
||||||
&local_user, &room_id, e
|
&local_user, &room_id, e
|
||||||
@@ -172,7 +209,7 @@ async fn ban_room(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if disable_federation {
|
if disable_federation {
|
||||||
services().rooms.metadata.disable_room(&room_id, true)?;
|
self.services.rooms.metadata.disable_room(&room_id, true)?;
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Room banned, removed all our local users, and disabled incoming federation with room.",
|
"Room banned, removed all our local users, and disabled incoming federation with room.",
|
||||||
));
|
));
|
||||||
@@ -184,16 +221,22 @@ async fn ban_room(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: bool) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Result<RoomMessageEventContent> {
|
||||||
|
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let rooms_s = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>();
|
let rooms_s = self
|
||||||
|
.body
|
||||||
|
.to_vec()
|
||||||
|
.drain(1..self.body.len().saturating_sub(1))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let admin_room_alias = &services().globals.admin_alias;
|
let admin_room_alias = &self.services.globals.admin_alias;
|
||||||
|
|
||||||
let mut room_ban_count: usize = 0;
|
let mut room_ban_count: usize = 0;
|
||||||
let mut room_ids: Vec<OwnedRoomId> = Vec::new();
|
let mut room_ids: Vec<OwnedRoomId> = Vec::new();
|
||||||
@@ -201,7 +244,7 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
|||||||
for &room in &rooms_s {
|
for &room in &rooms_s {
|
||||||
match <&RoomOrAliasId>::try_from(room) {
|
match <&RoomOrAliasId>::try_from(room) {
|
||||||
Ok(room_alias_or_id) => {
|
Ok(room_alias_or_id) => {
|
||||||
if let Some(admin_room_id) = Service::get_admin_room()? {
|
if let Some(admin_room_id) = self.services.admin.get_admin_room()? {
|
||||||
if room.to_owned().eq(&admin_room_id) || room.to_owned().eq(admin_room_alias) {
|
if room.to_owned().eq(&admin_room_id) || room.to_owned().eq(admin_room_alias) {
|
||||||
info!("User specified admin room in bulk ban list, ignoring");
|
info!("User specified admin room in bulk ban list, ignoring");
|
||||||
continue;
|
continue;
|
||||||
@@ -234,7 +277,7 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
|||||||
match RoomAliasId::parse(room_alias_or_id) {
|
match RoomAliasId::parse(room_alias_or_id) {
|
||||||
Ok(room_alias) => {
|
Ok(room_alias) => {
|
||||||
let room_id =
|
let room_id =
|
||||||
if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? {
|
if let Some(room_id) = self.services.rooms.alias.resolve_local_alias(&room_alias)? {
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
@@ -242,7 +285,8 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
|||||||
ID over federation"
|
ID over federation"
|
||||||
);
|
);
|
||||||
|
|
||||||
match services()
|
match self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_alias(&room_alias, None)
|
.resolve_alias(&room_alias, None)
|
||||||
@@ -306,66 +350,70 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
for room_id in room_ids {
|
for room_id in room_ids {
|
||||||
if services().rooms.metadata.ban_room(&room_id, true).is_ok() {
|
if self
|
||||||
|
.services
|
||||||
|
.rooms
|
||||||
|
.metadata
|
||||||
|
.ban_room(&room_id, true)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
debug!("Banned {room_id} successfully");
|
debug!("Banned {room_id} successfully");
|
||||||
room_ban_count = room_ban_count.saturating_add(1);
|
room_ban_count = room_ban_count.saturating_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Making all users leave the room {}", &room_id);
|
debug!("Making all users leave the room {}", &room_id);
|
||||||
if force {
|
if force {
|
||||||
for local_user in services()
|
for local_user in self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&room_id)
|
.room_members(&room_id)
|
||||||
.filter_map(|user| {
|
.filter_map(|user| {
|
||||||
user.ok().filter(|local_user| {
|
user.ok().filter(|local_user| {
|
||||||
local_user.server_name() == services().globals.server_name()
|
local_user.server_name() == self.services.globals.server_name()
|
||||||
// additional wrapped check here is to avoid adding remote
|
// additional wrapped check here is to avoid adding remote
|
||||||
// users who are in the admin room to the list of local
|
// users who are in the admin room to the list of local
|
||||||
// users (would fail auth check)
|
// users (would fail auth check)
|
||||||
&& (local_user.server_name()
|
&& (local_user.server_name()
|
||||||
== services().globals.server_name()
|
== self.services.globals.server_name()
|
||||||
// since this is a force operation, assume user is an
|
// since this is a force operation, assume user is an
|
||||||
// admin if somehow this fails
|
// admin if somehow this fails
|
||||||
&& services()
|
&& self.services
|
||||||
.users
|
.users
|
||||||
.is_admin(local_user)
|
.is_admin(local_user)
|
||||||
.unwrap_or(true))
|
.unwrap_or(true))
|
||||||
})
|
})
|
||||||
})
|
}) {
|
||||||
.collect::<Vec<OwnedUserId>>()
|
|
||||||
{
|
|
||||||
debug!(
|
debug!(
|
||||||
"Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)",
|
"Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)",
|
||||||
&local_user, room_id
|
&local_user, room_id
|
||||||
);
|
);
|
||||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
|
||||||
warn!(%e, "Failed to leave room");
|
warn!(%e, "Failed to leave room");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for local_user in services()
|
for local_user in self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&room_id)
|
.room_members(&room_id)
|
||||||
.filter_map(|user| {
|
.filter_map(|user| {
|
||||||
user.ok().filter(|local_user| {
|
user.ok().filter(|local_user| {
|
||||||
local_user.server_name() == services().globals.server_name()
|
local_user.server_name() == self.services.globals.server_name()
|
||||||
// additional wrapped check here is to avoid adding remote
|
// additional wrapped check here is to avoid adding remote
|
||||||
// users who are in the admin room to the list of local
|
// users who are in the admin room to the list of local
|
||||||
// users (would fail auth check)
|
// users (would fail auth check)
|
||||||
&& (local_user.server_name()
|
&& (local_user.server_name()
|
||||||
== services().globals.server_name()
|
== self.services.globals.server_name()
|
||||||
&& !services()
|
&& !self.services
|
||||||
.users
|
.users
|
||||||
.is_admin(local_user)
|
.is_admin(local_user)
|
||||||
.unwrap_or(false))
|
.unwrap_or(false))
|
||||||
})
|
})
|
||||||
})
|
}) {
|
||||||
.collect::<Vec<OwnedUserId>>()
|
|
||||||
{
|
|
||||||
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
||||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
|
||||||
error!(
|
error!(
|
||||||
"Error attempting to make local user {} leave room {} during bulk room banning: {}",
|
"Error attempting to make local user {} leave room {} during bulk room banning: {}",
|
||||||
&local_user, &room_id, e
|
&local_user, &room_id, e
|
||||||
@@ -381,7 +429,7 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if disable_federation {
|
if disable_federation {
|
||||||
services().rooms.metadata.disable_room(&room_id, true)?;
|
self.services.rooms.metadata.disable_room(&room_id, true)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,9 +445,8 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn unban_room(
|
#[admin_command]
|
||||||
_body: Vec<&str>, room: Box<RoomOrAliasId>, enable_federation: bool,
|
async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) -> Result<RoomMessageEventContent> {
|
||||||
) -> Result<RoomMessageEventContent> {
|
|
||||||
let room_id = if room.is_room_id() {
|
let room_id = if room.is_room_id() {
|
||||||
let room_id = match RoomId::parse(&room) {
|
let room_id = match RoomId::parse(&room) {
|
||||||
Ok(room_id) => room_id,
|
Ok(room_id) => room_id,
|
||||||
@@ -413,7 +460,7 @@ async fn unban_room(
|
|||||||
|
|
||||||
debug!("Room specified is a room ID, unbanning room ID");
|
debug!("Room specified is a room ID, unbanning room ID");
|
||||||
|
|
||||||
services().rooms.metadata.ban_room(&room_id, false)?;
|
self.services.rooms.metadata.ban_room(&room_id, false)?;
|
||||||
|
|
||||||
room_id
|
room_id
|
||||||
} else if room.is_room_alias_id() {
|
} else if room.is_room_alias_id() {
|
||||||
@@ -432,12 +479,13 @@ async fn unban_room(
|
|||||||
get_alias_helper to fetch room ID remotely"
|
get_alias_helper to fetch room ID remotely"
|
||||||
);
|
);
|
||||||
|
|
||||||
let room_id = if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? {
|
let room_id = if let Some(room_id) = self.services.rooms.alias.resolve_local_alias(&room_alias)? {
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
||||||
|
|
||||||
match services()
|
match self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_alias(&room_alias, None)
|
.resolve_alias(&room_alias, None)
|
||||||
@@ -455,7 +503,7 @@ async fn unban_room(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
services().rooms.metadata.ban_room(&room_id, false)?;
|
self.services.rooms.metadata.ban_room(&room_id, false)?;
|
||||||
|
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
@@ -466,7 +514,7 @@ async fn unban_room(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if enable_federation {
|
if enable_federation {
|
||||||
services().rooms.metadata.disable_room(&room_id, false)?;
|
self.services.rooms.metadata.disable_room(&room_id, false)?;
|
||||||
return Ok(RoomMessageEventContent::text_plain("Room unbanned."));
|
return Ok(RoomMessageEventContent::text_plain("Room unbanned."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,8 +524,10 @@ async fn unban_room(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_banned_rooms(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let rooms = services()
|
async fn list_banned_rooms(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let rooms = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.metadata
|
.metadata
|
||||||
.list_banned_rooms()
|
.list_banned_rooms()
|
||||||
@@ -491,7 +541,7 @@ async fn list_banned_rooms(_body: Vec<&str>) -> Result<RoomMessageEventContent>
|
|||||||
|
|
||||||
let mut rooms = room_ids
|
let mut rooms = room_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|room_id| get_room_info(&room_id))
|
.map(|room_id| get_room_info(self.services, &room_id))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
rooms.sort_by_key(|r| r.1);
|
rooms.sort_by_key(|r| r.1);
|
||||||
rooms.reverse();
|
rooms.reverse();
|
||||||
@@ -1,54 +1,92 @@
|
|||||||
use conduit::{warn, Error, Result};
|
use std::{fmt::Write, sync::Arc};
|
||||||
|
|
||||||
|
use conduit::{info, utils::time, warn, Err, Result};
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
use crate::services;
|
use crate::admin_command;
|
||||||
|
|
||||||
pub(super) async fn uptime(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let seconds = services()
|
pub(super) async fn uptime(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let elapsed = self
|
||||||
|
.services
|
||||||
.server
|
.server
|
||||||
.started
|
.started
|
||||||
.elapsed()
|
.elapsed()
|
||||||
.expect("standard duration")
|
.expect("standard duration");
|
||||||
.as_secs();
|
|
||||||
let result = format!(
|
|
||||||
"up {} days, {} hours, {} minutes, {} seconds.",
|
|
||||||
seconds / 86400,
|
|
||||||
(seconds % 86400) / 60 / 60,
|
|
||||||
(seconds % 3600) / 60,
|
|
||||||
seconds % 60,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_plain(result))
|
let result = time::pretty(elapsed);
|
||||||
|
Ok(RoomMessageEventContent::notice_plain(format!("{result}.")))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn show_config(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn show_config(&self) -> Result<RoomMessageEventContent> {
|
||||||
// Construct and send the response
|
// Construct and send the response
|
||||||
Ok(RoomMessageEventContent::text_plain(format!("{}", services().globals.config)))
|
Ok(RoomMessageEventContent::text_plain(format!("{}", self.services.globals.config)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let response0 = services().memory_usage().await?;
|
pub(super) async fn list_features(
|
||||||
let response1 = services().db.db.memory_usage()?;
|
&self, available: bool, enabled: bool, comma: bool,
|
||||||
let response2 = conduit::alloc::memory_usage();
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
let delim = if comma {
|
||||||
|
","
|
||||||
|
} else {
|
||||||
|
" "
|
||||||
|
};
|
||||||
|
if enabled && !available {
|
||||||
|
let features = info::rustc::features().join(delim);
|
||||||
|
let out = format!("`\n{features}\n`");
|
||||||
|
return Ok(RoomMessageEventContent::text_markdown(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
if available && !enabled {
|
||||||
|
let features = info::cargo::features().join(delim);
|
||||||
|
let out = format!("`\n{features}\n`");
|
||||||
|
return Ok(RoomMessageEventContent::text_markdown(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut features = String::new();
|
||||||
|
let enabled = info::rustc::features();
|
||||||
|
let available = info::cargo::features();
|
||||||
|
for feature in available {
|
||||||
|
let active = enabled.contains(&feature.as_str());
|
||||||
|
let emoji = if active {
|
||||||
|
"✅"
|
||||||
|
} else {
|
||||||
|
"❌"
|
||||||
|
};
|
||||||
|
let remark = if active {
|
||||||
|
"[enabled]"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
writeln!(features, "{emoji} {feature} {remark}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::text_markdown(features))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn memory_usage(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let services_usage = self.services.memory_usage().await?;
|
||||||
|
let database_usage = self.services.db.db.memory_usage()?;
|
||||||
|
let allocator_usage = conduit::alloc::memory_usage().map_or(String::new(), |s| format!("\nAllocator:\n{s}"));
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Services:\n{response0}\nDatabase:\n{response1}\n{}",
|
"Services:\n{services_usage}\nDatabase:\n{database_usage}{allocator_usage}",
|
||||||
if !response2.is_empty() {
|
|
||||||
format!("Allocator:\n {response2}")
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn clear_caches(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
services().clear_cache().await;
|
pub(super) async fn clear_caches(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
self.services.clear_cache().await;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain("Done."))
|
Ok(RoomMessageEventContent::text_plain("Done."))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn list_backups(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let result = services().globals.db.backup_list()?;
|
pub(super) async fn list_backups(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let result = self.services.globals.db.backup_list()?;
|
||||||
|
|
||||||
if result.is_empty() {
|
if result.is_empty() {
|
||||||
Ok(RoomMessageEventContent::text_plain("No backups found."))
|
Ok(RoomMessageEventContent::text_plain("No backups found."))
|
||||||
@@ -57,64 +95,69 @@ pub(super) async fn list_backups(_body: Vec<&str>) -> Result<RoomMessageEventCon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn backup_database(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let mut result = services()
|
pub(super) async fn backup_database(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let globals = Arc::clone(&self.services.globals);
|
||||||
|
let mut result = self
|
||||||
|
.services
|
||||||
.server
|
.server
|
||||||
.runtime()
|
.runtime()
|
||||||
.spawn_blocking(move || match services().globals.db.backup() {
|
.spawn_blocking(move || match globals.db.backup() {
|
||||||
Ok(()) => String::new(),
|
Ok(()) => String::new(),
|
||||||
Err(e) => (*e).to_string(),
|
Err(e) => (*e).to_string(),
|
||||||
})
|
})
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if result.is_empty() {
|
if result.is_empty() {
|
||||||
result = services().globals.db.backup_list()?;
|
result = self.services.globals.db.backup_list()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(&result))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) async fn list_database_files(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
let result = services().globals.db.file_list()?;
|
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(result))
|
Ok(RoomMessageEventContent::notice_markdown(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn admin_notice(_body: Vec<&str>, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn list_database_files(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let result = self.services.globals.db.file_list()?;
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::notice_markdown(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn admin_notice(&self, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
||||||
let message = message.join(" ");
|
let message = message.join(" ");
|
||||||
services().admin.send_text(&message).await;
|
self.services.admin.send_text(&message).await;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_plain("Notice was sent to #admins"))
|
Ok(RoomMessageEventContent::notice_plain("Notice was sent to #admins"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(conduit_mods)]
|
#[admin_command]
|
||||||
pub(super) async fn reload(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn reload_mods(&self) -> Result<RoomMessageEventContent> {
|
||||||
services().server.reload()?;
|
self.services.server.reload()?;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_plain("Reloading server..."))
|
Ok(RoomMessageEventContent::notice_plain("Reloading server..."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub(super) async fn restart(_body: Vec<&str>, force: bool) -> Result<RoomMessageEventContent> {
|
pub(super) async fn restart(&self, force: bool) -> Result<RoomMessageEventContent> {
|
||||||
use conduit::utils::sys::current_exe_deleted;
|
use conduit::utils::sys::current_exe_deleted;
|
||||||
|
|
||||||
if !force && current_exe_deleted() {
|
if !force && current_exe_deleted() {
|
||||||
return Err(Error::Err(
|
return Err!(
|
||||||
"The server cannot be restarted because the executable was tampered with. If this is expected use --force \
|
"The server cannot be restarted because the executable changed. If this is expected use --force to \
|
||||||
to override."
|
override."
|
||||||
.to_owned(),
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
services().server.restart()?;
|
self.services.server.restart()?;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_plain("Restarting server..."))
|
Ok(RoomMessageEventContent::notice_plain("Restarting server..."))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn shutdown(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn shutdown(&self) -> Result<RoomMessageEventContent> {
|
||||||
warn!("shutdown command");
|
warn!("shutdown command");
|
||||||
services().server.shutdown()?;
|
self.services.server.shutdown()?;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_plain("Shutting down server..."))
|
Ok(RoomMessageEventContent::notice_plain("Shutting down server..."))
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-28
@@ -2,12 +2,11 @@ mod commands;
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
|
||||||
|
|
||||||
use self::commands::*;
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[admin_command_dispatch]
|
||||||
#[derive(Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub(super) enum ServerCommand {
|
pub(super) enum ServerCommand {
|
||||||
/// - Time elapsed since startup
|
/// - Time elapsed since startup
|
||||||
Uptime,
|
Uptime,
|
||||||
@@ -15,6 +14,18 @@ pub(super) enum ServerCommand {
|
|||||||
/// - Show configuration values
|
/// - Show configuration values
|
||||||
ShowConfig,
|
ShowConfig,
|
||||||
|
|
||||||
|
/// - List the features built into the server
|
||||||
|
ListFeatures {
|
||||||
|
#[arg(short, long)]
|
||||||
|
available: bool,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
enabled: bool,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
comma: bool,
|
||||||
|
},
|
||||||
|
|
||||||
/// - Print database memory usage statistics
|
/// - Print database memory usage statistics
|
||||||
MemoryUsage,
|
MemoryUsage,
|
||||||
|
|
||||||
@@ -36,9 +47,9 @@ pub(super) enum ServerCommand {
|
|||||||
message: Vec<String>,
|
message: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[cfg(conduit_mods)]
|
|
||||||
/// - Hot-reload the server
|
/// - Hot-reload the server
|
||||||
Reload,
|
#[clap(alias = "reload")]
|
||||||
|
ReloadMods,
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
/// - Restart the server
|
/// - Restart the server
|
||||||
@@ -50,25 +61,3 @@ pub(super) enum ServerCommand {
|
|||||||
/// - Shutdown the server
|
/// - Shutdown the server
|
||||||
Shutdown,
|
Shutdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: ServerCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
Ok(match command {
|
|
||||||
ServerCommand::Uptime => uptime(body).await?,
|
|
||||||
ServerCommand::ShowConfig => show_config(body).await?,
|
|
||||||
ServerCommand::MemoryUsage => memory_usage(body).await?,
|
|
||||||
ServerCommand::ClearCaches => clear_caches(body).await?,
|
|
||||||
ServerCommand::ListBackups => list_backups(body).await?,
|
|
||||||
ServerCommand::BackupDatabase => backup_database(body).await?,
|
|
||||||
ServerCommand::ListDatabaseFiles => list_database_files(body).await?,
|
|
||||||
ServerCommand::AdminNotice {
|
|
||||||
message,
|
|
||||||
} => admin_notice(body, message).await?,
|
|
||||||
#[cfg(conduit_mods)]
|
|
||||||
ServerCommand::Reload => reload(body).await?,
|
|
||||||
#[cfg(unix)]
|
|
||||||
ServerCommand::Restart {
|
|
||||||
force,
|
|
||||||
} => restart(body, force).await?,
|
|
||||||
ServerCommand::Shutdown => shutdown(body).await?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_help_short() { get_help_inner("-h"); }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_help_long() { get_help_inner("--help"); }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_help_subcommand() { get_help_inner("help"); }
|
||||||
|
|
||||||
|
fn get_help_inner(input: &str) {
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
use crate::admin::AdminCommand;
|
||||||
|
|
||||||
|
let Err(error) = AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) else {
|
||||||
|
panic!("no error!");
|
||||||
|
};
|
||||||
|
|
||||||
|
let error = error.to_string();
|
||||||
|
// Search for a handful of keywords that suggest the help printed properly
|
||||||
|
assert!(error.contains("Usage:"));
|
||||||
|
assert!(error.contains("Commands:"));
|
||||||
|
assert!(error.contains("Options:"));
|
||||||
|
}
|
||||||
+137
-73
@@ -1,29 +1,29 @@
|
|||||||
use std::{collections::BTreeMap, fmt::Write as _};
|
use std::{collections::BTreeMap, fmt::Write as _};
|
||||||
|
|
||||||
use api::client::{join_room_by_id_helper, leave_all_rooms, update_avatar_url, update_displayname};
|
use api::client::{join_room_by_id_helper, leave_all_rooms, update_avatar_url, update_displayname};
|
||||||
use conduit::{utils, Result};
|
use conduit::{error, info, utils, warn, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::{
|
events::{
|
||||||
room::message::RoomMessageEventContent,
|
room::message::RoomMessageEventContent,
|
||||||
tag::{TagEvent, TagEventContent, TagInfo},
|
tag::{TagEvent, TagEventContent, TagInfo},
|
||||||
RoomAccountDataEventType,
|
RoomAccountDataEventType,
|
||||||
},
|
},
|
||||||
OwnedRoomId, OwnedUserId, RoomId,
|
OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, RoomId,
|
||||||
};
|
};
|
||||||
use tracing::{error, info, warn};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
escape_html, get_room_info, services,
|
admin_command, escape_html, get_room_info,
|
||||||
utils::{parse_active_local_user_id, parse_local_user_id},
|
utils::{parse_active_local_user_id, parse_local_user_id},
|
||||||
};
|
};
|
||||||
|
|
||||||
const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
|
const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
|
||||||
|
|
||||||
pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
match services().users.list_local_users() {
|
pub(super) async fn list_users(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
match self.services.users.list_local_users() {
|
||||||
Ok(users) => {
|
Ok(users) => {
|
||||||
let mut plain_msg = format!("Found {} local user account(s):\n```\n", users.len());
|
let mut plain_msg = format!("Found {} local user account(s):\n```\n", users.len());
|
||||||
plain_msg += &users.join("\n");
|
plain_msg += users.join("\n").as_str();
|
||||||
plain_msg += "\n```";
|
plain_msg += "\n```";
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(plain_msg))
|
Ok(RoomMessageEventContent::notice_markdown(plain_msg))
|
||||||
@@ -32,43 +32,51 @@ pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn create(
|
#[admin_command]
|
||||||
_body: Vec<&str>, username: String, password: Option<String>,
|
pub(super) async fn create_user(&self, username: String, password: Option<String>) -> Result<RoomMessageEventContent> {
|
||||||
) -> Result<RoomMessageEventContent> {
|
|
||||||
// Validate user id
|
// Validate user id
|
||||||
let user_id = parse_local_user_id(&username)?;
|
let user_id = parse_local_user_id(self.services, &username)?;
|
||||||
|
|
||||||
if services().users.exists(&user_id)? {
|
if self.services.users.exists(&user_id)? {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!("Userid {user_id} already exists")));
|
return Ok(RoomMessageEventContent::text_plain(format!("Userid {user_id} already exists")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user_id.is_historical() {
|
||||||
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"User ID {user_id} does not conform to new Matrix identifier spec"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
let password = password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH));
|
let password = password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH));
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
services().users.create(&user_id, Some(password.as_str()))?;
|
self.services
|
||||||
|
.users
|
||||||
|
.create(&user_id, Some(password.as_str()))?;
|
||||||
|
|
||||||
// Default to pretty displayname
|
// Default to pretty displayname
|
||||||
let mut displayname = user_id.localpart().to_owned();
|
let mut displayname = user_id.localpart().to_owned();
|
||||||
|
|
||||||
// If `new_user_displayname_suffix` is set, registration will push whatever
|
// If `new_user_displayname_suffix` is set, registration will push whatever
|
||||||
// content is set to the user's display name with a space before it
|
// content is set to the user's display name with a space before it
|
||||||
if !services()
|
if !self
|
||||||
|
.services
|
||||||
.globals
|
.globals
|
||||||
.config
|
.config
|
||||||
.new_user_displayname_suffix
|
.new_user_displayname_suffix
|
||||||
.is_empty()
|
.is_empty()
|
||||||
{
|
{
|
||||||
write!(displayname, " {}", services().globals.config.new_user_displayname_suffix)
|
write!(displayname, " {}", self.services.globals.config.new_user_displayname_suffix)
|
||||||
.expect("should be able to write to string buffer");
|
.expect("should be able to write to string buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
self.services
|
||||||
.users
|
.users
|
||||||
.set_displayname(&user_id, Some(displayname))
|
.set_displayname(&user_id, Some(displayname))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Initial account data
|
// Initial account data
|
||||||
services().account_data.update(
|
self.services.account_data.update(
|
||||||
None,
|
None,
|
||||||
&user_id,
|
&user_id,
|
||||||
ruma::events::GlobalAccountDataEventType::PushRules
|
ruma::events::GlobalAccountDataEventType::PushRules
|
||||||
@@ -82,12 +90,13 @@ pub(super) async fn create(
|
|||||||
.expect("to json value always works"),
|
.expect("to json value always works"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !services().globals.config.auto_join_rooms.is_empty() {
|
if !self.services.globals.config.auto_join_rooms.is_empty() {
|
||||||
for room in &services().globals.config.auto_join_rooms {
|
for room in &self.services.globals.config.auto_join_rooms {
|
||||||
if !services()
|
if !self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.server_in_room(services().globals.server_name(), room)?
|
.server_in_room(self.services.globals.server_name(), room)?
|
||||||
{
|
{
|
||||||
warn!("Skipping room {room} to automatically join as we have never joined before.");
|
warn!("Skipping room {room} to automatically join as we have never joined before.");
|
||||||
continue;
|
continue;
|
||||||
@@ -95,10 +104,11 @@ pub(super) async fn create(
|
|||||||
|
|
||||||
if let Some(room_id_server_name) = room.server_name() {
|
if let Some(room_id_server_name) = room.server_name() {
|
||||||
match join_room_by_id_helper(
|
match join_room_by_id_helper(
|
||||||
Some(&user_id),
|
self.services,
|
||||||
|
&user_id,
|
||||||
room,
|
room,
|
||||||
Some("Automatically joining this room upon registration".to_owned()),
|
Some("Automatically joining this room upon registration".to_owned()),
|
||||||
&[room_id_server_name.to_owned(), services().globals.server_name().to_owned()],
|
&[room_id_server_name.to_owned(), self.services.globals.server_name().to_owned()],
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -123,38 +133,38 @@ pub(super) async fn create(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn deactivate(
|
#[admin_command]
|
||||||
_body: Vec<&str>, no_leave_rooms: bool, user_id: String,
|
pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) -> Result<RoomMessageEventContent> {
|
||||||
) -> Result<RoomMessageEventContent> {
|
|
||||||
// Validate user id
|
// Validate user id
|
||||||
let user_id = parse_local_user_id(&user_id)?;
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
|
|
||||||
// don't deactivate the server service account
|
// don't deactivate the server service account
|
||||||
if user_id == services().globals.server_user {
|
if user_id == self.services.globals.server_user {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to deactivate the server service account.",
|
"Not allowed to deactivate the server service account.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
services().users.deactivate_account(&user_id)?;
|
self.services.users.deactivate_account(&user_id)?;
|
||||||
|
|
||||||
if !no_leave_rooms {
|
if !no_leave_rooms {
|
||||||
services()
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
"Making {user_id} leave all rooms after deactivation..."
|
"Making {user_id} leave all rooms after deactivation..."
|
||||||
)))
|
)))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let all_joined_rooms: Vec<OwnedRoomId> = services()
|
let all_joined_rooms: Vec<OwnedRoomId> = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(&user_id)
|
.rooms_joined(&user_id)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.collect();
|
.collect();
|
||||||
update_displayname(user_id.clone(), None, all_joined_rooms.clone()).await?;
|
update_displayname(self.services, user_id.clone(), None, all_joined_rooms.clone()).await?;
|
||||||
update_avatar_url(user_id.clone(), None, None, all_joined_rooms).await?;
|
update_avatar_url(self.services, user_id.clone(), None, None, all_joined_rooms).await?;
|
||||||
leave_all_rooms(&user_id).await;
|
leave_all_rooms(self.services, &user_id).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
@@ -162,10 +172,11 @@ pub(super) async fn deactivate(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn reset_password(_body: Vec<&str>, username: String) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let user_id = parse_local_user_id(&username)?;
|
pub(super) async fn reset_password(&self, username: String) -> Result<RoomMessageEventContent> {
|
||||||
|
let user_id = parse_local_user_id(self.services, &username)?;
|
||||||
|
|
||||||
if user_id == services().globals.server_user {
|
if user_id == self.services.globals.server_user {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to set the password for the server account. Please use the emergency password config option.",
|
"Not allowed to set the password for the server account. Please use the emergency password config option.",
|
||||||
));
|
));
|
||||||
@@ -173,7 +184,8 @@ pub(super) async fn reset_password(_body: Vec<&str>, username: String) -> Result
|
|||||||
|
|
||||||
let new_password = utils::random_string(AUTO_GEN_PASSWORD_LENGTH);
|
let new_password = utils::random_string(AUTO_GEN_PASSWORD_LENGTH);
|
||||||
|
|
||||||
match services()
|
match self
|
||||||
|
.services
|
||||||
.users
|
.users
|
||||||
.set_password(&user_id, Some(new_password.as_str()))
|
.set_password(&user_id, Some(new_password.as_str()))
|
||||||
{
|
{
|
||||||
@@ -186,25 +198,29 @@ pub(super) async fn reset_password(_body: Vec<&str>, username: String) -> Result
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn deactivate_all(
|
#[admin_command]
|
||||||
body: Vec<&str>, no_leave_rooms: bool, force: bool,
|
pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) -> Result<RoomMessageEventContent> {
|
||||||
) -> Result<RoomMessageEventContent> {
|
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let usernames = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>();
|
let usernames = self
|
||||||
|
.body
|
||||||
|
.to_vec()
|
||||||
|
.drain(1..self.body.len().saturating_sub(1))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut user_ids: Vec<OwnedUserId> = Vec::with_capacity(usernames.len());
|
let mut user_ids: Vec<OwnedUserId> = Vec::with_capacity(usernames.len());
|
||||||
let mut admins = Vec::new();
|
let mut admins = Vec::new();
|
||||||
|
|
||||||
for username in usernames {
|
for username in usernames {
|
||||||
match parse_active_local_user_id(username) {
|
match parse_active_local_user_id(self.services, username) {
|
||||||
Ok(user_id) => {
|
Ok(user_id) => {
|
||||||
if services().users.is_admin(&user_id)? && !force {
|
if self.services.users.is_admin(&user_id)? && !force {
|
||||||
services()
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
"{username} is an admin and --force is not set, skipping over"
|
"{username} is an admin and --force is not set, skipping over"
|
||||||
@@ -215,8 +231,8 @@ pub(super) async fn deactivate_all(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// don't deactivate the server service account
|
// don't deactivate the server service account
|
||||||
if user_id == services().globals.server_user {
|
if user_id == self.services.globals.server_user {
|
||||||
services()
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
"{username} is the server service account, skipping over"
|
"{username} is the server service account, skipping over"
|
||||||
@@ -228,7 +244,7 @@ pub(super) async fn deactivate_all(
|
|||||||
user_ids.push(user_id);
|
user_ids.push(user_id);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
services()
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
"{username} is not a valid username, skipping over: {e}"
|
"{username} is not a valid username, skipping over: {e}"
|
||||||
@@ -242,24 +258,25 @@ pub(super) async fn deactivate_all(
|
|||||||
let mut deactivation_count: usize = 0;
|
let mut deactivation_count: usize = 0;
|
||||||
|
|
||||||
for user_id in user_ids {
|
for user_id in user_ids {
|
||||||
match services().users.deactivate_account(&user_id) {
|
match self.services.users.deactivate_account(&user_id) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
deactivation_count = deactivation_count.saturating_add(1);
|
deactivation_count = deactivation_count.saturating_add(1);
|
||||||
if !no_leave_rooms {
|
if !no_leave_rooms {
|
||||||
info!("Forcing user {user_id} to leave all rooms apart of deactivate-all");
|
info!("Forcing user {user_id} to leave all rooms apart of deactivate-all");
|
||||||
let all_joined_rooms: Vec<OwnedRoomId> = services()
|
let all_joined_rooms: Vec<OwnedRoomId> = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(&user_id)
|
.rooms_joined(&user_id)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.collect();
|
.collect();
|
||||||
update_displayname(user_id.clone(), None, all_joined_rooms.clone()).await?;
|
update_displayname(self.services, user_id.clone(), None, all_joined_rooms.clone()).await?;
|
||||||
update_avatar_url(user_id.clone(), None, None, all_joined_rooms).await?;
|
update_avatar_url(self.services, user_id.clone(), None, None, all_joined_rooms).await?;
|
||||||
leave_all_rooms(&user_id).await;
|
leave_all_rooms(self.services, &user_id).await;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
services()
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!("Failed deactivating user: {e}")))
|
.send_message(RoomMessageEventContent::text_plain(format!("Failed deactivating user: {e}")))
|
||||||
.await;
|
.await;
|
||||||
@@ -280,16 +297,18 @@ pub(super) async fn deactivate_all(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn list_joined_rooms(&self, user_id: String) -> Result<RoomMessageEventContent> {
|
||||||
// Validate user id
|
// Validate user id
|
||||||
let user_id = parse_local_user_id(&user_id)?;
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
|
|
||||||
let mut rooms: Vec<(OwnedRoomId, u64, String)> = services()
|
let mut rooms: Vec<(OwnedRoomId, u64, String)> = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(&user_id)
|
.rooms_joined(&user_id)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.map(|room_id| get_room_info(&room_id))
|
.map(|room_id| get_room_info(self.services, &room_id))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if rooms.is_empty() {
|
if rooms.is_empty() {
|
||||||
@@ -331,12 +350,55 @@ pub(super) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
|
|||||||
Ok(RoomMessageEventContent::text_html(output_plain, output_html))
|
Ok(RoomMessageEventContent::text_html(output_plain, output_html))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn put_room_tag(
|
#[admin_command]
|
||||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>, tag: String,
|
pub(super) async fn force_join_room(
|
||||||
|
&self, user_id: String, room_id: OwnedRoomOrAliasId,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_active_local_user_id(&user_id)?;
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
|
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
|
||||||
|
|
||||||
let event = services()
|
assert!(
|
||||||
|
self.services.globals.user_is_local(&user_id),
|
||||||
|
"Parsed user_id must be a local user"
|
||||||
|
);
|
||||||
|
join_room_by_id_helper(self.services, &user_id, &room_id, None, &[], None).await?;
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
|
"{user_id} has been joined to {room_id}.",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn make_user_admin(&self, user_id: String) -> Result<RoomMessageEventContent> {
|
||||||
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
|
let displayname = self
|
||||||
|
.services
|
||||||
|
.users
|
||||||
|
.displayname(&user_id)?
|
||||||
|
.unwrap_or_else(|| user_id.to_string());
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
self.services.globals.user_is_local(&user_id),
|
||||||
|
"Parsed user_id must be a local user"
|
||||||
|
);
|
||||||
|
self.services
|
||||||
|
.admin
|
||||||
|
.make_user_admin(&user_id, displayname)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
|
"{user_id} has been granted admin privileges.",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn put_room_tag(
|
||||||
|
&self, user_id: String, room_id: Box<RoomId>, tag: String,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
let user_id = parse_active_local_user_id(self.services, &user_id)?;
|
||||||
|
|
||||||
|
let event = self
|
||||||
|
.services
|
||||||
.account_data
|
.account_data
|
||||||
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
|
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
|
||||||
|
|
||||||
@@ -354,7 +416,7 @@ pub(super) async fn put_room_tag(
|
|||||||
.tags
|
.tags
|
||||||
.insert(tag.clone().into(), TagInfo::new());
|
.insert(tag.clone().into(), TagInfo::new());
|
||||||
|
|
||||||
services().account_data.update(
|
self.services.account_data.update(
|
||||||
Some(&room_id),
|
Some(&room_id),
|
||||||
&user_id,
|
&user_id,
|
||||||
RoomAccountDataEventType::Tag,
|
RoomAccountDataEventType::Tag,
|
||||||
@@ -366,12 +428,14 @@ pub(super) async fn put_room_tag(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
pub(super) async fn delete_room_tag(
|
pub(super) async fn delete_room_tag(
|
||||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>, tag: String,
|
&self, user_id: String, room_id: Box<RoomId>, tag: String,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_active_local_user_id(&user_id)?;
|
let user_id = parse_active_local_user_id(self.services, &user_id)?;
|
||||||
|
|
||||||
let event = services()
|
let event = self
|
||||||
|
.services
|
||||||
.account_data
|
.account_data
|
||||||
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
|
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
|
||||||
|
|
||||||
@@ -386,7 +450,7 @@ pub(super) async fn delete_room_tag(
|
|||||||
|
|
||||||
tags_event.content.tags.remove(&tag.clone().into());
|
tags_event.content.tags.remove(&tag.clone().into());
|
||||||
|
|
||||||
services().account_data.update(
|
self.services.account_data.update(
|
||||||
Some(&room_id),
|
Some(&room_id),
|
||||||
&user_id,
|
&user_id,
|
||||||
RoomAccountDataEventType::Tag,
|
RoomAccountDataEventType::Tag,
|
||||||
@@ -398,12 +462,12 @@ pub(super) async fn delete_room_tag(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn get_room_tags(
|
#[admin_command]
|
||||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>,
|
pub(super) async fn get_room_tags(&self, user_id: String, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||||
) -> Result<RoomMessageEventContent> {
|
let user_id = parse_active_local_user_id(self.services, &user_id)?;
|
||||||
let user_id = parse_active_local_user_id(&user_id)?;
|
|
||||||
|
|
||||||
let event = services()
|
let event = self
|
||||||
|
.services
|
||||||
.account_data
|
.account_data
|
||||||
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
|
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
|
||||||
|
|
||||||
|
|||||||
+20
-45
@@ -2,15 +2,16 @@ mod commands;
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId};
|
use ruma::{OwnedRoomOrAliasId, RoomId};
|
||||||
|
|
||||||
use self::commands::*;
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[admin_command_dispatch]
|
||||||
#[derive(Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub(super) enum UserCommand {
|
pub(super) enum UserCommand {
|
||||||
/// - Create a new user
|
/// - Create a new user
|
||||||
Create {
|
#[clap(alias = "create")]
|
||||||
|
CreateUser {
|
||||||
/// Username of the new user
|
/// Username of the new user
|
||||||
username: String,
|
username: String,
|
||||||
/// Password of the new user, if unspecified one is generated
|
/// Password of the new user, if unspecified one is generated
|
||||||
@@ -49,7 +50,7 @@ pub(super) enum UserCommand {
|
|||||||
/// Markdown code block below the command.
|
/// Markdown code block below the command.
|
||||||
DeactivateAll {
|
DeactivateAll {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
/// Remove users from their joined rooms
|
/// Does not leave any rooms the user is in on deactivation
|
||||||
no_leave_rooms: bool,
|
no_leave_rooms: bool,
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
/// Also deactivate admin accounts and will assume leave all rooms too
|
/// Also deactivate admin accounts and will assume leave all rooms too
|
||||||
@@ -57,7 +58,8 @@ pub(super) enum UserCommand {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// - List local users in the database
|
/// - List local users in the database
|
||||||
List,
|
#[clap(alias = "list")]
|
||||||
|
ListUsers,
|
||||||
|
|
||||||
/// - Lists all the rooms (local and remote) that the specified user is
|
/// - Lists all the rooms (local and remote) that the specified user is
|
||||||
/// joined in
|
/// joined in
|
||||||
@@ -65,6 +67,17 @@ pub(super) enum UserCommand {
|
|||||||
user_id: String,
|
user_id: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// - Manually join a local user to a room.
|
||||||
|
ForceJoinRoom {
|
||||||
|
user_id: String,
|
||||||
|
room_id: OwnedRoomOrAliasId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Grant server-admin privileges to a user.
|
||||||
|
MakeUserAdmin {
|
||||||
|
user_id: String,
|
||||||
|
},
|
||||||
|
|
||||||
/// - Puts a room tag for the specified user and room ID.
|
/// - Puts a room tag for the specified user and room ID.
|
||||||
///
|
///
|
||||||
/// This is primarily useful if you'd like to set your admin room
|
/// This is primarily useful if you'd like to set your admin room
|
||||||
@@ -91,41 +104,3 @@ pub(super) enum UserCommand {
|
|||||||
room_id: Box<RoomId>,
|
room_id: Box<RoomId>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: UserCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
Ok(match command {
|
|
||||||
UserCommand::List => list(body).await?,
|
|
||||||
UserCommand::Create {
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
} => create(body, username, password).await?,
|
|
||||||
UserCommand::Deactivate {
|
|
||||||
no_leave_rooms,
|
|
||||||
user_id,
|
|
||||||
} => deactivate(body, no_leave_rooms, user_id).await?,
|
|
||||||
UserCommand::ResetPassword {
|
|
||||||
username,
|
|
||||||
} => reset_password(body, username).await?,
|
|
||||||
UserCommand::DeactivateAll {
|
|
||||||
no_leave_rooms,
|
|
||||||
force,
|
|
||||||
} => deactivate_all(body, no_leave_rooms, force).await?,
|
|
||||||
UserCommand::ListJoinedRooms {
|
|
||||||
user_id,
|
|
||||||
} => list_joined_rooms(body, user_id).await?,
|
|
||||||
UserCommand::PutRoomTag {
|
|
||||||
user_id,
|
|
||||||
room_id,
|
|
||||||
tag,
|
|
||||||
} => put_room_tag(body, user_id, room_id, tag).await?,
|
|
||||||
UserCommand::DeleteRoomTag {
|
|
||||||
user_id,
|
|
||||||
room_id,
|
|
||||||
tag,
|
|
||||||
} => delete_room_tag(body, user_id, room_id, tag).await?,
|
|
||||||
UserCommand::GetRoomTags {
|
|
||||||
user_id,
|
|
||||||
room_id,
|
|
||||||
} => get_room_tags(body, user_id, room_id).await?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
+18
-20
@@ -1,8 +1,6 @@
|
|||||||
use conduit_core::Error;
|
use conduit_core::{err, Err, Result};
|
||||||
use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId};
|
use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId};
|
||||||
use service::user_is_local;
|
use service::Services;
|
||||||
|
|
||||||
use crate::{services, Result};
|
|
||||||
|
|
||||||
pub(crate) fn escape_html(s: &str) -> String {
|
pub(crate) fn escape_html(s: &str) -> String {
|
||||||
s.replace('&', "&")
|
s.replace('&', "&")
|
||||||
@@ -10,17 +8,17 @@ pub(crate) fn escape_html(s: &str) -> String {
|
|||||||
.replace('>', ">")
|
.replace('>', ">")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_room_info(id: &RoomId) -> (OwnedRoomId, u64, String) {
|
pub(crate) fn get_room_info(services: &Services, id: &RoomId) -> (OwnedRoomId, u64, String) {
|
||||||
(
|
(
|
||||||
id.into(),
|
id.into(),
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_joined_count(id)
|
.room_joined_count(id)
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_name(id)
|
.get_name(id)
|
||||||
@@ -31,32 +29,32 @@ pub(crate) fn get_room_info(id: &RoomId) -> (OwnedRoomId, u64, String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parses user ID
|
/// Parses user ID
|
||||||
pub(crate) fn parse_user_id(user_id: &str) -> Result<OwnedUserId> {
|
pub(crate) fn parse_user_id(services: &Services, user_id: &str) -> Result<OwnedUserId> {
|
||||||
UserId::parse_with_server_name(user_id.to_lowercase(), services().globals.server_name())
|
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
|
||||||
.map_err(|e| Error::Err(format!("The supplied username is not a valid username: {e}")))
|
.map_err(|e| err!("The supplied username is not a valid username: {e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses user ID as our local user
|
/// Parses user ID as our local user
|
||||||
pub(crate) fn parse_local_user_id(user_id: &str) -> Result<OwnedUserId> {
|
pub(crate) fn parse_local_user_id(services: &Services, user_id: &str) -> Result<OwnedUserId> {
|
||||||
let user_id = parse_user_id(user_id)?;
|
let user_id = parse_user_id(services, user_id)?;
|
||||||
|
|
||||||
if !user_is_local(&user_id) {
|
if !services.globals.user_is_local(&user_id) {
|
||||||
return Err(Error::Err(String::from("User does not belong to our server.")));
|
return Err!("User {user_id:?} does not belong to our server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(user_id)
|
Ok(user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses user ID that is an active (not guest or deactivated) local user
|
/// Parses user ID that is an active (not guest or deactivated) local user
|
||||||
pub(crate) fn parse_active_local_user_id(user_id: &str) -> Result<OwnedUserId> {
|
pub(crate) fn parse_active_local_user_id(services: &Services, user_id: &str) -> Result<OwnedUserId> {
|
||||||
let user_id = parse_local_user_id(user_id)?;
|
let user_id = parse_local_user_id(services, user_id)?;
|
||||||
|
|
||||||
if !services().users.exists(&user_id)? {
|
if !services.users.exists(&user_id)? {
|
||||||
return Err(Error::Err(String::from("User does not exist on this server.")));
|
return Err!("User {user_id:?} does not exist on this server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if services().users.is_deactivated(&user_id)? {
|
if services.users.is_deactivated(&user_id)? {
|
||||||
return Err(Error::Err(String::from("User is deactivated.")));
|
return Err!("User {user_id:?} is deactivated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(user_id)
|
Ok(user_id)
|
||||||
|
|||||||
+2
-3
@@ -41,11 +41,12 @@ bytes.workspace = true
|
|||||||
conduit-core.workspace = true
|
conduit-core.workspace = true
|
||||||
conduit-database.workspace = true
|
conduit-database.workspace = true
|
||||||
conduit-service.workspace = true
|
conduit-service.workspace = true
|
||||||
|
const-str.workspace = true
|
||||||
futures-util.workspace = true
|
futures-util.workspace = true
|
||||||
hmac.workspace = true
|
hmac.workspace = true
|
||||||
http.workspace = true
|
http.workspace = true
|
||||||
|
http-body-util.workspace = true
|
||||||
hyper.workspace = true
|
hyper.workspace = true
|
||||||
image.workspace = true
|
|
||||||
ipaddress.workspace = true
|
ipaddress.workspace = true
|
||||||
jsonwebtoken.workspace = true
|
jsonwebtoken.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
@@ -56,10 +57,8 @@ serde_html_form.workspace = true
|
|||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
sha-1.workspace = true
|
sha-1.workspace = true
|
||||||
thiserror.workspace = true
|
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
webpage.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
+88
-79
@@ -1,7 +1,8 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduit::debug_info;
|
use conduit::{debug_info, error, info, utils, warn, Error, Result};
|
||||||
use register::RegistrationKind;
|
use register::RegistrationKind;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
@@ -17,15 +18,9 @@ use ruma::{
|
|||||||
events::{room::message::RoomMessageEventContent, GlobalAccountDataEventType},
|
events::{room::message::RoomMessageEventContent, GlobalAccountDataEventType},
|
||||||
push, OwnedRoomId, UserId,
|
push, OwnedRoomId, UserId,
|
||||||
};
|
};
|
||||||
use tracing::{error, info, warn};
|
|
||||||
|
|
||||||
use super::{join_room_by_id_helper, DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
|
use super::{join_room_by_id_helper, DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
|
||||||
use crate::{
|
use crate::Ruma;
|
||||||
service::user_is_local,
|
|
||||||
services,
|
|
||||||
utils::{self},
|
|
||||||
Error, Result, Ruma,
|
|
||||||
};
|
|
||||||
|
|
||||||
const RANDOM_USER_ID_LENGTH: usize = 10;
|
const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||||
|
|
||||||
@@ -42,20 +37,21 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
|
|||||||
/// invalid when trying to register
|
/// invalid when trying to register
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
|
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
|
||||||
pub(crate) async fn get_register_available_route(
|
pub(crate) async fn get_register_available_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_username_availability::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_username_availability::v3::Request>,
|
||||||
) -> Result<get_username_availability::v3::Response> {
|
) -> Result<get_username_availability::v3::Response> {
|
||||||
// Validate user id
|
// Validate user id
|
||||||
let user_id = UserId::parse_with_server_name(body.username.to_lowercase(), services().globals.server_name())
|
let user_id = UserId::parse_with_server_name(body.username.to_lowercase(), services.globals.server_name())
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
|
.filter(|user_id| !user_id.is_historical() && services.globals.user_is_local(user_id))
|
||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
||||||
|
|
||||||
// Check if username is creative enough
|
// Check if username is creative enough
|
||||||
if services().users.exists(&user_id)? {
|
if services.users.exists(&user_id)? {
|
||||||
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
|
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.forbidden_usernames()
|
.forbidden_usernames()
|
||||||
.is_match(user_id.localpart())
|
.is_match(user_id.localpart())
|
||||||
@@ -91,9 +87,9 @@ pub(crate) async fn get_register_available_route(
|
|||||||
#[allow(clippy::doc_markdown)]
|
#[allow(clippy::doc_markdown)]
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "register")]
|
#[tracing::instrument(skip_all, fields(%client), name = "register")]
|
||||||
pub(crate) async fn register_route(
|
pub(crate) async fn register_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
|
||||||
) -> Result<register::v3::Response> {
|
) -> Result<register::v3::Response> {
|
||||||
if !services().globals.allow_registration() && body.appservice_info.is_none() {
|
if !services.globals.allow_registration() && body.appservice_info.is_none() {
|
||||||
info!(
|
info!(
|
||||||
"Registration disabled and request not from known appservice, rejecting registration attempt for username \
|
"Registration disabled and request not from known appservice, rejecting registration attempt for username \
|
||||||
{:?}",
|
{:?}",
|
||||||
@@ -105,8 +101,8 @@ pub(crate) async fn register_route(
|
|||||||
let is_guest = body.kind == RegistrationKind::Guest;
|
let is_guest = body.kind == RegistrationKind::Guest;
|
||||||
|
|
||||||
if is_guest
|
if is_guest
|
||||||
&& (!services().globals.allow_guest_registration()
|
&& (!services.globals.allow_guest_registration()
|
||||||
|| (services().globals.allow_registration() && services().globals.config.registration_token.is_some()))
|
|| (services.globals.allow_registration() && services.globals.config.registration_token.is_some()))
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
"Guest registration disabled / registration enabled with token configured, rejecting guest registration \
|
"Guest registration disabled / registration enabled with token configured, rejecting guest registration \
|
||||||
@@ -121,7 +117,7 @@ pub(crate) async fn register_route(
|
|||||||
|
|
||||||
// forbid guests from registering if there is not a real admin user yet. give
|
// forbid guests from registering if there is not a real admin user yet. give
|
||||||
// generic user error.
|
// generic user error.
|
||||||
if is_guest && services().users.count()? < 2 {
|
if is_guest && services.users.count()? < 2 {
|
||||||
warn!(
|
warn!(
|
||||||
"Guest account attempted to register before a real admin user has been registered, rejecting \
|
"Guest account attempted to register before a real admin user has been registered, rejecting \
|
||||||
registration. Guest's initial device name: {:?}",
|
registration. Guest's initial device name: {:?}",
|
||||||
@@ -133,16 +129,16 @@ pub(crate) async fn register_route(
|
|||||||
let user_id = match (&body.username, is_guest) {
|
let user_id = match (&body.username, is_guest) {
|
||||||
(Some(username), false) => {
|
(Some(username), false) => {
|
||||||
let proposed_user_id =
|
let proposed_user_id =
|
||||||
UserId::parse_with_server_name(username.to_lowercase(), services().globals.server_name())
|
UserId::parse_with_server_name(username.to_lowercase(), services.globals.server_name())
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
|
.filter(|user_id| !user_id.is_historical() && services.globals.user_is_local(user_id))
|
||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
||||||
|
|
||||||
if services().users.exists(&proposed_user_id)? {
|
if services.users.exists(&proposed_user_id)? {
|
||||||
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
|
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.forbidden_usernames()
|
.forbidden_usernames()
|
||||||
.is_match(proposed_user_id.localpart())
|
.is_match(proposed_user_id.localpart())
|
||||||
@@ -155,10 +151,10 @@ pub(crate) async fn register_route(
|
|||||||
_ => loop {
|
_ => loop {
|
||||||
let proposed_user_id = UserId::parse_with_server_name(
|
let proposed_user_id = UserId::parse_with_server_name(
|
||||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
||||||
services().globals.server_name(),
|
services.globals.server_name(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if !services().users.exists(&proposed_user_id)? {
|
if !services.users.exists(&proposed_user_id)? {
|
||||||
break proposed_user_id;
|
break proposed_user_id;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -172,13 +168,13 @@ pub(crate) async fn register_route(
|
|||||||
} else {
|
} else {
|
||||||
return Err(Error::BadRequest(ErrorKind::MissingToken, "Missing appservice token."));
|
return Err(Error::BadRequest(ErrorKind::MissingToken, "Missing appservice token."));
|
||||||
}
|
}
|
||||||
} else if services().appservice.is_exclusive_user_id(&user_id).await {
|
} else if services.appservice.is_exclusive_user_id(&user_id).await {
|
||||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "User ID reserved by appservice."));
|
return Err(Error::BadRequest(ErrorKind::Exclusive, "User ID reserved by appservice."));
|
||||||
}
|
}
|
||||||
|
|
||||||
// UIAA
|
// UIAA
|
||||||
let mut uiaainfo;
|
let mut uiaainfo;
|
||||||
let skip_auth = if services().globals.config.registration_token.is_some() {
|
let skip_auth = if services.globals.config.registration_token.is_some() {
|
||||||
// Registration token required
|
// Registration token required
|
||||||
uiaainfo = UiaaInfo {
|
uiaainfo = UiaaInfo {
|
||||||
flows: vec![AuthFlow {
|
flows: vec![AuthFlow {
|
||||||
@@ -206,8 +202,8 @@ pub(crate) async fn register_route(
|
|||||||
|
|
||||||
if !skip_auth {
|
if !skip_auth {
|
||||||
if let Some(auth) = &body.auth {
|
if let Some(auth) = &body.auth {
|
||||||
let (worked, uiaainfo) = services().uiaa.try_auth(
|
let (worked, uiaainfo) = services.uiaa.try_auth(
|
||||||
&UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid"),
|
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
|
||||||
"".into(),
|
"".into(),
|
||||||
auth,
|
auth,
|
||||||
&uiaainfo,
|
&uiaainfo,
|
||||||
@@ -218,8 +214,8 @@ pub(crate) async fn register_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else if let Some(json) = body.json_body {
|
} else if let Some(json) = body.json_body {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
services().uiaa.create(
|
services.uiaa.create(
|
||||||
&UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid"),
|
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
|
||||||
"".into(),
|
"".into(),
|
||||||
&uiaainfo,
|
&uiaainfo,
|
||||||
&json,
|
&json,
|
||||||
@@ -237,25 +233,25 @@ pub(crate) async fn register_route(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
services().users.create(&user_id, password)?;
|
services.users.create(&user_id, password)?;
|
||||||
|
|
||||||
// Default to pretty displayname
|
// Default to pretty displayname
|
||||||
let mut displayname = user_id.localpart().to_owned();
|
let mut displayname = user_id.localpart().to_owned();
|
||||||
|
|
||||||
// If `new_user_displayname_suffix` is set, registration will push whatever
|
// If `new_user_displayname_suffix` is set, registration will push whatever
|
||||||
// content is set to the user's display name with a space before it
|
// content is set to the user's display name with a space before it
|
||||||
if !services().globals.new_user_displayname_suffix().is_empty() {
|
if !services.globals.new_user_displayname_suffix().is_empty() {
|
||||||
write!(displayname, " {}", services().globals.config.new_user_displayname_suffix)
|
write!(displayname, " {}", services.globals.config.new_user_displayname_suffix)
|
||||||
.expect("should be able to write to string buffer");
|
.expect("should be able to write to string buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_displayname(&user_id, Some(displayname.clone()))
|
.set_displayname(&user_id, Some(displayname.clone()))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Initial account data
|
// Initial account data
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
None,
|
None,
|
||||||
&user_id,
|
&user_id,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
@@ -290,7 +286,7 @@ pub(crate) async fn register_route(
|
|||||||
let token = utils::random_string(TOKEN_LENGTH);
|
let token = utils::random_string(TOKEN_LENGTH);
|
||||||
|
|
||||||
// Create device for this account
|
// Create device for this account
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
|
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
|
||||||
|
|
||||||
@@ -299,7 +295,7 @@ pub(crate) async fn register_route(
|
|||||||
// log in conduit admin channel if a non-guest user registered
|
// log in conduit admin channel if a non-guest user registered
|
||||||
if body.appservice_info.is_none() && !is_guest {
|
if body.appservice_info.is_none() && !is_guest {
|
||||||
info!("New user \"{user_id}\" registered on this server.");
|
info!("New user \"{user_id}\" registered on this server.");
|
||||||
services()
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"New user \"{user_id}\" registered on this server from IP {client}."
|
"New user \"{user_id}\" registered on this server from IP {client}."
|
||||||
@@ -308,8 +304,8 @@ pub(crate) async fn register_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// log in conduit admin channel if a guest registered
|
// log in conduit admin channel if a guest registered
|
||||||
if body.appservice_info.is_none() && is_guest && services().globals.log_guest_registrations() {
|
if body.appservice_info.is_none() && is_guest && services.globals.log_guest_registrations() {
|
||||||
info!("New guest user \"{user_id}\" registered on this server from IP.");
|
info!("New guest user \"{user_id}\" registered on this server.");
|
||||||
|
|
||||||
if let Some(device_display_name) = &body.initial_device_display_name {
|
if let Some(device_display_name) = &body.initial_device_display_name {
|
||||||
if body
|
if body
|
||||||
@@ -317,7 +313,7 @@ pub(crate) async fn register_route(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|device_display_name| !device_display_name.is_empty())
|
.is_some_and(|device_display_name| !device_display_name.is_empty())
|
||||||
{
|
{
|
||||||
services()
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"Guest user \"{user_id}\" with device display name `{device_display_name}` registered on this \
|
"Guest user \"{user_id}\" with device display name `{device_display_name}` registered on this \
|
||||||
@@ -325,7 +321,7 @@ pub(crate) async fn register_route(
|
|||||||
)))
|
)))
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
services()
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"Guest user \"{user_id}\" with no device display name registered on this server from IP \
|
"Guest user \"{user_id}\" with no device display name registered on this server from IP \
|
||||||
@@ -334,7 +330,7 @@ pub(crate) async fn register_route(
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
services()
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"Guest user \"{user_id}\" with no device display name registered on this server from IP {client}.",
|
"Guest user \"{user_id}\" with no device display name registered on this server from IP {client}.",
|
||||||
@@ -346,14 +342,12 @@ pub(crate) async fn register_route(
|
|||||||
// If this is the first real user, grant them admin privileges except for guest
|
// If this is the first real user, grant them admin privileges except for guest
|
||||||
// users Note: the server user, @conduit:servername, is generated first
|
// users Note: the server user, @conduit:servername, is generated first
|
||||||
if !is_guest {
|
if !is_guest {
|
||||||
if let Some(admin_room) = service::admin::Service::get_admin_room()? {
|
if let Some(admin_room) = services.admin.get_admin_room()? {
|
||||||
if services()
|
if services.rooms.state_cache.room_joined_count(&admin_room)? == Some(1) {
|
||||||
.rooms
|
services
|
||||||
.state_cache
|
.admin
|
||||||
.room_joined_count(&admin_room)?
|
.make_user_admin(&user_id, displayname)
|
||||||
== Some(1)
|
.await?;
|
||||||
{
|
|
||||||
service::admin::make_user_admin(&user_id, displayname).await?;
|
|
||||||
|
|
||||||
warn!("Granting {user_id} admin privileges as the first user");
|
warn!("Granting {user_id} admin privileges as the first user");
|
||||||
}
|
}
|
||||||
@@ -361,14 +355,14 @@ pub(crate) async fn register_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if body.appservice_info.is_none()
|
if body.appservice_info.is_none()
|
||||||
&& !services().globals.config.auto_join_rooms.is_empty()
|
&& !services.globals.config.auto_join_rooms.is_empty()
|
||||||
&& (services().globals.allow_guests_auto_join_rooms() || !is_guest)
|
&& (services.globals.allow_guests_auto_join_rooms() || !is_guest)
|
||||||
{
|
{
|
||||||
for room in &services().globals.config.auto_join_rooms {
|
for room in &services.globals.config.auto_join_rooms {
|
||||||
if !services()
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.server_in_room(services().globals.server_name(), room)?
|
.server_in_room(services.globals.server_name(), room)?
|
||||||
{
|
{
|
||||||
warn!("Skipping room {room} to automatically join as we have never joined before.");
|
warn!("Skipping room {room} to automatically join as we have never joined before.");
|
||||||
continue;
|
continue;
|
||||||
@@ -376,10 +370,11 @@ pub(crate) async fn register_route(
|
|||||||
|
|
||||||
if let Some(room_id_server_name) = room.server_name() {
|
if let Some(room_id_server_name) = room.server_name() {
|
||||||
if let Err(e) = join_room_by_id_helper(
|
if let Err(e) = join_room_by_id_helper(
|
||||||
Some(&user_id),
|
&services,
|
||||||
|
&user_id,
|
||||||
room,
|
room,
|
||||||
Some("Automatically joining this room upon registration".to_owned()),
|
Some("Automatically joining this room upon registration".to_owned()),
|
||||||
&[room_id_server_name.to_owned(), services().globals.server_name().to_owned()],
|
&[room_id_server_name.to_owned(), services.globals.server_name().to_owned()],
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -421,9 +416,15 @@ pub(crate) async fn register_route(
|
|||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
|
#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
|
||||||
pub(crate) async fn change_password_route(
|
pub(crate) async fn change_password_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<change_password::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<change_password::v3::Request>,
|
||||||
) -> Result<change_password::v3::Response> {
|
) -> Result<change_password::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
// Authentication for this endpoint was made optional, but we need
|
||||||
|
// authentication currently
|
||||||
|
let sender_user = body
|
||||||
|
.sender_user
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| Error::BadRequest(ErrorKind::MissingToken, "Missing access token."))?;
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let mut uiaainfo = UiaaInfo {
|
let mut uiaainfo = UiaaInfo {
|
||||||
@@ -437,7 +438,7 @@ pub(crate) async fn change_password_route(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(auth) = &body.auth {
|
if let Some(auth) = &body.auth {
|
||||||
let (worked, uiaainfo) = services()
|
let (worked, uiaainfo) = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||||
if !worked {
|
if !worked {
|
||||||
@@ -446,7 +447,7 @@ pub(crate) async fn change_password_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else if let Some(json) = body.json_body {
|
} else if let Some(json) = body.json_body {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
services()
|
services
|
||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
@@ -454,24 +455,24 @@ pub(crate) async fn change_password_route(
|
|||||||
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_password(sender_user, Some(&body.new_password))?;
|
.set_password(sender_user, Some(&body.new_password))?;
|
||||||
|
|
||||||
if body.logout_devices {
|
if body.logout_devices {
|
||||||
// Logout all devices except the current one
|
// Logout all devices except the current one
|
||||||
for id in services()
|
for id in services
|
||||||
.users
|
.users
|
||||||
.all_device_ids(sender_user)
|
.all_device_ids(sender_user)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.filter(|id| id != sender_device)
|
.filter(|id| id != sender_device)
|
||||||
{
|
{
|
||||||
services().users.remove_device(sender_user, &id)?;
|
services.users.remove_device(sender_user, &id)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("User {sender_user} changed their password.");
|
info!("User {sender_user} changed their password.");
|
||||||
services()
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"User {sender_user} changed their password."
|
"User {sender_user} changed their password."
|
||||||
@@ -486,14 +487,16 @@ pub(crate) async fn change_password_route(
|
|||||||
/// Get `user_id` of the sender user.
|
/// Get `user_id` of the sender user.
|
||||||
///
|
///
|
||||||
/// Note: Also works for Application Services
|
/// Note: Also works for Application Services
|
||||||
pub(crate) async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3::Response> {
|
pub(crate) async fn whoami_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<whoami::v3::Request>,
|
||||||
|
) -> Result<whoami::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let device_id = body.sender_device.clone();
|
let device_id = body.sender_device.clone();
|
||||||
|
|
||||||
Ok(whoami::v3::Response {
|
Ok(whoami::v3::Response {
|
||||||
user_id: sender_user.clone(),
|
user_id: sender_user.clone(),
|
||||||
device_id,
|
device_id,
|
||||||
is_guest: services().users.is_deactivated(sender_user)? && body.appservice_info.is_none(),
|
is_guest: services.users.is_deactivated(sender_user)? && body.appservice_info.is_none(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,9 +513,15 @@ pub(crate) async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoa
|
|||||||
/// - Removes ability to log in again
|
/// - Removes ability to log in again
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
|
#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
|
||||||
pub(crate) async fn deactivate_route(
|
pub(crate) async fn deactivate_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<deactivate::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<deactivate::v3::Request>,
|
||||||
) -> Result<deactivate::v3::Response> {
|
) -> Result<deactivate::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
// Authentication for this endpoint was made optional, but we need
|
||||||
|
// authentication currently
|
||||||
|
let sender_user = body
|
||||||
|
.sender_user
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| Error::BadRequest(ErrorKind::MissingToken, "Missing access token."))?;
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let mut uiaainfo = UiaaInfo {
|
let mut uiaainfo = UiaaInfo {
|
||||||
@@ -526,7 +535,7 @@ pub(crate) async fn deactivate_route(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(auth) = &body.auth {
|
if let Some(auth) = &body.auth {
|
||||||
let (worked, uiaainfo) = services()
|
let (worked, uiaainfo) = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||||
if !worked {
|
if !worked {
|
||||||
@@ -535,7 +544,7 @@ pub(crate) async fn deactivate_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else if let Some(json) = body.json_body {
|
} else if let Some(json) = body.json_body {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
services()
|
services
|
||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
@@ -544,23 +553,23 @@ pub(crate) async fn deactivate_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove devices and mark account as deactivated
|
// Remove devices and mark account as deactivated
|
||||||
services().users.deactivate_account(sender_user)?;
|
services.users.deactivate_account(sender_user)?;
|
||||||
|
|
||||||
// Remove profile pictures and display name
|
// Remove profile pictures and display name
|
||||||
let all_joined_rooms: Vec<OwnedRoomId> = services()
|
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(sender_user)
|
.rooms_joined(sender_user)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.collect();
|
.collect();
|
||||||
super::update_displayname(sender_user.clone(), None, all_joined_rooms.clone()).await?;
|
super::update_displayname(&services, sender_user.clone(), None, all_joined_rooms.clone()).await?;
|
||||||
super::update_avatar_url(sender_user.clone(), None, None, all_joined_rooms).await?;
|
super::update_avatar_url(&services, sender_user.clone(), None, None, all_joined_rooms).await?;
|
||||||
|
|
||||||
// Make the user leave all rooms before deactivation
|
// Make the user leave all rooms before deactivation
|
||||||
super::leave_all_rooms(sender_user).await;
|
super::leave_all_rooms(&services, sender_user).await;
|
||||||
|
|
||||||
info!("User {sender_user} deactivated their account.");
|
info!("User {sender_user} deactivated their account.");
|
||||||
services()
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"User {sender_user} deactivated their account."
|
"User {sender_user} deactivated their account."
|
||||||
@@ -622,9 +631,9 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route(
|
|||||||
/// Currently does not have any ratelimiting, and this isn't very practical as
|
/// Currently does not have any ratelimiting, and this isn't very practical as
|
||||||
/// there is only one registration token allowed.
|
/// there is only one registration token allowed.
|
||||||
pub(crate) async fn check_registration_token_validity(
|
pub(crate) async fn check_registration_token_validity(
|
||||||
body: Ruma<check_registration_token_validity::v1::Request>,
|
State(services): State<crate::State>, body: Ruma<check_registration_token_validity::v1::Request>,
|
||||||
) -> Result<check_registration_token_validity::v1::Response> {
|
) -> Result<check_registration_token_validity::v1::Response> {
|
||||||
let Some(reg_token) = services().globals.config.registration_token.clone() else {
|
let Some(reg_token) = services.globals.config.registration_token.clone() else {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::forbidden(),
|
ErrorKind::forbidden(),
|
||||||
"Server does not allow token registration.",
|
"Server does not allow token registration.",
|
||||||
|
|||||||
+34
-18
@@ -1,3 +1,5 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use conduit::{debug, Error, Result};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
@@ -6,21 +8,27 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
OwnedServerName, RoomAliasId, RoomId,
|
OwnedServerName, RoomAliasId, RoomId,
|
||||||
};
|
};
|
||||||
use tracing::debug;
|
use service::Services;
|
||||||
|
|
||||||
use crate::{service::server_is_ours, services, Error, Result, Ruma};
|
use crate::Ruma;
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/v3/directory/room/{roomAlias}`
|
/// # `PUT /_matrix/client/v3/directory/room/{roomAlias}`
|
||||||
///
|
///
|
||||||
/// Creates a new room alias on this server.
|
/// Creates a new room alias on this server.
|
||||||
pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) -> Result<create_alias::v3::Response> {
|
pub(crate) async fn create_alias_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<create_alias::v3::Request>,
|
||||||
|
) -> Result<create_alias::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
service::rooms::alias::appservice_checks(&body.room_alias, &body.appservice_info).await?;
|
services
|
||||||
|
.rooms
|
||||||
|
.alias
|
||||||
|
.appservice_checks(&body.room_alias, &body.appservice_info)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// this isn't apart of alias_checks or delete alias route because we should
|
// this isn't apart of alias_checks or delete alias route because we should
|
||||||
// allow removing forbidden room aliases
|
// allow removing forbidden room aliases
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.forbidden_alias_names()
|
.forbidden_alias_names()
|
||||||
.is_match(body.room_alias.alias())
|
.is_match(body.room_alias.alias())
|
||||||
@@ -28,7 +36,7 @@ pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) ->
|
|||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room alias is forbidden."));
|
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room alias is forbidden."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if services()
|
if services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_local_alias(&body.room_alias)?
|
.resolve_local_alias(&body.room_alias)?
|
||||||
@@ -37,7 +45,7 @@ pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) ->
|
|||||||
return Err(Error::Conflict("Alias already exists."));
|
return Err(Error::Conflict("Alias already exists."));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.set_alias(&body.room_alias, &body.room_id, sender_user)?;
|
.set_alias(&body.room_alias, &body.room_id, sender_user)?;
|
||||||
@@ -50,12 +58,18 @@ pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) ->
|
|||||||
/// Deletes a room alias from this server.
|
/// Deletes a room alias from this server.
|
||||||
///
|
///
|
||||||
/// - TODO: Update canonical alias event
|
/// - TODO: Update canonical alias event
|
||||||
pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) -> Result<delete_alias::v3::Response> {
|
pub(crate) async fn delete_alias_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<delete_alias::v3::Request>,
|
||||||
|
) -> Result<delete_alias::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
service::rooms::alias::appservice_checks(&body.room_alias, &body.appservice_info).await?;
|
services
|
||||||
|
.rooms
|
||||||
|
.alias
|
||||||
|
.appservice_checks(&body.room_alias, &body.appservice_info)
|
||||||
|
.await?;
|
||||||
|
|
||||||
if services()
|
if services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_local_alias(&body.room_alias)?
|
.resolve_local_alias(&body.room_alias)?
|
||||||
@@ -64,7 +78,7 @@ pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) ->
|
|||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Alias does not exist."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Alias does not exist."));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.remove_alias(&body.room_alias, sender_user)
|
.remove_alias(&body.room_alias, sender_user)
|
||||||
@@ -78,11 +92,13 @@ pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) ->
|
|||||||
/// # `GET /_matrix/client/v3/directory/room/{roomAlias}`
|
/// # `GET /_matrix/client/v3/directory/room/{roomAlias}`
|
||||||
///
|
///
|
||||||
/// Resolve an alias locally or over federation.
|
/// Resolve an alias locally or over federation.
|
||||||
pub(crate) async fn get_alias_route(body: Ruma<get_alias::v3::Request>) -> Result<get_alias::v3::Response> {
|
pub(crate) async fn get_alias_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_alias::v3::Request>,
|
||||||
|
) -> Result<get_alias::v3::Response> {
|
||||||
let room_alias = body.body.room_alias;
|
let room_alias = body.body.room_alias;
|
||||||
let servers = None;
|
let servers = None;
|
||||||
|
|
||||||
let Ok((room_id, pre_servers)) = services()
|
let Ok((room_id, pre_servers)) = services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_alias(&room_alias, servers.as_ref())
|
.resolve_alias(&room_alias, servers.as_ref())
|
||||||
@@ -91,17 +107,17 @@ pub(crate) async fn get_alias_route(body: Ruma<get_alias::v3::Request>) -> Resul
|
|||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room with alias not found."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Room with alias not found."));
|
||||||
};
|
};
|
||||||
|
|
||||||
let servers = room_available_servers(&room_id, &room_alias, &pre_servers);
|
let servers = room_available_servers(&services, &room_id, &room_alias, &pre_servers);
|
||||||
debug!(?room_alias, ?room_id, "available servers: {servers:?}");
|
debug!(?room_alias, ?room_id, "available servers: {servers:?}");
|
||||||
|
|
||||||
Ok(get_alias::v3::Response::new(room_id, servers))
|
Ok(get_alias::v3::Response::new(room_id, servers))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn room_available_servers(
|
fn room_available_servers(
|
||||||
room_id: &RoomId, room_alias: &RoomAliasId, pre_servers: &Option<Vec<OwnedServerName>>,
|
services: &Services, room_id: &RoomId, room_alias: &RoomAliasId, pre_servers: &Option<Vec<OwnedServerName>>,
|
||||||
) -> Vec<OwnedServerName> {
|
) -> Vec<OwnedServerName> {
|
||||||
// find active servers in room state cache to suggest
|
// find active servers in room state cache to suggest
|
||||||
let mut servers: Vec<OwnedServerName> = services()
|
let mut servers: Vec<OwnedServerName> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_servers(room_id)
|
.room_servers(room_id)
|
||||||
@@ -124,10 +140,10 @@ fn room_available_servers(
|
|||||||
// prefer the room alias server first
|
// prefer the room alias server first
|
||||||
if let Some(server_index) = servers
|
if let Some(server_index) = servers
|
||||||
.iter()
|
.iter()
|
||||||
.position(|server_name| server_is_ours(server_name))
|
.position(|server_name| services.globals.server_is_ours(server_name))
|
||||||
{
|
{
|
||||||
servers.swap_remove(server_index);
|
servers.swap_remove(server_index);
|
||||||
servers.insert(0, services().globals.server_name().to_owned());
|
servers.insert(0, services.globals.server_name().to_owned());
|
||||||
} else if let Some(alias_server_index) = servers
|
} else if let Some(alias_server_index) = servers
|
||||||
.iter()
|
.iter()
|
||||||
.position(|server| server == room_alias.server_name())
|
.position(|server| server == room_alias.server_name())
|
||||||
|
|||||||
+49
-62
@@ -1,3 +1,4 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
backup::{
|
backup::{
|
||||||
@@ -11,16 +12,16 @@ use ruma::{
|
|||||||
UInt,
|
UInt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/room_keys/version`
|
/// # `POST /_matrix/client/r0/room_keys/version`
|
||||||
///
|
///
|
||||||
/// Creates a new backup.
|
/// Creates a new backup.
|
||||||
pub(crate) async fn create_backup_version_route(
|
pub(crate) async fn create_backup_version_route(
|
||||||
body: Ruma<create_backup_version::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<create_backup_version::v3::Request>,
|
||||||
) -> Result<create_backup_version::v3::Response> {
|
) -> Result<create_backup_version::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let version = services()
|
let version = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.create_backup(sender_user, &body.algorithm)?;
|
.create_backup(sender_user, &body.algorithm)?;
|
||||||
|
|
||||||
@@ -34,10 +35,10 @@ pub(crate) async fn create_backup_version_route(
|
|||||||
/// Update information about an existing backup. Only `auth_data` can be
|
/// Update information about an existing backup. Only `auth_data` can be
|
||||||
/// modified.
|
/// modified.
|
||||||
pub(crate) async fn update_backup_version_route(
|
pub(crate) async fn update_backup_version_route(
|
||||||
body: Ruma<update_backup_version::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<update_backup_version::v3::Request>,
|
||||||
) -> Result<update_backup_version::v3::Response> {
|
) -> Result<update_backup_version::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.update_backup(sender_user, &body.version, &body.algorithm)?;
|
.update_backup(sender_user, &body.version, &body.algorithm)?;
|
||||||
|
|
||||||
@@ -48,20 +49,20 @@ pub(crate) async fn update_backup_version_route(
|
|||||||
///
|
///
|
||||||
/// Get information about the latest backup version.
|
/// Get information about the latest backup version.
|
||||||
pub(crate) async fn get_latest_backup_info_route(
|
pub(crate) async fn get_latest_backup_info_route(
|
||||||
body: Ruma<get_latest_backup_info::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_latest_backup_info::v3::Request>,
|
||||||
) -> Result<get_latest_backup_info::v3::Response> {
|
) -> Result<get_latest_backup_info::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let (version, algorithm) = services()
|
let (version, algorithm) = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_latest_backup(sender_user)?
|
.get_latest_backup(sender_user)?
|
||||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
|
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
|
||||||
|
|
||||||
Ok(get_latest_backup_info::v3::Response {
|
Ok(get_latest_backup_info::v3::Response {
|
||||||
algorithm,
|
algorithm,
|
||||||
count: (UInt::try_from(services().key_backups.count_keys(sender_user, &version)?)
|
count: (UInt::try_from(services.key_backups.count_keys(sender_user, &version)?)
|
||||||
.expect("user backup keys count should not be that high")),
|
.expect("user backup keys count should not be that high")),
|
||||||
etag: services().key_backups.get_etag(sender_user, &version)?,
|
etag: services.key_backups.get_etag(sender_user, &version)?,
|
||||||
version,
|
version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -70,10 +71,10 @@ pub(crate) async fn get_latest_backup_info_route(
|
|||||||
///
|
///
|
||||||
/// Get information about an existing backup.
|
/// Get information about an existing backup.
|
||||||
pub(crate) async fn get_backup_info_route(
|
pub(crate) async fn get_backup_info_route(
|
||||||
body: Ruma<get_backup_info::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_backup_info::v3::Request>,
|
||||||
) -> Result<get_backup_info::v3::Response> {
|
) -> Result<get_backup_info::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let algorithm = services()
|
let algorithm = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_backup(sender_user, &body.version)?
|
.get_backup(sender_user, &body.version)?
|
||||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
|
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
|
||||||
@@ -81,14 +82,12 @@ pub(crate) async fn get_backup_info_route(
|
|||||||
Ok(get_backup_info::v3::Response {
|
Ok(get_backup_info::v3::Response {
|
||||||
algorithm,
|
algorithm,
|
||||||
count: (UInt::try_from(
|
count: (UInt::try_from(
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.count_keys(sender_user, &body.version)?,
|
.count_keys(sender_user, &body.version)?,
|
||||||
)
|
)
|
||||||
.expect("user backup keys count should not be that high")),
|
.expect("user backup keys count should not be that high")),
|
||||||
etag: services()
|
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||||
.key_backups
|
|
||||||
.get_etag(sender_user, &body.version)?,
|
|
||||||
version: body.version.clone(),
|
version: body.version.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -100,11 +99,11 @@ pub(crate) async fn get_backup_info_route(
|
|||||||
/// - Deletes both information about the backup, as well as all key data related
|
/// - Deletes both information about the backup, as well as all key data related
|
||||||
/// to the backup
|
/// to the backup
|
||||||
pub(crate) async fn delete_backup_version_route(
|
pub(crate) async fn delete_backup_version_route(
|
||||||
body: Ruma<delete_backup_version::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<delete_backup_version::v3::Request>,
|
||||||
) -> Result<delete_backup_version::v3::Response> {
|
) -> Result<delete_backup_version::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.delete_backup(sender_user, &body.version)?;
|
.delete_backup(sender_user, &body.version)?;
|
||||||
|
|
||||||
@@ -120,12 +119,12 @@ pub(crate) async fn delete_backup_version_route(
|
|||||||
/// - Adds the keys to the backup
|
/// - Adds the keys to the backup
|
||||||
/// - Returns the new number of keys in this backup and the etag
|
/// - Returns the new number of keys in this backup and the etag
|
||||||
pub(crate) async fn add_backup_keys_route(
|
pub(crate) async fn add_backup_keys_route(
|
||||||
body: Ruma<add_backup_keys::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<add_backup_keys::v3::Request>,
|
||||||
) -> Result<add_backup_keys::v3::Response> {
|
) -> Result<add_backup_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if Some(&body.version)
|
if Some(&body.version)
|
||||||
!= services()
|
!= services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_latest_backup_version(sender_user)?
|
.get_latest_backup_version(sender_user)?
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -138,7 +137,7 @@ pub(crate) async fn add_backup_keys_route(
|
|||||||
|
|
||||||
for (room_id, room) in &body.rooms {
|
for (room_id, room) in &body.rooms {
|
||||||
for (session_id, key_data) in &room.sessions {
|
for (session_id, key_data) in &room.sessions {
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.add_key(sender_user, &body.version, room_id, session_id, key_data)?;
|
.add_key(sender_user, &body.version, room_id, session_id, key_data)?;
|
||||||
}
|
}
|
||||||
@@ -146,14 +145,12 @@ pub(crate) async fn add_backup_keys_route(
|
|||||||
|
|
||||||
Ok(add_backup_keys::v3::Response {
|
Ok(add_backup_keys::v3::Response {
|
||||||
count: (UInt::try_from(
|
count: (UInt::try_from(
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.count_keys(sender_user, &body.version)?,
|
.count_keys(sender_user, &body.version)?,
|
||||||
)
|
)
|
||||||
.expect("user backup keys count should not be that high")),
|
.expect("user backup keys count should not be that high")),
|
||||||
etag: services()
|
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||||
.key_backups
|
|
||||||
.get_etag(sender_user, &body.version)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,12 +163,12 @@ pub(crate) async fn add_backup_keys_route(
|
|||||||
/// - Adds the keys to the backup
|
/// - Adds the keys to the backup
|
||||||
/// - Returns the new number of keys in this backup and the etag
|
/// - Returns the new number of keys in this backup and the etag
|
||||||
pub(crate) async fn add_backup_keys_for_room_route(
|
pub(crate) async fn add_backup_keys_for_room_route(
|
||||||
body: Ruma<add_backup_keys_for_room::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_room::v3::Request>,
|
||||||
) -> Result<add_backup_keys_for_room::v3::Response> {
|
) -> Result<add_backup_keys_for_room::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if Some(&body.version)
|
if Some(&body.version)
|
||||||
!= services()
|
!= services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_latest_backup_version(sender_user)?
|
.get_latest_backup_version(sender_user)?
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -183,21 +180,19 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (session_id, key_data) in &body.sessions {
|
for (session_id, key_data) in &body.sessions {
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.add_key(sender_user, &body.version, &body.room_id, session_id, key_data)?;
|
.add_key(sender_user, &body.version, &body.room_id, session_id, key_data)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(add_backup_keys_for_room::v3::Response {
|
Ok(add_backup_keys_for_room::v3::Response {
|
||||||
count: (UInt::try_from(
|
count: (UInt::try_from(
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.count_keys(sender_user, &body.version)?,
|
.count_keys(sender_user, &body.version)?,
|
||||||
)
|
)
|
||||||
.expect("user backup keys count should not be that high")),
|
.expect("user backup keys count should not be that high")),
|
||||||
etag: services()
|
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||||
.key_backups
|
|
||||||
.get_etag(sender_user, &body.version)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,12 +205,12 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
|||||||
/// - Adds the keys to the backup
|
/// - Adds the keys to the backup
|
||||||
/// - Returns the new number of keys in this backup and the etag
|
/// - Returns the new number of keys in this backup and the etag
|
||||||
pub(crate) async fn add_backup_keys_for_session_route(
|
pub(crate) async fn add_backup_keys_for_session_route(
|
||||||
body: Ruma<add_backup_keys_for_session::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_session::v3::Request>,
|
||||||
) -> Result<add_backup_keys_for_session::v3::Response> {
|
) -> Result<add_backup_keys_for_session::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if Some(&body.version)
|
if Some(&body.version)
|
||||||
!= services()
|
!= services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_latest_backup_version(sender_user)?
|
.get_latest_backup_version(sender_user)?
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -226,20 +221,18 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.add_key(sender_user, &body.version, &body.room_id, &body.session_id, &body.session_data)?;
|
.add_key(sender_user, &body.version, &body.room_id, &body.session_id, &body.session_data)?;
|
||||||
|
|
||||||
Ok(add_backup_keys_for_session::v3::Response {
|
Ok(add_backup_keys_for_session::v3::Response {
|
||||||
count: (UInt::try_from(
|
count: (UInt::try_from(
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.count_keys(sender_user, &body.version)?,
|
.count_keys(sender_user, &body.version)?,
|
||||||
)
|
)
|
||||||
.expect("user backup keys count should not be that high")),
|
.expect("user backup keys count should not be that high")),
|
||||||
etag: services()
|
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||||
.key_backups
|
|
||||||
.get_etag(sender_user, &body.version)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,11 +240,11 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
|||||||
///
|
///
|
||||||
/// Retrieves all keys from the backup.
|
/// Retrieves all keys from the backup.
|
||||||
pub(crate) async fn get_backup_keys_route(
|
pub(crate) async fn get_backup_keys_route(
|
||||||
body: Ruma<get_backup_keys::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_backup_keys::v3::Request>,
|
||||||
) -> Result<get_backup_keys::v3::Response> {
|
) -> Result<get_backup_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let rooms = services().key_backups.get_all(sender_user, &body.version)?;
|
let rooms = services.key_backups.get_all(sender_user, &body.version)?;
|
||||||
|
|
||||||
Ok(get_backup_keys::v3::Response {
|
Ok(get_backup_keys::v3::Response {
|
||||||
rooms,
|
rooms,
|
||||||
@@ -262,11 +255,11 @@ pub(crate) async fn get_backup_keys_route(
|
|||||||
///
|
///
|
||||||
/// Retrieves all keys from the backup for a given room.
|
/// Retrieves all keys from the backup for a given room.
|
||||||
pub(crate) async fn get_backup_keys_for_room_route(
|
pub(crate) async fn get_backup_keys_for_room_route(
|
||||||
body: Ruma<get_backup_keys_for_room::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_room::v3::Request>,
|
||||||
) -> Result<get_backup_keys_for_room::v3::Response> {
|
) -> Result<get_backup_keys_for_room::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let sessions = services()
|
let sessions = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_room(sender_user, &body.version, &body.room_id)?;
|
.get_room(sender_user, &body.version, &body.room_id)?;
|
||||||
|
|
||||||
@@ -279,11 +272,11 @@ pub(crate) async fn get_backup_keys_for_room_route(
|
|||||||
///
|
///
|
||||||
/// Retrieves a key from the backup.
|
/// Retrieves a key from the backup.
|
||||||
pub(crate) async fn get_backup_keys_for_session_route(
|
pub(crate) async fn get_backup_keys_for_session_route(
|
||||||
body: Ruma<get_backup_keys_for_session::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_session::v3::Request>,
|
||||||
) -> Result<get_backup_keys_for_session::v3::Response> {
|
) -> Result<get_backup_keys_for_session::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let key_data = services()
|
let key_data = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_session(sender_user, &body.version, &body.room_id, &body.session_id)?
|
.get_session(sender_user, &body.version, &body.room_id, &body.session_id)?
|
||||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Backup key not found for this user's session."))?;
|
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Backup key not found for this user's session."))?;
|
||||||
@@ -297,24 +290,22 @@ pub(crate) async fn get_backup_keys_for_session_route(
|
|||||||
///
|
///
|
||||||
/// Delete the keys from the backup.
|
/// Delete the keys from the backup.
|
||||||
pub(crate) async fn delete_backup_keys_route(
|
pub(crate) async fn delete_backup_keys_route(
|
||||||
body: Ruma<delete_backup_keys::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<delete_backup_keys::v3::Request>,
|
||||||
) -> Result<delete_backup_keys::v3::Response> {
|
) -> Result<delete_backup_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.delete_all_keys(sender_user, &body.version)?;
|
.delete_all_keys(sender_user, &body.version)?;
|
||||||
|
|
||||||
Ok(delete_backup_keys::v3::Response {
|
Ok(delete_backup_keys::v3::Response {
|
||||||
count: (UInt::try_from(
|
count: (UInt::try_from(
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.count_keys(sender_user, &body.version)?,
|
.count_keys(sender_user, &body.version)?,
|
||||||
)
|
)
|
||||||
.expect("user backup keys count should not be that high")),
|
.expect("user backup keys count should not be that high")),
|
||||||
etag: services()
|
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||||
.key_backups
|
|
||||||
.get_etag(sender_user, &body.version)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,24 +313,22 @@ pub(crate) async fn delete_backup_keys_route(
|
|||||||
///
|
///
|
||||||
/// Delete the keys from the backup for a given room.
|
/// Delete the keys from the backup for a given room.
|
||||||
pub(crate) async fn delete_backup_keys_for_room_route(
|
pub(crate) async fn delete_backup_keys_for_room_route(
|
||||||
body: Ruma<delete_backup_keys_for_room::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_room::v3::Request>,
|
||||||
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.delete_room_keys(sender_user, &body.version, &body.room_id)?;
|
.delete_room_keys(sender_user, &body.version, &body.room_id)?;
|
||||||
|
|
||||||
Ok(delete_backup_keys_for_room::v3::Response {
|
Ok(delete_backup_keys_for_room::v3::Response {
|
||||||
count: (UInt::try_from(
|
count: (UInt::try_from(
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.count_keys(sender_user, &body.version)?,
|
.count_keys(sender_user, &body.version)?,
|
||||||
)
|
)
|
||||||
.expect("user backup keys count should not be that high")),
|
.expect("user backup keys count should not be that high")),
|
||||||
etag: services()
|
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||||
.key_backups
|
|
||||||
.get_etag(sender_user, &body.version)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,23 +336,21 @@ pub(crate) async fn delete_backup_keys_for_room_route(
|
|||||||
///
|
///
|
||||||
/// Delete a key from the backup.
|
/// Delete a key from the backup.
|
||||||
pub(crate) async fn delete_backup_keys_for_session_route(
|
pub(crate) async fn delete_backup_keys_for_session_route(
|
||||||
body: Ruma<delete_backup_keys_for_session::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_session::v3::Request>,
|
||||||
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.delete_room_key(sender_user, &body.version, &body.room_id, &body.session_id)?;
|
.delete_room_key(sender_user, &body.version, &body.room_id, &body.session_id)?;
|
||||||
|
|
||||||
Ok(delete_backup_keys_for_session::v3::Response {
|
Ok(delete_backup_keys_for_session::v3::Response {
|
||||||
count: (UInt::try_from(
|
count: (UInt::try_from(
|
||||||
services()
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.count_keys(sender_user, &body.version)?,
|
.count_keys(sender_user, &body.version)?,
|
||||||
)
|
)
|
||||||
.expect("user backup keys count should not be that high")),
|
.expect("user backup keys count should not be that high")),
|
||||||
etag: services()
|
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||||
.key_backups
|
|
||||||
.get_etag(sender_user, &body.version)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,30 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use ruma::api::client::discovery::get_capabilities::{
|
use ruma::api::client::discovery::get_capabilities::{
|
||||||
self, Capabilities, RoomVersionStability, RoomVersionsCapability, ThirdPartyIdChangesCapability,
|
self, Capabilities, RoomVersionStability, RoomVersionsCapability, ThirdPartyIdChangesCapability,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Result, Ruma};
|
use crate::{Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/v3/capabilities`
|
/// # `GET /_matrix/client/v3/capabilities`
|
||||||
///
|
///
|
||||||
/// Get information on the supported feature set and other relevent capabilities
|
/// Get information on the supported feature set and other relevent capabilities
|
||||||
/// of this server.
|
/// of this server.
|
||||||
pub(crate) async fn get_capabilities_route(
|
pub(crate) async fn get_capabilities_route(
|
||||||
_body: Ruma<get_capabilities::v3::Request>,
|
State(services): State<crate::State>, _body: Ruma<get_capabilities::v3::Request>,
|
||||||
) -> Result<get_capabilities::v3::Response> {
|
) -> Result<get_capabilities::v3::Response> {
|
||||||
let mut available = BTreeMap::new();
|
let mut available = BTreeMap::new();
|
||||||
for room_version in &services().globals.unstable_room_versions {
|
for room_version in &services.globals.unstable_room_versions {
|
||||||
available.insert(room_version.clone(), RoomVersionStability::Unstable);
|
available.insert(room_version.clone(), RoomVersionStability::Unstable);
|
||||||
}
|
}
|
||||||
for room_version in &services().globals.stable_room_versions {
|
for room_version in &services.globals.stable_room_versions {
|
||||||
available.insert(room_version.clone(), RoomVersionStability::Stable);
|
available.insert(room_version.clone(), RoomVersionStability::Stable);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut capabilities = Capabilities::default();
|
let mut capabilities = Capabilities::default();
|
||||||
capabilities.room_versions = RoomVersionsCapability {
|
capabilities.room_versions = RoomVersionsCapability {
|
||||||
default: services().globals.default_room_version(),
|
default: services.globals.default_room_version(),
|
||||||
available,
|
available,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+19
-10
@@ -1,3 +1,4 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
config::{get_global_account_data, get_room_account_data, set_global_account_data, set_room_account_data},
|
config::{get_global_account_data, get_room_account_data, set_global_account_data, set_room_account_data},
|
||||||
@@ -10,15 +11,21 @@ use ruma::{
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::{json, value::RawValue as RawJsonValue};
|
use serde_json::{json, value::RawValue as RawJsonValue};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{service::Services, Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}`
|
/// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}`
|
||||||
///
|
///
|
||||||
/// Sets some account data for the sender user.
|
/// Sets some account data for the sender user.
|
||||||
pub(crate) async fn set_global_account_data_route(
|
pub(crate) async fn set_global_account_data_route(
|
||||||
body: Ruma<set_global_account_data::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<set_global_account_data::v3::Request>,
|
||||||
) -> Result<set_global_account_data::v3::Response> {
|
) -> Result<set_global_account_data::v3::Response> {
|
||||||
set_account_data(None, &body.sender_user, &body.event_type.to_string(), body.data.json())?;
|
set_account_data(
|
||||||
|
&services,
|
||||||
|
None,
|
||||||
|
&body.sender_user,
|
||||||
|
&body.event_type.to_string(),
|
||||||
|
body.data.json(),
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(set_global_account_data::v3::Response {})
|
Ok(set_global_account_data::v3::Response {})
|
||||||
}
|
}
|
||||||
@@ -27,9 +34,10 @@ pub(crate) async fn set_global_account_data_route(
|
|||||||
///
|
///
|
||||||
/// Sets some room account data for the sender user.
|
/// Sets some room account data for the sender user.
|
||||||
pub(crate) async fn set_room_account_data_route(
|
pub(crate) async fn set_room_account_data_route(
|
||||||
body: Ruma<set_room_account_data::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<set_room_account_data::v3::Request>,
|
||||||
) -> Result<set_room_account_data::v3::Response> {
|
) -> Result<set_room_account_data::v3::Response> {
|
||||||
set_account_data(
|
set_account_data(
|
||||||
|
&services,
|
||||||
Some(&body.room_id),
|
Some(&body.room_id),
|
||||||
&body.sender_user,
|
&body.sender_user,
|
||||||
&body.event_type.to_string(),
|
&body.event_type.to_string(),
|
||||||
@@ -43,11 +51,11 @@ pub(crate) async fn set_room_account_data_route(
|
|||||||
///
|
///
|
||||||
/// Gets some account data for the sender user.
|
/// Gets some account data for the sender user.
|
||||||
pub(crate) async fn get_global_account_data_route(
|
pub(crate) async fn get_global_account_data_route(
|
||||||
body: Ruma<get_global_account_data::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_global_account_data::v3::Request>,
|
||||||
) -> Result<get_global_account_data::v3::Response> {
|
) -> Result<get_global_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event: Box<RawJsonValue> = services()
|
let event: Box<RawJsonValue> = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, body.event_type.to_string().into())?
|
.get(None, sender_user, body.event_type.to_string().into())?
|
||||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||||
@@ -65,11 +73,11 @@ pub(crate) async fn get_global_account_data_route(
|
|||||||
///
|
///
|
||||||
/// Gets some room account data for the sender user.
|
/// Gets some room account data for the sender user.
|
||||||
pub(crate) async fn get_room_account_data_route(
|
pub(crate) async fn get_room_account_data_route(
|
||||||
body: Ruma<get_room_account_data::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_room_account_data::v3::Request>,
|
||||||
) -> Result<get_room_account_data::v3::Response> {
|
) -> Result<get_room_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event: Box<RawJsonValue> = services()
|
let event: Box<RawJsonValue> = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(Some(&body.room_id), sender_user, body.event_type.clone())?
|
.get(Some(&body.room_id), sender_user, body.event_type.clone())?
|
||||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||||
@@ -84,14 +92,15 @@ pub(crate) async fn get_room_account_data_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_account_data(
|
fn set_account_data(
|
||||||
room_id: Option<&RoomId>, sender_user: &Option<OwnedUserId>, event_type: &str, data: &RawJsonValue,
|
services: &Services, room_id: Option<&RoomId>, sender_user: &Option<OwnedUserId>, event_type: &str,
|
||||||
|
data: &RawJsonValue,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let sender_user = sender_user.as_ref().expect("user is authenticated");
|
let sender_user = sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let data: serde_json::Value =
|
let data: serde_json::Value =
|
||||||
serde_json::from_str(data.get()).map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
|
serde_json::from_str(data.get()).map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
|
||||||
|
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
room_id,
|
room_id,
|
||||||
sender_user,
|
sender_user,
|
||||||
event_type.into(),
|
event_type.into(),
|
||||||
|
|||||||
+21
-18
@@ -1,12 +1,13 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{context::get_context, error::ErrorKind, filter::LazyLoadOptions},
|
api::client::{context::get_context, error::ErrorKind, filter::LazyLoadOptions},
|
||||||
events::StateEventType,
|
events::StateEventType,
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/context`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/context`
|
||||||
///
|
///
|
||||||
@@ -14,7 +15,9 @@ use crate::{services, Error, Result, Ruma};
|
|||||||
///
|
///
|
||||||
/// - Only works if the user is joined (TODO: always allow, but only show events
|
/// - Only works if the user is joined (TODO: always allow, but only show events
|
||||||
/// if the user was joined, depending on history_visibility)
|
/// if the user was joined, depending on history_visibility)
|
||||||
pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> Result<get_context::v3::Response> {
|
pub(crate) async fn get_context_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_context::v3::Request>,
|
||||||
|
) -> Result<get_context::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -27,13 +30,13 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
|
|
||||||
let mut lazy_loaded = HashSet::new();
|
let mut lazy_loaded = HashSet::new();
|
||||||
|
|
||||||
let base_token = services()
|
let base_token = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_pdu_count(&body.event_id)?
|
.get_pdu_count(&body.event_id)?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Base event id not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Base event id not found."))?;
|
||||||
|
|
||||||
let base_event = services()
|
let base_event = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_pdu(&body.event_id)?
|
.get_pdu(&body.event_id)?
|
||||||
@@ -41,7 +44,7 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
|
|
||||||
let room_id = base_event.room_id.clone();
|
let room_id = base_event.room_id.clone();
|
||||||
|
|
||||||
if !services()
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(sender_user, &room_id, &body.event_id)?
|
.user_can_see_event(sender_user, &room_id, &body.event_id)?
|
||||||
@@ -52,7 +55,7 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
if !services.rooms.lazy_loading.lazy_load_was_sent_before(
|
||||||
sender_user,
|
sender_user,
|
||||||
sender_device,
|
sender_device,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -67,14 +70,14 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
|
|
||||||
let base_event = base_event.to_room_event();
|
let base_event = base_event.to_room_event();
|
||||||
|
|
||||||
let events_before: Vec<_> = services()
|
let events_before: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_until(sender_user, &room_id, base_token)?
|
.pdus_until(sender_user, &room_id, base_token)?
|
||||||
.take(limit / 2)
|
.take(limit / 2)
|
||||||
.filter_map(Result::ok) // Remove buggy events
|
.filter_map(Result::ok) // Remove buggy events
|
||||||
.filter(|(_, pdu)| {
|
.filter(|(_, pdu)| {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
||||||
@@ -83,7 +86,7 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (_, event) in &events_before {
|
for (_, event) in &events_before {
|
||||||
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
if !services.rooms.lazy_loading.lazy_load_was_sent_before(
|
||||||
sender_user,
|
sender_user,
|
||||||
sender_device,
|
sender_device,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -103,14 +106,14 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
.map(|(_, pdu)| pdu.to_room_event())
|
.map(|(_, pdu)| pdu.to_room_event())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let events_after: Vec<_> = services()
|
let events_after: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_after(sender_user, &room_id, base_token)?
|
.pdus_after(sender_user, &room_id, base_token)?
|
||||||
.take(limit / 2)
|
.take(limit / 2)
|
||||||
.filter_map(Result::ok) // Remove buggy events
|
.filter_map(Result::ok) // Remove buggy events
|
||||||
.filter(|(_, pdu)| {
|
.filter(|(_, pdu)| {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
||||||
@@ -119,7 +122,7 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (_, event) in &events_after {
|
for (_, event) in &events_after {
|
||||||
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
if !services.rooms.lazy_loading.lazy_load_was_sent_before(
|
||||||
sender_user,
|
sender_user,
|
||||||
sender_device,
|
sender_device,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -130,7 +133,7 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let shortstatehash = services()
|
let shortstatehash = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.pdu_shortstatehash(
|
.pdu_shortstatehash(
|
||||||
@@ -139,7 +142,7 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
.map_or(&*body.event_id, |(_, e)| &*e.event_id),
|
.map_or(&*body.event_id, |(_, e)| &*e.event_id),
|
||||||
)?
|
)?
|
||||||
.map_or(
|
.map_or(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state
|
.state
|
||||||
.get_room_shortstatehash(&room_id)?
|
.get_room_shortstatehash(&room_id)?
|
||||||
@@ -147,7 +150,7 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
|hash| hash,
|
|hash| hash,
|
||||||
);
|
);
|
||||||
|
|
||||||
let state_ids = services()
|
let state_ids = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_full_ids(shortstatehash)
|
.state_full_ids(shortstatehash)
|
||||||
@@ -165,20 +168,20 @@ pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> R
|
|||||||
let mut state = Vec::with_capacity(state_ids.len());
|
let mut state = Vec::with_capacity(state_ids.len());
|
||||||
|
|
||||||
for (shortstatekey, id) in state_ids {
|
for (shortstatekey, id) in state_ids {
|
||||||
let (event_type, state_key) = services()
|
let (event_type, state_key) = services
|
||||||
.rooms
|
.rooms
|
||||||
.short
|
.short
|
||||||
.get_statekey_from_short(shortstatekey)?;
|
.get_statekey_from_short(shortstatekey)?;
|
||||||
|
|
||||||
if event_type != StateEventType::RoomMember {
|
if event_type != StateEventType::RoomMember {
|
||||||
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
|
let Some(pdu) = services.rooms.timeline.get_pdu(&id)? else {
|
||||||
error!("Pdu in state not found: {}", id);
|
error!("Pdu in state not found: {}", id);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
state.push(pdu.to_state_event());
|
state.push(pdu.to_state_event());
|
||||||
} else if !lazy_load_enabled || lazy_loaded.contains(&state_key) {
|
} else if !lazy_load_enabled || lazy_loaded.contains(&state_key) {
|
||||||
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
|
let Some(pdu) = services.rooms.timeline.get_pdu(&id)? else {
|
||||||
error!("Pdu in state not found: {}", id);
|
error!("Pdu in state not found: {}", id);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|||||||
+25
-18
@@ -1,3 +1,4 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::api::client::{
|
use ruma::api::client::{
|
||||||
device::{self, delete_device, delete_devices, get_device, get_devices, update_device},
|
device::{self, delete_device, delete_devices, get_device, get_devices, update_device},
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
@@ -5,15 +6,17 @@ use ruma::api::client::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::SESSION_ID_LENGTH;
|
use super::SESSION_ID_LENGTH;
|
||||||
use crate::{services, utils, Error, Result, Ruma};
|
use crate::{utils, Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/devices`
|
/// # `GET /_matrix/client/r0/devices`
|
||||||
///
|
///
|
||||||
/// Get metadata on all devices of the sender user.
|
/// Get metadata on all devices of the sender user.
|
||||||
pub(crate) async fn get_devices_route(body: Ruma<get_devices::v3::Request>) -> Result<get_devices::v3::Response> {
|
pub(crate) async fn get_devices_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_devices::v3::Request>,
|
||||||
|
) -> Result<get_devices::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let devices: Vec<device::Device> = services()
|
let devices: Vec<device::Device> = services
|
||||||
.users
|
.users
|
||||||
.all_devices_metadata(sender_user)
|
.all_devices_metadata(sender_user)
|
||||||
.filter_map(Result::ok) // Filter out buggy devices
|
.filter_map(Result::ok) // Filter out buggy devices
|
||||||
@@ -27,10 +30,12 @@ pub(crate) async fn get_devices_route(body: Ruma<get_devices::v3::Request>) -> R
|
|||||||
/// # `GET /_matrix/client/r0/devices/{deviceId}`
|
/// # `GET /_matrix/client/r0/devices/{deviceId}`
|
||||||
///
|
///
|
||||||
/// Get metadata on a single device of the sender user.
|
/// Get metadata on a single device of the sender user.
|
||||||
pub(crate) async fn get_device_route(body: Ruma<get_device::v3::Request>) -> Result<get_device::v3::Response> {
|
pub(crate) async fn get_device_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_device::v3::Request>,
|
||||||
|
) -> Result<get_device::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let device = services()
|
let device = services
|
||||||
.users
|
.users
|
||||||
.get_device_metadata(sender_user, &body.body.device_id)?
|
.get_device_metadata(sender_user, &body.body.device_id)?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?;
|
||||||
@@ -43,17 +48,19 @@ pub(crate) async fn get_device_route(body: Ruma<get_device::v3::Request>) -> Res
|
|||||||
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
|
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
|
||||||
///
|
///
|
||||||
/// Updates the metadata on a given device of the sender user.
|
/// Updates the metadata on a given device of the sender user.
|
||||||
pub(crate) async fn update_device_route(body: Ruma<update_device::v3::Request>) -> Result<update_device::v3::Response> {
|
pub(crate) async fn update_device_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<update_device::v3::Request>,
|
||||||
|
) -> Result<update_device::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let mut device = services()
|
let mut device = services
|
||||||
.users
|
.users
|
||||||
.get_device_metadata(sender_user, &body.device_id)?
|
.get_device_metadata(sender_user, &body.device_id)?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?;
|
||||||
|
|
||||||
device.display_name.clone_from(&body.display_name);
|
device.display_name.clone_from(&body.display_name);
|
||||||
|
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.update_device_metadata(sender_user, &body.device_id, &device)?;
|
.update_device_metadata(sender_user, &body.device_id, &device)?;
|
||||||
|
|
||||||
@@ -70,7 +77,9 @@ pub(crate) async fn update_device_route(body: Ruma<update_device::v3::Request>)
|
|||||||
/// last seen ts)
|
/// last seen ts)
|
||||||
/// - Forgets to-device events
|
/// - Forgets to-device events
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
pub(crate) async fn delete_device_route(body: Ruma<delete_device::v3::Request>) -> Result<delete_device::v3::Response> {
|
pub(crate) async fn delete_device_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<delete_device::v3::Request>,
|
||||||
|
) -> Result<delete_device::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -86,7 +95,7 @@ pub(crate) async fn delete_device_route(body: Ruma<delete_device::v3::Request>)
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(auth) = &body.auth {
|
if let Some(auth) = &body.auth {
|
||||||
let (worked, uiaainfo) = services()
|
let (worked, uiaainfo) = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||||
if !worked {
|
if !worked {
|
||||||
@@ -95,7 +104,7 @@ pub(crate) async fn delete_device_route(body: Ruma<delete_device::v3::Request>)
|
|||||||
// Success!
|
// Success!
|
||||||
} else if let Some(json) = body.json_body {
|
} else if let Some(json) = body.json_body {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
services()
|
services
|
||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
@@ -103,9 +112,7 @@ pub(crate) async fn delete_device_route(body: Ruma<delete_device::v3::Request>)
|
|||||||
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services.users.remove_device(sender_user, &body.device_id)?;
|
||||||
.users
|
|
||||||
.remove_device(sender_user, &body.device_id)?;
|
|
||||||
|
|
||||||
Ok(delete_device::v3::Response {})
|
Ok(delete_device::v3::Response {})
|
||||||
}
|
}
|
||||||
@@ -123,7 +130,7 @@ pub(crate) async fn delete_device_route(body: Ruma<delete_device::v3::Request>)
|
|||||||
/// - Forgets to-device events
|
/// - Forgets to-device events
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
pub(crate) async fn delete_devices_route(
|
pub(crate) async fn delete_devices_route(
|
||||||
body: Ruma<delete_devices::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<delete_devices::v3::Request>,
|
||||||
) -> Result<delete_devices::v3::Response> {
|
) -> Result<delete_devices::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
@@ -140,7 +147,7 @@ pub(crate) async fn delete_devices_route(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(auth) = &body.auth {
|
if let Some(auth) = &body.auth {
|
||||||
let (worked, uiaainfo) = services()
|
let (worked, uiaainfo) = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||||
if !worked {
|
if !worked {
|
||||||
@@ -149,7 +156,7 @@ pub(crate) async fn delete_devices_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else if let Some(json) = body.json_body {
|
} else if let Some(json) = body.json_body {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
services()
|
services
|
||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
@@ -158,7 +165,7 @@ pub(crate) async fn delete_devices_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for device_id in &body.devices {
|
for device_id in &body.devices {
|
||||||
services().users.remove_device(sender_user, device_id)?;
|
services.users.remove_device(sender_user, device_id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(delete_devices::v3::Response {})
|
Ok(delete_devices::v3::Response {})
|
||||||
|
|||||||
+76
-33
@@ -1,4 +1,6 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
|
use conduit::{err, info, warn, Error, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::{
|
client::{
|
||||||
@@ -10,14 +12,17 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
directory::{Filter, PublicRoomJoinRule, PublicRoomsChunk, RoomNetwork},
|
directory::{Filter, PublicRoomJoinRule, PublicRoomsChunk, RoomNetwork},
|
||||||
events::{
|
events::{
|
||||||
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
|
room::{
|
||||||
|
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||||
|
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||||
|
},
|
||||||
StateEventType,
|
StateEventType,
|
||||||
},
|
},
|
||||||
uint, ServerName, UInt,
|
uint, RoomId, ServerName, UInt, UserId,
|
||||||
};
|
};
|
||||||
use tracing::{error, info, warn};
|
use service::Services;
|
||||||
|
|
||||||
use crate::{service::server_is_ours, services, Error, Result, Ruma};
|
use crate::Ruma;
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/publicRooms`
|
/// # `POST /_matrix/client/v3/publicRooms`
|
||||||
///
|
///
|
||||||
@@ -26,10 +31,11 @@ use crate::{service::server_is_ours, services, Error, Result, Ruma};
|
|||||||
/// - Rooms are ordered by the number of joined members
|
/// - Rooms are ordered by the number of joined members
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||||
pub(crate) async fn get_public_rooms_filtered_route(
|
pub(crate) async fn get_public_rooms_filtered_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms_filtered::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_public_rooms_filtered::v3::Request>,
|
||||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||||
if let Some(server) = &body.server {
|
if let Some(server) = &body.server {
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.forbidden_remote_room_directory_server_names()
|
.forbidden_remote_room_directory_server_names()
|
||||||
.contains(server)
|
.contains(server)
|
||||||
@@ -42,6 +48,7 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let response = get_public_rooms_filtered_helper(
|
let response = get_public_rooms_filtered_helper(
|
||||||
|
&services,
|
||||||
body.server.as_deref(),
|
body.server.as_deref(),
|
||||||
body.limit,
|
body.limit,
|
||||||
body.since.as_deref(),
|
body.since.as_deref(),
|
||||||
@@ -64,10 +71,11 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
|||||||
/// - Rooms are ordered by the number of joined members
|
/// - Rooms are ordered by the number of joined members
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||||
pub(crate) async fn get_public_rooms_route(
|
pub(crate) async fn get_public_rooms_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_public_rooms::v3::Request>,
|
||||||
) -> Result<get_public_rooms::v3::Response> {
|
) -> Result<get_public_rooms::v3::Response> {
|
||||||
if let Some(server) = &body.server {
|
if let Some(server) = &body.server {
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.forbidden_remote_room_directory_server_names()
|
.forbidden_remote_room_directory_server_names()
|
||||||
.contains(server)
|
.contains(server)
|
||||||
@@ -80,6 +88,7 @@ pub(crate) async fn get_public_rooms_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let response = get_public_rooms_filtered_helper(
|
let response = get_public_rooms_filtered_helper(
|
||||||
|
&services,
|
||||||
body.server.as_deref(),
|
body.server.as_deref(),
|
||||||
body.limit,
|
body.limit,
|
||||||
body.since.as_deref(),
|
body.since.as_deref(),
|
||||||
@@ -103,22 +112,28 @@ pub(crate) async fn get_public_rooms_route(
|
|||||||
/// # `PUT /_matrix/client/r0/directory/list/room/{roomId}`
|
/// # `PUT /_matrix/client/r0/directory/list/room/{roomId}`
|
||||||
///
|
///
|
||||||
/// Sets the visibility of a given room in the room directory.
|
/// Sets the visibility of a given room in the room directory.
|
||||||
///
|
|
||||||
/// - TODO: Access control checks
|
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "room_directory")]
|
#[tracing::instrument(skip_all, fields(%client), name = "room_directory")]
|
||||||
pub(crate) async fn set_room_visibility_route(
|
pub(crate) async fn set_room_visibility_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<set_room_visibility::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<set_room_visibility::v3::Request>,
|
||||||
) -> Result<set_room_visibility::v3::Response> {
|
) -> Result<set_room_visibility::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services().rooms.metadata.exists(&body.room_id)? {
|
if !services.rooms.metadata.exists(&body.room_id)? {
|
||||||
// Return 404 if the room doesn't exist
|
// Return 404 if the room doesn't exist
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !user_can_publish_room(&services, sender_user, &body.room_id)? {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::forbidden(),
|
||||||
|
"User is not allowed to publish this room",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match &body.visibility {
|
match &body.visibility {
|
||||||
room::Visibility::Public => {
|
room::Visibility::Public => {
|
||||||
if services().globals.config.lockdown_public_room_directory && !services().users.is_admin(sender_user)? {
|
if services.globals.config.lockdown_public_room_directory && !services.users.is_admin(sender_user)? {
|
||||||
info!(
|
info!(
|
||||||
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
|
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
|
||||||
\"lockdown_public_room_directory\" is enabled",
|
\"lockdown_public_room_directory\" is enabled",
|
||||||
@@ -131,10 +146,10 @@ pub(crate) async fn set_room_visibility_route(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
services().rooms.directory.set_public(&body.room_id)?;
|
services.rooms.directory.set_public(&body.room_id)?;
|
||||||
info!("{sender_user} made {0} public", body.room_id);
|
info!("{sender_user} made {0} public", body.room_id);
|
||||||
},
|
},
|
||||||
room::Visibility::Private => services().rooms.directory.set_not_public(&body.room_id)?,
|
room::Visibility::Private => services.rooms.directory.set_not_public(&body.room_id)?,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
@@ -150,15 +165,15 @@ pub(crate) async fn set_room_visibility_route(
|
|||||||
///
|
///
|
||||||
/// Gets the visibility of a given room in the room directory.
|
/// Gets the visibility of a given room in the room directory.
|
||||||
pub(crate) async fn get_room_visibility_route(
|
pub(crate) async fn get_room_visibility_route(
|
||||||
body: Ruma<get_room_visibility::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_room_visibility::v3::Request>,
|
||||||
) -> Result<get_room_visibility::v3::Response> {
|
) -> Result<get_room_visibility::v3::Response> {
|
||||||
if !services().rooms.metadata.exists(&body.room_id)? {
|
if !services.rooms.metadata.exists(&body.room_id)? {
|
||||||
// Return 404 if the room doesn't exist
|
// Return 404 if the room doesn't exist
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_room_visibility::v3::Response {
|
Ok(get_room_visibility::v3::Response {
|
||||||
visibility: if services().rooms.directory.is_public_room(&body.room_id)? {
|
visibility: if services.rooms.directory.is_public_room(&body.room_id)? {
|
||||||
room::Visibility::Public
|
room::Visibility::Public
|
||||||
} else {
|
} else {
|
||||||
room::Visibility::Private
|
room::Visibility::Private
|
||||||
@@ -167,10 +182,11 @@ pub(crate) async fn get_room_visibility_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_public_rooms_filtered_helper(
|
pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
server: Option<&ServerName>, limit: Option<UInt>, since: Option<&str>, filter: &Filter, _network: &RoomNetwork,
|
services: &Services, server: Option<&ServerName>, limit: Option<UInt>, since: Option<&str>, filter: &Filter,
|
||||||
|
_network: &RoomNetwork,
|
||||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||||
if let Some(other_server) = server.filter(|server_name| !server_is_ours(server_name)) {
|
if let Some(other_server) = server.filter(|server_name| !services.globals.server_is_ours(server_name)) {
|
||||||
let response = services()
|
let response = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
other_server,
|
other_server,
|
||||||
@@ -216,7 +232,7 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut all_rooms: Vec<_> = services()
|
let mut all_rooms: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.directory
|
.directory
|
||||||
.public_rooms()
|
.public_rooms()
|
||||||
@@ -224,12 +240,12 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
|||||||
let room_id = room_id?;
|
let room_id = room_id?;
|
||||||
|
|
||||||
let chunk = PublicRoomsChunk {
|
let chunk = PublicRoomsChunk {
|
||||||
canonical_alias: services()
|
canonical_alias: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_canonical_alias(&room_id)?,
|
.get_canonical_alias(&room_id)?,
|
||||||
name: services().rooms.state_accessor.get_name(&room_id)?,
|
name: services.rooms.state_accessor.get_name(&room_id)?,
|
||||||
num_joined_members: services()
|
num_joined_members: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_joined_count(&room_id)?
|
.room_joined_count(&room_id)?
|
||||||
@@ -239,24 +255,24 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
|||||||
})
|
})
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("user count should not be that big"),
|
.expect("user count should not be that big"),
|
||||||
topic: services()
|
topic: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_room_topic(&room_id)
|
.get_room_topic(&room_id)
|
||||||
.unwrap_or(None),
|
.unwrap_or(None),
|
||||||
world_readable: services().rooms.state_accessor.is_world_readable(&room_id)?,
|
world_readable: services.rooms.state_accessor.is_world_readable(&room_id)?,
|
||||||
guest_can_join: services()
|
guest_can_join: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.guest_can_join(&room_id)?,
|
.guest_can_join(&room_id)?,
|
||||||
avatar_url: services()
|
avatar_url: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_avatar(&room_id)?
|
.get_avatar(&room_id)?
|
||||||
.into_option()
|
.into_option()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.url,
|
.url,
|
||||||
join_rule: services()
|
join_rule: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room_id, &StateEventType::RoomJoinRules, "")?
|
.room_state_get(&room_id, &StateEventType::RoomJoinRules, "")?
|
||||||
@@ -268,14 +284,13 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
|||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!("Invalid room join rule event in database: {}", e);
|
err!(Database(error!("Invalid room join rule event in database: {e}")))
|
||||||
Error::BadDatabase("Invalid room join rule event in database.")
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.flatten()
|
.flatten()
|
||||||
.ok_or_else(|| Error::bad_database("Missing room join rule event for room."))?,
|
.ok_or_else(|| Error::bad_database("Missing room join rule event for room."))?,
|
||||||
room_type: services()
|
room_type: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_room_type(&room_id)?,
|
.get_room_type(&room_id)?,
|
||||||
@@ -351,3 +366,31 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
|||||||
total_room_count_estimate: Some(total_room_count_estimate),
|
total_room_count_estimate: Some(total_room_count_estimate),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check whether the user can publish to the room directory via power levels of
|
||||||
|
/// room history visibility event or room creator
|
||||||
|
fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
||||||
|
if let Some(event) = services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
||||||
|
{
|
||||||
|
serde_json::from_str(event.content.get())
|
||||||
|
.map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels"))
|
||||||
|
.map(|content: RoomPowerLevelsEventContent| {
|
||||||
|
RoomPowerLevels::from(content).user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
||||||
|
})
|
||||||
|
} else if let Some(event) =
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
|
||||||
|
{
|
||||||
|
Ok(event.sender == user_id)
|
||||||
|
} else {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::forbidden(),
|
||||||
|
"User is not allowed to publish this room",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::api::client::{
|
use ruma::api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
filter::{create_filter, get_filter},
|
filter::{create_filter, get_filter},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/user/{userId}/filter/{filterId}`
|
/// # `GET /_matrix/client/r0/user/{userId}/filter/{filterId}`
|
||||||
///
|
///
|
||||||
/// Loads a filter that was previously created.
|
/// Loads a filter that was previously created.
|
||||||
///
|
///
|
||||||
/// - A user can only access their own filters
|
/// - A user can only access their own filters
|
||||||
pub(crate) async fn get_filter_route(body: Ruma<get_filter::v3::Request>) -> Result<get_filter::v3::Response> {
|
pub(crate) async fn get_filter_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_filter::v3::Request>,
|
||||||
|
) -> Result<get_filter::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let Some(filter) = services().users.get_filter(sender_user, &body.filter_id)? else {
|
let Some(filter) = services.users.get_filter(sender_user, &body.filter_id)? else {
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found."));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,9 +25,11 @@ pub(crate) async fn get_filter_route(body: Ruma<get_filter::v3::Request>) -> Res
|
|||||||
/// # `PUT /_matrix/client/r0/user/{userId}/filter`
|
/// # `PUT /_matrix/client/r0/user/{userId}/filter`
|
||||||
///
|
///
|
||||||
/// Creates a new filter to be used by other endpoints.
|
/// Creates a new filter to be used by other endpoints.
|
||||||
pub(crate) async fn create_filter_route(body: Ruma<create_filter::v3::Request>) -> Result<create_filter::v3::Response> {
|
pub(crate) async fn create_filter_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<create_filter::v3::Request>,
|
||||||
|
) -> Result<create_filter::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
Ok(create_filter::v3::Response::new(
|
Ok(create_filter::v3::Response::new(
|
||||||
services().users.create_filter(sender_user, &body.filter)?,
|
services.users.create_filter(sender_user, &body.filter)?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-55
@@ -1,9 +1,10 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cmp,
|
|
||||||
collections::{hash_map, BTreeMap, HashMap, HashSet},
|
collections::{hash_map, BTreeMap, HashMap, HashSet},
|
||||||
time::{Duration, Instant},
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
|
use conduit::{utils, utils::math::continue_exponential_backoff_secs, Err, Error, Result};
|
||||||
use futures_util::{stream::FuturesUnordered, StreamExt};
|
use futures_util::{stream::FuturesUnordered, StreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
@@ -18,15 +19,9 @@ use ruma::{
|
|||||||
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
use super::SESSION_ID_LENGTH;
|
use super::SESSION_ID_LENGTH;
|
||||||
use crate::{
|
use crate::{service::Services, Ruma};
|
||||||
service::user_is_local,
|
|
||||||
services,
|
|
||||||
utils::{self},
|
|
||||||
Error, Result, Ruma,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/keys/upload`
|
/// # `POST /_matrix/client/r0/keys/upload`
|
||||||
///
|
///
|
||||||
@@ -35,12 +30,14 @@ use crate::{
|
|||||||
/// - Adds one time keys
|
/// - Adds one time keys
|
||||||
/// - If there are no device keys yet: Adds device keys (TODO: merge with
|
/// - If there are no device keys yet: Adds device keys (TODO: merge with
|
||||||
/// existing keys?)
|
/// existing keys?)
|
||||||
pub(crate) async fn upload_keys_route(body: Ruma<upload_keys::v3::Request>) -> Result<upload_keys::v3::Response> {
|
pub(crate) async fn upload_keys_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<upload_keys::v3::Request>,
|
||||||
|
) -> Result<upload_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
for (key_key, key_value) in &body.one_time_keys {
|
for (key_key, key_value) in &body.one_time_keys {
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.add_one_time_key(sender_user, sender_device, key_key, key_value)?;
|
.add_one_time_key(sender_user, sender_device, key_key, key_value)?;
|
||||||
}
|
}
|
||||||
@@ -48,19 +45,19 @@ pub(crate) async fn upload_keys_route(body: Ruma<upload_keys::v3::Request>) -> R
|
|||||||
if let Some(device_keys) = &body.device_keys {
|
if let Some(device_keys) = &body.device_keys {
|
||||||
// TODO: merge this and the existing event?
|
// TODO: merge this and the existing event?
|
||||||
// This check is needed to assure that signatures are kept
|
// This check is needed to assure that signatures are kept
|
||||||
if services()
|
if services
|
||||||
.users
|
.users
|
||||||
.get_device_keys(sender_user, sender_device)?
|
.get_device_keys(sender_user, sender_device)?
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.add_device_keys(sender_user, sender_device, device_keys)?;
|
.add_device_keys(sender_user, sender_device, device_keys)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(upload_keys::v3::Response {
|
Ok(upload_keys::v3::Response {
|
||||||
one_time_key_counts: services()
|
one_time_key_counts: services
|
||||||
.users
|
.users
|
||||||
.count_one_time_keys(sender_user, sender_device)?,
|
.count_one_time_keys(sender_user, sender_device)?,
|
||||||
})
|
})
|
||||||
@@ -74,10 +71,13 @@ pub(crate) async fn upload_keys_route(body: Ruma<upload_keys::v3::Request>) -> R
|
|||||||
/// - Gets master keys, self-signing keys, user signing keys and device keys.
|
/// - Gets master keys, self-signing keys, user signing keys and device keys.
|
||||||
/// - The master and self-signing keys contain signatures that the user is
|
/// - The master and self-signing keys contain signatures that the user is
|
||||||
/// allowed to see
|
/// allowed to see
|
||||||
pub(crate) async fn get_keys_route(body: Ruma<get_keys::v3::Request>) -> Result<get_keys::v3::Response> {
|
pub(crate) async fn get_keys_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_keys::v3::Request>,
|
||||||
|
) -> Result<get_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
get_keys_helper(
|
get_keys_helper(
|
||||||
|
&services,
|
||||||
Some(sender_user),
|
Some(sender_user),
|
||||||
&body.device_keys,
|
&body.device_keys,
|
||||||
|u| u == sender_user,
|
|u| u == sender_user,
|
||||||
@@ -89,8 +89,10 @@ pub(crate) async fn get_keys_route(body: Ruma<get_keys::v3::Request>) -> Result<
|
|||||||
/// # `POST /_matrix/client/r0/keys/claim`
|
/// # `POST /_matrix/client/r0/keys/claim`
|
||||||
///
|
///
|
||||||
/// Claims one-time keys
|
/// Claims one-time keys
|
||||||
pub(crate) async fn claim_keys_route(body: Ruma<claim_keys::v3::Request>) -> Result<claim_keys::v3::Response> {
|
pub(crate) async fn claim_keys_route(
|
||||||
claim_keys_helper(&body.one_time_keys).await
|
State(services): State<crate::State>, body: Ruma<claim_keys::v3::Request>,
|
||||||
|
) -> Result<claim_keys::v3::Response> {
|
||||||
|
claim_keys_helper(&services, &body.one_time_keys).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/keys/device_signing/upload`
|
/// # `POST /_matrix/client/r0/keys/device_signing/upload`
|
||||||
@@ -99,7 +101,7 @@ pub(crate) async fn claim_keys_route(body: Ruma<claim_keys::v3::Request>) -> Res
|
|||||||
///
|
///
|
||||||
/// - Requires UIAA to verify password
|
/// - Requires UIAA to verify password
|
||||||
pub(crate) async fn upload_signing_keys_route(
|
pub(crate) async fn upload_signing_keys_route(
|
||||||
body: Ruma<upload_signing_keys::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<upload_signing_keys::v3::Request>,
|
||||||
) -> Result<upload_signing_keys::v3::Response> {
|
) -> Result<upload_signing_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
@@ -116,7 +118,7 @@ pub(crate) async fn upload_signing_keys_route(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(auth) = &body.auth {
|
if let Some(auth) = &body.auth {
|
||||||
let (worked, uiaainfo) = services()
|
let (worked, uiaainfo) = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||||
if !worked {
|
if !worked {
|
||||||
@@ -125,7 +127,7 @@ pub(crate) async fn upload_signing_keys_route(
|
|||||||
// Success!
|
// Success!
|
||||||
} else if let Some(json) = body.json_body {
|
} else if let Some(json) = body.json_body {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
services()
|
services
|
||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err(Error::Uiaa(uiaainfo));
|
||||||
@@ -134,7 +136,7 @@ pub(crate) async fn upload_signing_keys_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(master_key) = &body.master_key {
|
if let Some(master_key) = &body.master_key {
|
||||||
services().users.add_cross_signing_keys(
|
services.users.add_cross_signing_keys(
|
||||||
sender_user,
|
sender_user,
|
||||||
master_key,
|
master_key,
|
||||||
&body.self_signing_key,
|
&body.self_signing_key,
|
||||||
@@ -150,7 +152,7 @@ pub(crate) async fn upload_signing_keys_route(
|
|||||||
///
|
///
|
||||||
/// Uploads end-to-end key signatures from the sender user.
|
/// Uploads end-to-end key signatures from the sender user.
|
||||||
pub(crate) async fn upload_signatures_route(
|
pub(crate) async fn upload_signatures_route(
|
||||||
body: Ruma<upload_signatures::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<upload_signatures::v3::Request>,
|
||||||
) -> Result<upload_signatures::v3::Response> {
|
) -> Result<upload_signatures::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -177,7 +179,7 @@ pub(crate) async fn upload_signatures_route(
|
|||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid signature value."))?
|
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid signature value."))?
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
);
|
);
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.sign_key(user_id, key_id, signature, sender_user)?;
|
.sign_key(user_id, key_id, signature, sender_user)?;
|
||||||
}
|
}
|
||||||
@@ -196,14 +198,14 @@ pub(crate) async fn upload_signatures_route(
|
|||||||
///
|
///
|
||||||
/// - TODO: left users
|
/// - TODO: left users
|
||||||
pub(crate) async fn get_key_changes_route(
|
pub(crate) async fn get_key_changes_route(
|
||||||
body: Ruma<get_key_changes::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_key_changes::v3::Request>,
|
||||||
) -> Result<get_key_changes::v3::Response> {
|
) -> Result<get_key_changes::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let mut device_list_updates = HashSet::new();
|
let mut device_list_updates = HashSet::new();
|
||||||
|
|
||||||
device_list_updates.extend(
|
device_list_updates.extend(
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.keys_changed(
|
.keys_changed(
|
||||||
sender_user.as_str(),
|
sender_user.as_str(),
|
||||||
@@ -219,14 +221,14 @@ pub(crate) async fn get_key_changes_route(
|
|||||||
.filter_map(Result::ok),
|
.filter_map(Result::ok),
|
||||||
);
|
);
|
||||||
|
|
||||||
for room_id in services()
|
for room_id in services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(sender_user)
|
.rooms_joined(sender_user)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
{
|
{
|
||||||
device_list_updates.extend(
|
device_list_updates.extend(
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.keys_changed(
|
.keys_changed(
|
||||||
room_id.as_ref(),
|
room_id.as_ref(),
|
||||||
@@ -249,8 +251,8 @@ pub(crate) async fn get_key_changes_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||||
sender_user: Option<&UserId>, device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>, allowed_signatures: F,
|
services: &Services, sender_user: Option<&UserId>, device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>,
|
||||||
include_display_names: bool,
|
allowed_signatures: F, include_display_names: bool,
|
||||||
) -> Result<get_keys::v3::Response> {
|
) -> Result<get_keys::v3::Response> {
|
||||||
let mut master_keys = BTreeMap::new();
|
let mut master_keys = BTreeMap::new();
|
||||||
let mut self_signing_keys = BTreeMap::new();
|
let mut self_signing_keys = BTreeMap::new();
|
||||||
@@ -262,7 +264,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
for (user_id, device_ids) in device_keys_input {
|
for (user_id, device_ids) in device_keys_input {
|
||||||
let user_id: &UserId = user_id;
|
let user_id: &UserId = user_id;
|
||||||
|
|
||||||
if !user_is_local(user_id) {
|
if !services.globals.user_is_local(user_id) {
|
||||||
get_over_federation
|
get_over_federation
|
||||||
.entry(user_id.server_name())
|
.entry(user_id.server_name())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
@@ -272,10 +274,10 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
|
|
||||||
if device_ids.is_empty() {
|
if device_ids.is_empty() {
|
||||||
let mut container = BTreeMap::new();
|
let mut container = BTreeMap::new();
|
||||||
for device_id in services().users.all_device_ids(user_id) {
|
for device_id in services.users.all_device_ids(user_id) {
|
||||||
let device_id = device_id?;
|
let device_id = device_id?;
|
||||||
if let Some(mut keys) = services().users.get_device_keys(user_id, &device_id)? {
|
if let Some(mut keys) = services.users.get_device_keys(user_id, &device_id)? {
|
||||||
let metadata = services()
|
let metadata = services
|
||||||
.users
|
.users
|
||||||
.get_device_metadata(user_id, &device_id)?
|
.get_device_metadata(user_id, &device_id)?
|
||||||
.ok_or_else(|| Error::bad_database("all_device_keys contained nonexistent device."))?;
|
.ok_or_else(|| Error::bad_database("all_device_keys contained nonexistent device."))?;
|
||||||
@@ -290,8 +292,8 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
} else {
|
} else {
|
||||||
for device_id in device_ids {
|
for device_id in device_ids {
|
||||||
let mut container = BTreeMap::new();
|
let mut container = BTreeMap::new();
|
||||||
if let Some(mut keys) = services().users.get_device_keys(user_id, device_id)? {
|
if let Some(mut keys) = services.users.get_device_keys(user_id, device_id)? {
|
||||||
let metadata = services()
|
let metadata = services
|
||||||
.users
|
.users
|
||||||
.get_device_metadata(user_id, device_id)?
|
.get_device_metadata(user_id, device_id)?
|
||||||
.ok_or(Error::BadRequest(
|
.ok_or(Error::BadRequest(
|
||||||
@@ -307,21 +309,21 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(master_key) = services()
|
if let Some(master_key) = services
|
||||||
.users
|
.users
|
||||||
.get_master_key(sender_user, user_id, &allowed_signatures)?
|
.get_master_key(sender_user, user_id, &allowed_signatures)?
|
||||||
{
|
{
|
||||||
master_keys.insert(user_id.to_owned(), master_key);
|
master_keys.insert(user_id.to_owned(), master_key);
|
||||||
}
|
}
|
||||||
if let Some(self_signing_key) =
|
if let Some(self_signing_key) =
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.get_self_signing_key(sender_user, user_id, &allowed_signatures)?
|
.get_self_signing_key(sender_user, user_id, &allowed_signatures)?
|
||||||
{
|
{
|
||||||
self_signing_keys.insert(user_id.to_owned(), self_signing_key);
|
self_signing_keys.insert(user_id.to_owned(), self_signing_key);
|
||||||
}
|
}
|
||||||
if Some(user_id) == sender_user {
|
if Some(user_id) == sender_user {
|
||||||
if let Some(user_signing_key) = services().users.get_user_signing_key(user_id)? {
|
if let Some(user_signing_key) = services.users.get_user_signing_key(user_id)? {
|
||||||
user_signing_keys.insert(user_id.to_owned(), user_signing_key);
|
user_signing_keys.insert(user_id.to_owned(), user_signing_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,7 +332,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
let mut failures = BTreeMap::new();
|
let mut failures = BTreeMap::new();
|
||||||
|
|
||||||
let back_off = |id| async {
|
let back_off = |id| async {
|
||||||
match services()
|
match services
|
||||||
.globals
|
.globals
|
||||||
.bad_query_ratelimiter
|
.bad_query_ratelimiter
|
||||||
.write()
|
.write()
|
||||||
@@ -349,7 +351,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
let mut futures: FuturesUnordered<_> = get_over_federation
|
let mut futures: FuturesUnordered<_> = get_over_federation
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(server, vec)| async move {
|
.map(|(server, vec)| async move {
|
||||||
if let Some((time, tries)) = services()
|
if let Some((time, tries)) = services
|
||||||
.globals
|
.globals
|
||||||
.bad_query_ratelimiter
|
.bad_query_ratelimiter
|
||||||
.read()
|
.read()
|
||||||
@@ -357,12 +359,10 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
.get(server)
|
.get(server)
|
||||||
{
|
{
|
||||||
// Exponential backoff
|
// Exponential backoff
|
||||||
const MAX_DURATION: Duration = Duration::from_secs(60 * 60 * 24);
|
const MIN: u64 = 5 * 60;
|
||||||
let min_elapsed_duration = cmp::min(MAX_DURATION, Duration::from_secs(5 * 60) * (*tries) * (*tries));
|
const MAX: u64 = 60 * 60 * 24;
|
||||||
|
if continue_exponential_backoff_secs(MIN, MAX, time.elapsed(), *tries) {
|
||||||
if time.elapsed() < min_elapsed_duration {
|
return (server, Err!(BadServerResponse("bad query from {server:?}, still backing off")));
|
||||||
debug!("Backing off query from {:?}", server);
|
|
||||||
return (server, Err(Error::BadServerResponse("bad query, still backing off")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +374,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
let request = federation::keys::get_keys::v1::Request {
|
let request = federation::keys::get_keys::v1::Request {
|
||||||
device_keys: device_keys_input_fed,
|
device_keys: device_keys_input_fed,
|
||||||
};
|
};
|
||||||
let response = services()
|
let response = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(server, request)
|
.send_federation_request(server, request)
|
||||||
.await;
|
.await;
|
||||||
@@ -386,19 +386,19 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
while let Some((server, response)) = futures.next().await {
|
while let Some((server, response)) = futures.next().await {
|
||||||
if let Ok(Ok(response)) = response {
|
if let Ok(Ok(response)) = response {
|
||||||
for (user, masterkey) in response.master_keys {
|
for (user, masterkey) in response.master_keys {
|
||||||
let (master_key_id, mut master_key) = services().users.parse_master_key(&user, &masterkey)?;
|
let (master_key_id, mut master_key) = services.users.parse_master_key(&user, &masterkey)?;
|
||||||
|
|
||||||
if let Some(our_master_key) =
|
if let Some(our_master_key) =
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.get_key(&master_key_id, sender_user, &user, &allowed_signatures)?
|
.get_key(&master_key_id, sender_user, &user, &allowed_signatures)?
|
||||||
{
|
{
|
||||||
let (_, our_master_key) = services().users.parse_master_key(&user, &our_master_key)?;
|
let (_, our_master_key) = services.users.parse_master_key(&user, &our_master_key)?;
|
||||||
master_key.signatures.extend(our_master_key.signatures);
|
master_key.signatures.extend(our_master_key.signatures);
|
||||||
}
|
}
|
||||||
let json = serde_json::to_value(master_key).expect("to_value always works");
|
let json = serde_json::to_value(master_key).expect("to_value always works");
|
||||||
let raw = serde_json::from_value(json).expect("Raw::from_value always works");
|
let raw = serde_json::from_value(json).expect("Raw::from_value always works");
|
||||||
services().users.add_cross_signing_keys(
|
services.users.add_cross_signing_keys(
|
||||||
&user, &raw, &None, &None,
|
&user, &raw, &None, &None,
|
||||||
false, /* Dont notify. A notification would trigger another key request resulting in an
|
false, /* Dont notify. A notification would trigger another key request resulting in an
|
||||||
* endless loop */
|
* endless loop */
|
||||||
@@ -449,14 +449,14 @@ fn add_unsigned_device_display_name(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn claim_keys_helper(
|
pub(crate) async fn claim_keys_helper(
|
||||||
one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, DeviceKeyAlgorithm>>,
|
services: &Services, one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, DeviceKeyAlgorithm>>,
|
||||||
) -> Result<claim_keys::v3::Response> {
|
) -> Result<claim_keys::v3::Response> {
|
||||||
let mut one_time_keys = BTreeMap::new();
|
let mut one_time_keys = BTreeMap::new();
|
||||||
|
|
||||||
let mut get_over_federation = BTreeMap::new();
|
let mut get_over_federation = BTreeMap::new();
|
||||||
|
|
||||||
for (user_id, map) in one_time_keys_input {
|
for (user_id, map) in one_time_keys_input {
|
||||||
if !user_is_local(user_id) {
|
if !services.globals.user_is_local(user_id) {
|
||||||
get_over_federation
|
get_over_federation
|
||||||
.entry(user_id.server_name())
|
.entry(user_id.server_name())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
@@ -465,7 +465,7 @@ pub(crate) async fn claim_keys_helper(
|
|||||||
|
|
||||||
let mut container = BTreeMap::new();
|
let mut container = BTreeMap::new();
|
||||||
for (device_id, key_algorithm) in map {
|
for (device_id, key_algorithm) in map {
|
||||||
if let Some(one_time_keys) = services()
|
if let Some(one_time_keys) = services
|
||||||
.users
|
.users
|
||||||
.take_one_time_key(user_id, device_id, key_algorithm)?
|
.take_one_time_key(user_id, device_id, key_algorithm)?
|
||||||
{
|
{
|
||||||
@@ -488,7 +488,7 @@ pub(crate) async fn claim_keys_helper(
|
|||||||
}
|
}
|
||||||
(
|
(
|
||||||
server,
|
server,
|
||||||
services()
|
services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
server,
|
server,
|
||||||
|
|||||||
+103
-310
@@ -1,10 +1,18 @@
|
|||||||
#![allow(deprecated)]
|
#![allow(deprecated)]
|
||||||
|
|
||||||
use std::{io::Cursor, sync::Arc, time::Duration};
|
use std::time::Duration;
|
||||||
|
|
||||||
use image::io::Reader as ImgReader;
|
use axum::extract::State;
|
||||||
use ipaddress::IPAddress;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use reqwest::Url;
|
use conduit::{
|
||||||
|
debug_warn, err, error,
|
||||||
|
utils::{
|
||||||
|
self,
|
||||||
|
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
|
||||||
|
math::ruma_from_usize,
|
||||||
|
},
|
||||||
|
warn, Err, Error, Result,
|
||||||
|
};
|
||||||
use ruma::api::client::{
|
use ruma::api::client::{
|
||||||
error::{ErrorKind, RetryAfter},
|
error::{ErrorKind, RetryAfter},
|
||||||
media::{
|
media::{
|
||||||
@@ -12,25 +20,12 @@ use ruma::api::client::{
|
|||||||
get_media_preview,
|
get_media_preview,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tracing::{debug, error, warn};
|
use service::{
|
||||||
use webpage::HTML;
|
media::{FileMeta, MXC_LENGTH},
|
||||||
|
Services,
|
||||||
use crate::{
|
|
||||||
debug_warn,
|
|
||||||
service::{
|
|
||||||
media::{FileMeta, UrlPreviewData},
|
|
||||||
server_is_ours,
|
|
||||||
},
|
|
||||||
services,
|
|
||||||
utils::{
|
|
||||||
self,
|
|
||||||
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
|
|
||||||
},
|
|
||||||
Error, Result, Ruma, RumaResponse,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// generated MXC ID (`media-id`) length
|
use crate::{Ruma, RumaResponse};
|
||||||
const MXC_LENGTH: usize = 32;
|
|
||||||
|
|
||||||
/// Cache control for immutable objects
|
/// Cache control for immutable objects
|
||||||
const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
|
const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
|
||||||
@@ -41,10 +36,10 @@ const CORP_CROSS_ORIGIN: &str = "cross-origin";
|
|||||||
///
|
///
|
||||||
/// Returns max upload size.
|
/// Returns max upload size.
|
||||||
pub(crate) async fn get_media_config_route(
|
pub(crate) async fn get_media_config_route(
|
||||||
_body: Ruma<get_media_config::v3::Request>,
|
State(services): State<crate::State>, _body: Ruma<get_media_config::v3::Request>,
|
||||||
) -> Result<get_media_config::v3::Response> {
|
) -> Result<get_media_config::v3::Response> {
|
||||||
Ok(get_media_config::v3::Response {
|
Ok(get_media_config::v3::Response {
|
||||||
upload_size: services().globals.max_request_size().into(),
|
upload_size: ruma_from_usize(services.globals.config.max_request_size),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,26 +51,34 @@ pub(crate) async fn get_media_config_route(
|
|||||||
///
|
///
|
||||||
/// Returns max upload size.
|
/// Returns max upload size.
|
||||||
pub(crate) async fn get_media_config_v1_route(
|
pub(crate) async fn get_media_config_v1_route(
|
||||||
body: Ruma<get_media_config::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_media_config::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_media_config::v3::Response>> {
|
) -> Result<RumaResponse<get_media_config::v3::Response>> {
|
||||||
get_media_config_route(body).await.map(RumaResponse)
|
get_media_config_route(State(services), body)
|
||||||
|
.await
|
||||||
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/media/v3/preview_url`
|
/// # `GET /_matrix/media/v3/preview_url`
|
||||||
///
|
///
|
||||||
/// Returns URL preview.
|
/// Returns URL preview.
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "url_preview")]
|
||||||
pub(crate) async fn get_media_preview_route(
|
pub(crate) async fn get_media_preview_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_media_preview::v3::Request>,
|
body: Ruma<get_media_preview::v3::Request>,
|
||||||
) -> Result<get_media_preview::v3::Response> {
|
) -> Result<get_media_preview::v3::Response> {
|
||||||
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let url = &body.url;
|
let url = &body.url;
|
||||||
if !url_preview_allowed(url) {
|
if !services.media.url_preview_allowed(url) {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "URL is not allowed to be previewed"));
|
return Err!(Request(Forbidden(
|
||||||
|
warn!(%sender_user, %url, "URL is not allowed to be previewed")
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
match get_url_preview(url).await {
|
match services.media.get_url_preview(url).await {
|
||||||
Ok(preview) => {
|
Ok(preview) => {
|
||||||
let res = serde_json::value::to_raw_value(&preview).map_err(|e| {
|
let res = serde_json::value::to_raw_value(&preview).map_err(|e| {
|
||||||
error!("Failed to convert UrlPreviewData into a serde json value: {}", e);
|
error!(%sender_user, "Failed to convert UrlPreviewData into a serde json value: {e}");
|
||||||
Error::BadRequest(
|
Error::BadRequest(
|
||||||
ErrorKind::LimitExceeded {
|
ErrorKind::LimitExceeded {
|
||||||
retry_after: Some(RetryAfter::Delay(Duration::from_secs(5))),
|
retry_after: Some(RetryAfter::Delay(Duration::from_secs(5))),
|
||||||
@@ -87,8 +90,7 @@ pub(crate) async fn get_media_preview_route(
|
|||||||
Ok(get_media_preview::v3::Response::from_raw_value(res))
|
Ok(get_media_preview::v3::Response::from_raw_value(res))
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to generate a URL preview: {e}");
|
warn!(%sender_user, "Failed to generate a URL preview: {e}");
|
||||||
|
|
||||||
// there doesn't seem to be an agreed-upon error code in the spec.
|
// there doesn't seem to be an agreed-upon error code in the spec.
|
||||||
// the only response codes in the preview_url spec page are 200 and 429.
|
// the only response codes in the preview_url spec page are 200 and 429.
|
||||||
Err(Error::BadRequest(
|
Err(Error::BadRequest(
|
||||||
@@ -108,10 +110,14 @@ pub(crate) async fn get_media_preview_route(
|
|||||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||||
///
|
///
|
||||||
/// Returns URL preview.
|
/// Returns URL preview.
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "url_preview")]
|
||||||
pub(crate) async fn get_media_preview_v1_route(
|
pub(crate) async fn get_media_preview_v1_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_media_preview::v3::Request>,
|
body: Ruma<get_media_preview::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
|
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
|
||||||
get_media_preview_route(body).await.map(RumaResponse)
|
get_media_preview_route(State(services), InsecureClientIp(client), body)
|
||||||
|
.await
|
||||||
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `POST /_matrix/media/v3/upload`
|
/// # `POST /_matrix/media/v3/upload`
|
||||||
@@ -120,18 +126,16 @@ pub(crate) async fn get_media_preview_v1_route(
|
|||||||
///
|
///
|
||||||
/// - Some metadata will be saved in the database
|
/// - Some metadata will be saved in the database
|
||||||
/// - Media will be saved in the media/ directory
|
/// - Media will be saved in the media/ directory
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "media_upload")]
|
||||||
pub(crate) async fn create_content_route(
|
pub(crate) async fn create_content_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<create_content::v3::Request>,
|
body: Ruma<create_content::v3::Request>,
|
||||||
) -> Result<create_content::v3::Response> {
|
) -> Result<create_content::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let mxc = format!(
|
let mxc = format!("mxc://{}/{}", services.globals.server_name(), utils::random_string(MXC_LENGTH));
|
||||||
"mxc://{}/{}",
|
|
||||||
services().globals.server_name(),
|
|
||||||
utils::random_string(MXC_LENGTH)
|
|
||||||
);
|
|
||||||
|
|
||||||
services()
|
services
|
||||||
.media
|
.media
|
||||||
.create(
|
.create(
|
||||||
Some(sender_user.clone()),
|
Some(sender_user.clone()),
|
||||||
@@ -167,10 +171,14 @@ pub(crate) async fn create_content_route(
|
|||||||
///
|
///
|
||||||
/// - Some metadata will be saved in the database
|
/// - Some metadata will be saved in the database
|
||||||
/// - Media will be saved in the media/ directory
|
/// - Media will be saved in the media/ directory
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "media_upload")]
|
||||||
pub(crate) async fn create_content_v1_route(
|
pub(crate) async fn create_content_v1_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<create_content::v3::Request>,
|
body: Ruma<create_content::v3::Request>,
|
||||||
) -> Result<RumaResponse<create_content::v3::Response>> {
|
) -> Result<RumaResponse<create_content::v3::Response>> {
|
||||||
create_content_route(body).await.map(RumaResponse)
|
create_content_route(State(services), InsecureClientIp(client), body)
|
||||||
|
.await
|
||||||
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}`
|
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}`
|
||||||
@@ -181,16 +189,21 @@ pub(crate) async fn create_content_v1_route(
|
|||||||
/// - Only redirects if `allow_redirect` is true
|
/// - Only redirects if `allow_redirect` is true
|
||||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||||
/// seconds
|
/// seconds
|
||||||
pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> Result<get_content::v3::Response> {
|
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||||
|
pub(crate) async fn get_content_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_content::v3::Request>,
|
||||||
|
) -> Result<get_content::v3::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||||
|
|
||||||
if let Some(FileMeta {
|
if let Some(FileMeta {
|
||||||
|
content,
|
||||||
content_type,
|
content_type,
|
||||||
file,
|
|
||||||
content_disposition,
|
content_disposition,
|
||||||
}) = services().media.get(&mxc).await?
|
}) = services.media.get(&mxc).await?
|
||||||
{
|
{
|
||||||
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
|
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
|
||||||
|
let file = content.expect("content");
|
||||||
|
|
||||||
Ok(get_content::v3::Response {
|
Ok(get_content::v3::Response {
|
||||||
file,
|
file,
|
||||||
@@ -199,8 +212,9 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
|
|||||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||||
})
|
})
|
||||||
} else if !server_is_ours(&body.server_name) && body.allow_remote {
|
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
|
||||||
let response = get_remote_content(
|
let response = get_remote_content(
|
||||||
|
&services,
|
||||||
&mxc,
|
&mxc,
|
||||||
&body.server_name,
|
&body.server_name,
|
||||||
body.media_id.clone(),
|
body.media_id.clone(),
|
||||||
@@ -208,10 +222,7 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
|
|||||||
body.timeout_ms,
|
body.timeout_ms,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| err!(Request(NotFound(debug_warn!("Fetching media `{mxc}` failed: {e:?}")))))?;
|
||||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
|
||||||
Error::BadRequest(ErrorKind::NotFound, "Remote media error.")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let content_disposition = Some(make_content_disposition(
|
let content_disposition = Some(make_content_disposition(
|
||||||
&response.content_type,
|
&response.content_type,
|
||||||
@@ -227,7 +238,7 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
|
|||||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
|
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
Err!(Request(NotFound("Media not found.")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,10 +254,14 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
|
|||||||
/// - Only redirects if `allow_redirect` is true
|
/// - Only redirects if `allow_redirect` is true
|
||||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||||
/// seconds
|
/// seconds
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||||
pub(crate) async fn get_content_v1_route(
|
pub(crate) async fn get_content_v1_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content::v3::Request>,
|
body: Ruma<get_content::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_content::v3::Response>> {
|
) -> Result<RumaResponse<get_content::v3::Response>> {
|
||||||
get_content_route(body).await.map(RumaResponse)
|
get_content_route(State(services), InsecureClientIp(client), body)
|
||||||
|
.await
|
||||||
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}`
|
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}`
|
||||||
@@ -257,16 +272,18 @@ pub(crate) async fn get_content_v1_route(
|
|||||||
/// - Only redirects if `allow_redirect` is true
|
/// - Only redirects if `allow_redirect` is true
|
||||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||||
/// seconds
|
/// seconds
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||||
pub(crate) async fn get_content_as_filename_route(
|
pub(crate) async fn get_content_as_filename_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_as_filename::v3::Request>,
|
body: Ruma<get_content_as_filename::v3::Request>,
|
||||||
) -> Result<get_content_as_filename::v3::Response> {
|
) -> Result<get_content_as_filename::v3::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||||
|
|
||||||
if let Some(FileMeta {
|
if let Some(FileMeta {
|
||||||
|
content,
|
||||||
content_type,
|
content_type,
|
||||||
file,
|
|
||||||
content_disposition,
|
content_disposition,
|
||||||
}) = services().media.get(&mxc).await?
|
}) = services.media.get(&mxc).await?
|
||||||
{
|
{
|
||||||
let content_disposition = Some(make_content_disposition(
|
let content_disposition = Some(make_content_disposition(
|
||||||
&content_type,
|
&content_type,
|
||||||
@@ -274,6 +291,7 @@ pub(crate) async fn get_content_as_filename_route(
|
|||||||
Some(body.filename.clone()),
|
Some(body.filename.clone()),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let file = content.expect("content");
|
||||||
Ok(get_content_as_filename::v3::Response {
|
Ok(get_content_as_filename::v3::Response {
|
||||||
file,
|
file,
|
||||||
content_type,
|
content_type,
|
||||||
@@ -281,8 +299,9 @@ pub(crate) async fn get_content_as_filename_route(
|
|||||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||||
})
|
})
|
||||||
} else if !server_is_ours(&body.server_name) && body.allow_remote {
|
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
|
||||||
match get_remote_content(
|
match get_remote_content(
|
||||||
|
&services,
|
||||||
&mxc,
|
&mxc,
|
||||||
&body.server_name,
|
&body.server_name,
|
||||||
body.media_id.clone(),
|
body.media_id.clone(),
|
||||||
@@ -306,13 +325,10 @@ pub(crate) async fn get_content_as_filename_route(
|
|||||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => Err!(Request(NotFound(debug_warn!("Fetching media `{mxc}` failed: {e:?}")))),
|
||||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
|
||||||
Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error."))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
Err!(Request(NotFound("Media not found.")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,10 +344,14 @@ pub(crate) async fn get_content_as_filename_route(
|
|||||||
/// - Only redirects if `allow_redirect` is true
|
/// - Only redirects if `allow_redirect` is true
|
||||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||||
/// seconds
|
/// seconds
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||||
pub(crate) async fn get_content_as_filename_v1_route(
|
pub(crate) async fn get_content_as_filename_v1_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_as_filename::v3::Request>,
|
body: Ruma<get_content_as_filename::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
||||||
get_content_as_filename_route(body).await.map(RumaResponse)
|
get_content_as_filename_route(State(services), InsecureClientIp(client), body)
|
||||||
|
.await
|
||||||
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/media/v3/thumbnail/{serverName}/{mediaId}`
|
/// # `GET /_matrix/media/v3/thumbnail/{serverName}/{mediaId}`
|
||||||
@@ -342,29 +362,32 @@ pub(crate) async fn get_content_as_filename_v1_route(
|
|||||||
/// - Only redirects if `allow_redirect` is true
|
/// - Only redirects if `allow_redirect` is true
|
||||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||||
/// seconds
|
/// seconds
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
||||||
pub(crate) async fn get_content_thumbnail_route(
|
pub(crate) async fn get_content_thumbnail_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||||
) -> Result<get_content_thumbnail::v3::Response> {
|
) -> Result<get_content_thumbnail::v3::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||||
|
|
||||||
if let Some(FileMeta {
|
if let Some(FileMeta {
|
||||||
|
content,
|
||||||
content_type,
|
content_type,
|
||||||
file,
|
|
||||||
content_disposition,
|
content_disposition,
|
||||||
}) = services()
|
}) = services
|
||||||
.media
|
.media
|
||||||
.get_thumbnail(
|
.get_thumbnail(
|
||||||
&mxc,
|
&mxc,
|
||||||
body.width
|
body.width
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
.map_err(|e| err!(Request(InvalidParam("Width is invalid: {e:?}"))))?,
|
||||||
body.height
|
body.height
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Height is invalid."))?,
|
.map_err(|e| err!(Request(InvalidParam("Height is invalid: {e:?}"))))?,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
|
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
|
||||||
|
let file = content.expect("content");
|
||||||
|
|
||||||
Ok(get_content_thumbnail::v3::Response {
|
Ok(get_content_thumbnail::v3::Response {
|
||||||
file,
|
file,
|
||||||
@@ -373,8 +396,8 @@ pub(crate) async fn get_content_thumbnail_route(
|
|||||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||||
content_disposition,
|
content_disposition,
|
||||||
})
|
})
|
||||||
} else if !server_is_ours(&body.server_name) && body.allow_remote {
|
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.prevent_media_downloads_from()
|
.prevent_media_downloads_from()
|
||||||
.contains(&body.server_name)
|
.contains(&body.server_name)
|
||||||
@@ -382,10 +405,10 @@ pub(crate) async fn get_content_thumbnail_route(
|
|||||||
// we'll lie to the client and say the blocked server's media was not found and
|
// we'll lie to the client and say the blocked server's media was not found and
|
||||||
// log. the client has no way of telling anyways so this is a security bonus.
|
// log. the client has no way of telling anyways so this is a security bonus.
|
||||||
debug_warn!("Received request for media `{}` on blocklisted server", mxc);
|
debug_warn!("Received request for media `{}` on blocklisted server", mxc);
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
return Err!(Request(NotFound("Media not found.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
match services()
|
match services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
&body.server_name,
|
&body.server_name,
|
||||||
@@ -404,7 +427,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(get_thumbnail_response) => {
|
Ok(get_thumbnail_response) => {
|
||||||
services()
|
services
|
||||||
.media
|
.media
|
||||||
.upload_thumbnail(
|
.upload_thumbnail(
|
||||||
None,
|
None,
|
||||||
@@ -431,13 +454,10 @@ pub(crate) async fn get_content_thumbnail_route(
|
|||||||
content_disposition,
|
content_disposition,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => Err!(Request(NotFound(debug_warn!("Fetching media `{mxc}` failed: {e:?}")))),
|
||||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
|
||||||
Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error."))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
Err!(Request(NotFound("Media not found.")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,16 +473,21 @@ pub(crate) async fn get_content_thumbnail_route(
|
|||||||
/// - Only redirects if `allow_redirect` is true
|
/// - Only redirects if `allow_redirect` is true
|
||||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||||
/// seconds
|
/// seconds
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
||||||
pub(crate) async fn get_content_thumbnail_v1_route(
|
pub(crate) async fn get_content_thumbnail_v1_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
||||||
get_content_thumbnail_route(body).await.map(RumaResponse)
|
get_content_thumbnail_route(State(services), InsecureClientIp(client), body)
|
||||||
|
.await
|
||||||
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_remote_content(
|
async fn get_remote_content(
|
||||||
mxc: &str, server_name: &ruma::ServerName, media_id: String, allow_redirect: bool, timeout_ms: Duration,
|
services: &Services, mxc: &str, server_name: &ruma::ServerName, media_id: String, allow_redirect: bool,
|
||||||
|
timeout_ms: Duration,
|
||||||
) -> Result<get_content::v3::Response, Error> {
|
) -> Result<get_content::v3::Response, Error> {
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.prevent_media_downloads_from()
|
.prevent_media_downloads_from()
|
||||||
.contains(&server_name.to_owned())
|
.contains(&server_name.to_owned())
|
||||||
@@ -470,10 +495,10 @@ async fn get_remote_content(
|
|||||||
// we'll lie to the client and say the blocked server's media was not found and
|
// we'll lie to the client and say the blocked server's media was not found and
|
||||||
// log. the client has no way of telling anyways so this is a security bonus.
|
// log. the client has no way of telling anyways so this is a security bonus.
|
||||||
debug_warn!("Received request for media `{mxc}` on blocklisted server");
|
debug_warn!("Received request for media `{mxc}` on blocklisted server");
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
return Err!(Request(NotFound("Media not found.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let content_response = services()
|
let content_response = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
server_name,
|
server_name,
|
||||||
@@ -493,7 +518,7 @@ async fn get_remote_content(
|
|||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
|
|
||||||
services()
|
services
|
||||||
.media
|
.media
|
||||||
.create(
|
.create(
|
||||||
None,
|
None,
|
||||||
@@ -512,235 +537,3 @@ async fn get_remote_content(
|
|||||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
|
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn download_image(client: &reqwest::Client, url: &str) -> Result<UrlPreviewData> {
|
|
||||||
let image = client.get(url).send().await?.bytes().await?;
|
|
||||||
let mxc = format!(
|
|
||||||
"mxc://{}/{}",
|
|
||||||
services().globals.server_name(),
|
|
||||||
utils::random_string(MXC_LENGTH)
|
|
||||||
);
|
|
||||||
|
|
||||||
services()
|
|
||||||
.media
|
|
||||||
.create(None, &mxc, None, None, &image)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let (width, height) = match ImgReader::new(Cursor::new(&image)).with_guessed_format() {
|
|
||||||
Err(_) => (None, None),
|
|
||||||
Ok(reader) => match reader.into_dimensions() {
|
|
||||||
Err(_) => (None, None),
|
|
||||||
Ok((width, height)) => (Some(width), Some(height)),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(UrlPreviewData {
|
|
||||||
image: Some(mxc),
|
|
||||||
image_size: Some(image.len()),
|
|
||||||
image_width: width,
|
|
||||||
image_height: height,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn download_html(client: &reqwest::Client, url: &str) -> Result<UrlPreviewData> {
|
|
||||||
let mut response = client.get(url).send().await?;
|
|
||||||
|
|
||||||
let mut bytes: Vec<u8> = Vec::new();
|
|
||||||
while let Some(chunk) = response.chunk().await? {
|
|
||||||
bytes.extend_from_slice(&chunk);
|
|
||||||
if bytes.len() > services().globals.url_preview_max_spider_size() {
|
|
||||||
debug!(
|
|
||||||
"Response body from URL {} exceeds url_preview_max_spider_size ({}), not processing the rest of the \
|
|
||||||
response body and assuming our necessary data is in this range.",
|
|
||||||
url,
|
|
||||||
services().globals.url_preview_max_spider_size()
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let body = String::from_utf8_lossy(&bytes);
|
|
||||||
let Ok(html) = HTML::from_string(body.to_string(), Some(url.to_owned())) else {
|
|
||||||
return Err(Error::BadRequest(ErrorKind::Unknown, "Failed to parse HTML"));
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut data = match html.opengraph.images.first() {
|
|
||||||
None => UrlPreviewData::default(),
|
|
||||||
Some(obj) => download_image(client, &obj.url).await?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let props = html.opengraph.properties;
|
|
||||||
|
|
||||||
/* use OpenGraph title/description, but fall back to HTML if not available */
|
|
||||||
data.title = props.get("title").cloned().or(html.title);
|
|
||||||
data.description = props.get("description").cloned().or(html.description);
|
|
||||||
|
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn request_url_preview(url: &str) -> Result<UrlPreviewData> {
|
|
||||||
if let Ok(ip) = IPAddress::parse(url) {
|
|
||||||
if !services().globals.valid_cidr_range(&ip) {
|
|
||||||
return Err(Error::BadServerResponse("Requesting from this address is forbidden"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let client = &services().globals.client.url_preview;
|
|
||||||
let response = client.head(url).send().await?;
|
|
||||||
|
|
||||||
if let Some(remote_addr) = response.remote_addr() {
|
|
||||||
if let Ok(ip) = IPAddress::parse(remote_addr.ip().to_string()) {
|
|
||||||
if !services().globals.valid_cidr_range(&ip) {
|
|
||||||
return Err(Error::BadServerResponse("Requesting from this address is forbidden"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(content_type) = response
|
|
||||||
.headers()
|
|
||||||
.get(reqwest::header::CONTENT_TYPE)
|
|
||||||
.and_then(|x| x.to_str().ok())
|
|
||||||
else {
|
|
||||||
return Err(Error::BadRequest(ErrorKind::Unknown, "Unknown Content-Type"));
|
|
||||||
};
|
|
||||||
let data = match content_type {
|
|
||||||
html if html.starts_with("text/html") => download_html(client, url).await?,
|
|
||||||
img if img.starts_with("image/") => download_image(client, url).await?,
|
|
||||||
_ => return Err(Error::BadRequest(ErrorKind::Unknown, "Unsupported Content-Type")),
|
|
||||||
};
|
|
||||||
|
|
||||||
services().media.set_url_preview(url, &data).await?;
|
|
||||||
|
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_url_preview(url: &str) -> Result<UrlPreviewData> {
|
|
||||||
if let Some(preview) = services().media.get_url_preview(url).await {
|
|
||||||
return Ok(preview);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure that only one request is made per URL
|
|
||||||
let mutex_request = Arc::clone(
|
|
||||||
services()
|
|
||||||
.media
|
|
||||||
.url_preview_mutex
|
|
||||||
.write()
|
|
||||||
.await
|
|
||||||
.entry(url.to_owned())
|
|
||||||
.or_default(),
|
|
||||||
);
|
|
||||||
let _request_lock = mutex_request.lock().await;
|
|
||||||
|
|
||||||
match services().media.get_url_preview(url).await {
|
|
||||||
Some(preview) => Ok(preview),
|
|
||||||
None => request_url_preview(url).await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn url_preview_allowed(url_str: &str) -> bool {
|
|
||||||
let url: Url = match Url::parse(url_str) {
|
|
||||||
Ok(u) => u,
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Failed to parse URL from a str: {}", e);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if ["http", "https"]
|
|
||||||
.iter()
|
|
||||||
.all(|&scheme| scheme != url.scheme().to_lowercase())
|
|
||||||
{
|
|
||||||
debug!("Ignoring non-HTTP/HTTPS URL to preview: {}", url);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let host = match url.host_str() {
|
|
||||||
None => {
|
|
||||||
debug!("Ignoring URL preview for a URL that does not have a host (?): {}", url);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
Some(h) => h.to_owned(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let allowlist_domain_contains = services().globals.url_preview_domain_contains_allowlist();
|
|
||||||
let allowlist_domain_explicit = services().globals.url_preview_domain_explicit_allowlist();
|
|
||||||
let denylist_domain_explicit = services().globals.url_preview_domain_explicit_denylist();
|
|
||||||
let allowlist_url_contains = services().globals.url_preview_url_contains_allowlist();
|
|
||||||
|
|
||||||
if allowlist_domain_contains.contains(&"*".to_owned())
|
|
||||||
|| allowlist_domain_explicit.contains(&"*".to_owned())
|
|
||||||
|| allowlist_url_contains.contains(&"*".to_owned())
|
|
||||||
{
|
|
||||||
debug!("Config key contains * which is allowing all URL previews. Allowing URL {}", url);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !host.is_empty() {
|
|
||||||
if denylist_domain_explicit.contains(&host) {
|
|
||||||
debug!(
|
|
||||||
"Host {} is not allowed by url_preview_domain_explicit_denylist (check 1/4)",
|
|
||||||
&host
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if allowlist_domain_explicit.contains(&host) {
|
|
||||||
debug!("Host {} is allowed by url_preview_domain_explicit_allowlist (check 2/4)", &host);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if allowlist_domain_contains
|
|
||||||
.iter()
|
|
||||||
.any(|domain_s| domain_s.contains(&host.clone()))
|
|
||||||
{
|
|
||||||
debug!("Host {} is allowed by url_preview_domain_contains_allowlist (check 3/4)", &host);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if allowlist_url_contains
|
|
||||||
.iter()
|
|
||||||
.any(|url_s| url.to_string().contains(&url_s.to_string()))
|
|
||||||
{
|
|
||||||
debug!("URL {} is allowed by url_preview_url_contains_allowlist (check 4/4)", &host);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check root domain if available and if user has root domain checks
|
|
||||||
if services().globals.url_preview_check_root_domain() {
|
|
||||||
debug!("Checking root domain");
|
|
||||||
match host.split_once('.') {
|
|
||||||
None => return false,
|
|
||||||
Some((_, root_domain)) => {
|
|
||||||
if denylist_domain_explicit.contains(&root_domain.to_owned()) {
|
|
||||||
debug!(
|
|
||||||
"Root domain {} is not allowed by url_preview_domain_explicit_denylist (check 1/3)",
|
|
||||||
&root_domain
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if allowlist_domain_explicit.contains(&root_domain.to_owned()) {
|
|
||||||
debug!(
|
|
||||||
"Root domain {} is allowed by url_preview_domain_explicit_allowlist (check 2/3)",
|
|
||||||
&root_domain
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if allowlist_domain_contains
|
|
||||||
.iter()
|
|
||||||
.any(|domain_s| domain_s.contains(&root_domain.to_owned()))
|
|
||||||
{
|
|
||||||
debug!(
|
|
||||||
"Root domain {} is allowed by url_preview_domain_contains_allowlist (check 3/3)",
|
|
||||||
&root_domain
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|||||||
+270
-282
File diff suppressed because it is too large
Load Diff
+30
-31
@@ -1,5 +1,6 @@
|
|||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use conduit::PduCount;
|
use conduit::PduCount;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
@@ -12,7 +13,10 @@ use ruma::{
|
|||||||
};
|
};
|
||||||
use serde_json::{from_str, Value};
|
use serde_json::{from_str, Value};
|
||||||
|
|
||||||
use crate::{service::pdu::PduBuilder, services, utils, Error, PduEvent, Result, Ruma};
|
use crate::{
|
||||||
|
service::{pdu::PduBuilder, Services},
|
||||||
|
utils, Error, PduEvent, Result, Ruma,
|
||||||
|
};
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`
|
/// # `PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`
|
||||||
///
|
///
|
||||||
@@ -24,25 +28,19 @@ use crate::{service::pdu::PduBuilder, services, utils, Error, PduEvent, Result,
|
|||||||
/// - Tries to send the event into the room, auth rules will determine if it is
|
/// - Tries to send the event into the room, auth rules will determine if it is
|
||||||
/// allowed
|
/// allowed
|
||||||
pub(crate) async fn send_message_event_route(
|
pub(crate) async fn send_message_event_route(
|
||||||
body: Ruma<send_message_event::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<send_message_event::v3::Request>,
|
||||||
) -> Result<send_message_event::v3::Response> {
|
) -> Result<send_message_event::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_deref();
|
let sender_device = body.sender_device.as_deref();
|
||||||
|
|
||||||
let state_lock = services()
|
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||||
.globals
|
|
||||||
.roomid_mutex_state
|
|
||||||
.lock(&body.room_id)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Forbid m.room.encrypted if encryption is disabled
|
// Forbid m.room.encrypted if encryption is disabled
|
||||||
if MessageLikeEventType::RoomEncrypted == body.event_type && !services().globals.allow_encryption() {
|
if MessageLikeEventType::RoomEncrypted == body.event_type && !services.globals.allow_encryption() {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Encryption has been disabled"));
|
return Err(Error::BadRequest(ErrorKind::forbidden(), "Encryption has been disabled"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.event_type == MessageLikeEventType::CallInvite
|
if body.event_type == MessageLikeEventType::CallInvite && services.rooms.directory.is_public_room(&body.room_id)? {
|
||||||
&& services().rooms.directory.is_public_room(&body.room_id)?
|
|
||||||
{
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::forbidden(),
|
ErrorKind::forbidden(),
|
||||||
"Room call invites are not allowed in public rooms",
|
"Room call invites are not allowed in public rooms",
|
||||||
@@ -50,7 +48,7 @@ pub(crate) async fn send_message_event_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a new transaction id
|
// Check if this is a new transaction id
|
||||||
if let Some(response) = services()
|
if let Some(response) = services
|
||||||
.transaction_ids
|
.transaction_ids
|
||||||
.existing_txnid(sender_user, sender_device, &body.txn_id)?
|
.existing_txnid(sender_user, sender_device, &body.txn_id)?
|
||||||
{
|
{
|
||||||
@@ -75,7 +73,7 @@ pub(crate) async fn send_message_event_route(
|
|||||||
let mut unsigned = BTreeMap::new();
|
let mut unsigned = BTreeMap::new();
|
||||||
unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into());
|
unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into());
|
||||||
|
|
||||||
let event_id = services()
|
let event_id = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -93,7 +91,7 @@ pub(crate) async fn send_message_event_route(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
services()
|
services
|
||||||
.transaction_ids
|
.transaction_ids
|
||||||
.add_txnid(sender_user, sender_device, &body.txn_id, event_id.as_bytes())?;
|
.add_txnid(sender_user, sender_device, &body.txn_id, event_id.as_bytes())?;
|
||||||
|
|
||||||
@@ -109,7 +107,7 @@ pub(crate) async fn send_message_event_route(
|
|||||||
/// - Only works if the user is joined (TODO: always allow, but only show events
|
/// - Only works if the user is joined (TODO: always allow, but only show events
|
||||||
/// where the user was joined, depending on `history_visibility`)
|
/// where the user was joined, depending on `history_visibility`)
|
||||||
pub(crate) async fn get_message_events_route(
|
pub(crate) async fn get_message_events_route(
|
||||||
body: Ruma<get_message_events::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_message_events::v3::Request>,
|
||||||
) -> Result<get_message_events::v3::Response> {
|
) -> Result<get_message_events::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
@@ -127,7 +125,7 @@ pub(crate) async fn get_message_events_route(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|t| PduCount::try_from_string(t).ok());
|
.and_then(|t| PduCount::try_from_string(t).ok());
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.lazy_loading
|
.lazy_loading
|
||||||
.lazy_load_confirm_delivery(sender_user, sender_device, &body.room_id, from)
|
.lazy_load_confirm_delivery(sender_user, sender_device, &body.room_id, from)
|
||||||
@@ -143,12 +141,12 @@ pub(crate) async fn get_message_events_route(
|
|||||||
|
|
||||||
match body.dir {
|
match body.dir {
|
||||||
ruma::api::Direction::Forward => {
|
ruma::api::Direction::Forward => {
|
||||||
let events_after: Vec<_> = services()
|
let events_after: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_after(sender_user, &body.room_id, from)?
|
.pdus_after(sender_user, &body.room_id, from)?
|
||||||
.filter_map(Result::ok) // Filter out buggy events
|
.filter_map(Result::ok) // Filter out buggy events
|
||||||
.filter(|(_, pdu)| { contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)
|
.filter(|(_, pdu)| { contains_url_filter(pdu, &body.filter) && visibility_filter(&services, pdu, sender_user, &body.room_id)
|
||||||
|
|
||||||
})
|
})
|
||||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
||||||
@@ -161,7 +159,7 @@ pub(crate) async fn get_message_events_route(
|
|||||||
* https://github.com/vector-im/element-web/issues/21034
|
* https://github.com/vector-im/element-web/issues/21034
|
||||||
*/
|
*/
|
||||||
if !cfg!(feature = "element_hacks")
|
if !cfg!(feature = "element_hacks")
|
||||||
&& !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
&& !services.rooms.lazy_loading.lazy_load_was_sent_before(
|
||||||
sender_user,
|
sender_user,
|
||||||
sender_device,
|
sender_device,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
@@ -185,17 +183,17 @@ pub(crate) async fn get_message_events_route(
|
|||||||
resp.chunk = events_after;
|
resp.chunk = events_after;
|
||||||
},
|
},
|
||||||
ruma::api::Direction::Backward => {
|
ruma::api::Direction::Backward => {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.backfill_if_required(&body.room_id, from)
|
.backfill_if_required(&body.room_id, from)
|
||||||
.await?;
|
.await?;
|
||||||
let events_before: Vec<_> = services()
|
let events_before: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_until(sender_user, &body.room_id, from)?
|
.pdus_until(sender_user, &body.room_id, from)?
|
||||||
.filter_map(Result::ok) // Filter out buggy events
|
.filter_map(Result::ok) // Filter out buggy events
|
||||||
.filter(|(_, pdu)| {contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)})
|
.filter(|(_, pdu)| {contains_url_filter(pdu, &body.filter) && visibility_filter(&services, pdu, sender_user, &body.room_id)})
|
||||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.collect();
|
.collect();
|
||||||
@@ -206,7 +204,7 @@ pub(crate) async fn get_message_events_route(
|
|||||||
* https://github.com/vector-im/element-web/issues/21034
|
* https://github.com/vector-im/element-web/issues/21034
|
||||||
*/
|
*/
|
||||||
if !cfg!(feature = "element_hacks")
|
if !cfg!(feature = "element_hacks")
|
||||||
&& !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
&& !services.rooms.lazy_loading.lazy_load_was_sent_before(
|
||||||
sender_user,
|
sender_user,
|
||||||
sender_device,
|
sender_device,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
@@ -233,11 +231,12 @@ pub(crate) async fn get_message_events_route(
|
|||||||
|
|
||||||
resp.state = Vec::new();
|
resp.state = Vec::new();
|
||||||
for ll_id in &lazy_loaded {
|
for ll_id in &lazy_loaded {
|
||||||
if let Some(member_event) = services().rooms.state_accessor.room_state_get(
|
if let Some(member_event) =
|
||||||
&body.room_id,
|
services
|
||||||
&StateEventType::RoomMember,
|
.rooms
|
||||||
ll_id.as_str(),
|
.state_accessor
|
||||||
)? {
|
.room_state_get(&body.room_id, &StateEventType::RoomMember, ll_id.as_str())?
|
||||||
|
{
|
||||||
resp.state.push(member_event.to_state_event());
|
resp.state.push(member_event.to_state_event());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,7 +244,7 @@ pub(crate) async fn get_message_events_route(
|
|||||||
// remove the feature check when we are sure clients like element can handle it
|
// remove the feature check when we are sure clients like element can handle it
|
||||||
if !cfg!(feature = "element_hacks") {
|
if !cfg!(feature = "element_hacks") {
|
||||||
if let Some(next_token) = next_token {
|
if let Some(next_token) = next_token {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.lazy_loading
|
.lazy_loading
|
||||||
.lazy_load_mark_sent(sender_user, sender_device, &body.room_id, lazy_loaded, next_token)
|
.lazy_load_mark_sent(sender_user, sender_device, &body.room_id, lazy_loaded, next_token)
|
||||||
@@ -256,8 +255,8 @@ pub(crate) async fn get_message_events_route(
|
|||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visibility_filter(pdu: &PduEvent, user_id: &UserId, room_id: &RoomId) -> bool {
|
fn visibility_filter(services: &Services, pdu: &PduEvent, user_id: &UserId, room_id: &RoomId) -> bool {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(user_id, room_id, &pdu.event_id)
|
.user_can_see_event(user_id, room_id, &pdu.event_id)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use conduit::utils;
|
use conduit::utils;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{account, error::ErrorKind},
|
api::client::{account, error::ErrorKind},
|
||||||
@@ -7,7 +8,7 @@ use ruma::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::TOKEN_LENGTH;
|
use super::TOKEN_LENGTH;
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/user/{userId}/openid/request_token`
|
/// # `POST /_matrix/client/v3/user/{userId}/openid/request_token`
|
||||||
///
|
///
|
||||||
@@ -15,7 +16,7 @@ use crate::{services, Error, Result, Ruma};
|
|||||||
///
|
///
|
||||||
/// - The token generated is only valid for the OpenID API
|
/// - The token generated is only valid for the OpenID API
|
||||||
pub(crate) async fn create_openid_token_route(
|
pub(crate) async fn create_openid_token_route(
|
||||||
body: Ruma<account::request_openid_token::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<account::request_openid_token::v3::Request>,
|
||||||
) -> Result<account::request_openid_token::v3::Response> {
|
) -> Result<account::request_openid_token::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -28,14 +29,14 @@ pub(crate) async fn create_openid_token_route(
|
|||||||
|
|
||||||
let access_token = utils::random_string(TOKEN_LENGTH);
|
let access_token = utils::random_string(TOKEN_LENGTH);
|
||||||
|
|
||||||
let expires_in = services()
|
let expires_in = services
|
||||||
.users
|
.users
|
||||||
.create_openid_token(&body.user_id, &access_token)?;
|
.create_openid_token(&body.user_id, &access_token)?;
|
||||||
|
|
||||||
Ok(account::request_openid_token::v3::Response {
|
Ok(account::request_openid_token::v3::Response {
|
||||||
access_token,
|
access_token,
|
||||||
token_type: TokenType::Bearer,
|
token_type: TokenType::Bearer,
|
||||||
matrix_server_name: services().globals.config.server_name.clone(),
|
matrix_server_name: services.globals.config.server_name.clone(),
|
||||||
expires_in: Duration::from_secs(expires_in),
|
expires_in: Duration::from_secs(expires_in),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,32 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use ruma::api::client::{
|
use ruma::api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
presence::{get_presence, set_presence},
|
presence::{get_presence, set_presence},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/presence/{userId}/status`
|
/// # `PUT /_matrix/client/r0/presence/{userId}/status`
|
||||||
///
|
///
|
||||||
/// Sets the presence state of the sender user.
|
/// Sets the presence state of the sender user.
|
||||||
pub(crate) async fn set_presence_route(body: Ruma<set_presence::v3::Request>) -> Result<set_presence::v3::Response> {
|
pub(crate) async fn set_presence_route(
|
||||||
if !services().globals.allow_local_presence() {
|
State(services): State<crate::State>, body: Ruma<set_presence::v3::Request>,
|
||||||
|
) -> Result<set_presence::v3::Response> {
|
||||||
|
if !services.globals.allow_local_presence() {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
|
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
services()
|
if sender_user != &body.user_id && body.appservice_info.is_none() {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Not allowed to set presence of other users",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
services
|
||||||
.presence
|
.presence
|
||||||
.set_presence(sender_user, &body.presence, None, None, body.status_msg.clone())?;
|
.set_presence(sender_user, &body.presence, None, None, body.status_msg.clone())?;
|
||||||
|
|
||||||
@@ -28,8 +38,10 @@ pub(crate) async fn set_presence_route(body: Ruma<set_presence::v3::Request>) ->
|
|||||||
/// Gets the presence state of the given user.
|
/// Gets the presence state of the given user.
|
||||||
///
|
///
|
||||||
/// - Only works if you share a room with the user
|
/// - Only works if you share a room with the user
|
||||||
pub(crate) async fn get_presence_route(body: Ruma<get_presence::v3::Request>) -> Result<get_presence::v3::Response> {
|
pub(crate) async fn get_presence_route(
|
||||||
if !services().globals.allow_local_presence() {
|
State(services): State<crate::State>, body: Ruma<get_presence::v3::Request>,
|
||||||
|
) -> Result<get_presence::v3::Response> {
|
||||||
|
if !services.globals.allow_local_presence() {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
|
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,12 +49,12 @@ pub(crate) async fn get_presence_route(body: Ruma<get_presence::v3::Request>) ->
|
|||||||
|
|
||||||
let mut presence_event = None;
|
let mut presence_event = None;
|
||||||
|
|
||||||
for _room_id in services()
|
for _room_id in services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])?
|
.get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])?
|
||||||
{
|
{
|
||||||
if let Some(presence) = services().presence.get_presence(&body.user_id)? {
|
if let Some(presence) = services.presence.get_presence(&body.user_id)? {
|
||||||
presence_event = Some(presence);
|
presence_event = Some(presence);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-59
@@ -1,3 +1,5 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use conduit::{pdu::PduBuilder, warn, Error, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::{
|
client::{
|
||||||
@@ -11,12 +13,9 @@ use ruma::{
|
|||||||
OwnedMxcUri, OwnedRoomId, OwnedUserId,
|
OwnedMxcUri, OwnedRoomId, OwnedUserId,
|
||||||
};
|
};
|
||||||
use serde_json::value::to_raw_value;
|
use serde_json::value::to_raw_value;
|
||||||
use tracing::warn;
|
use service::Services;
|
||||||
|
|
||||||
use crate::{
|
use crate::Ruma;
|
||||||
service::{pdu::PduBuilder, user_is_local},
|
|
||||||
services, Error, Result, Ruma,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/profile/{userId}/displayname`
|
/// # `PUT /_matrix/client/r0/profile/{userId}/displayname`
|
||||||
///
|
///
|
||||||
@@ -24,21 +23,21 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// - Also makes sure other users receive the update using presence EDUs
|
/// - Also makes sure other users receive the update using presence EDUs
|
||||||
pub(crate) async fn set_displayname_route(
|
pub(crate) async fn set_displayname_route(
|
||||||
body: Ruma<set_display_name::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<set_display_name::v3::Request>,
|
||||||
) -> Result<set_display_name::v3::Response> {
|
) -> Result<set_display_name::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let all_joined_rooms: Vec<OwnedRoomId> = services()
|
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(sender_user)
|
.rooms_joined(sender_user)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
update_displayname(sender_user.clone(), body.displayname.clone(), all_joined_rooms).await?;
|
update_displayname(&services, sender_user.clone(), body.displayname.clone(), all_joined_rooms).await?;
|
||||||
|
|
||||||
if services().globals.allow_local_presence() {
|
if services.globals.allow_local_presence() {
|
||||||
// Presence update
|
// Presence update
|
||||||
services()
|
services
|
||||||
.presence
|
.presence
|
||||||
.ping_presence(sender_user, &PresenceState::Online)?;
|
.ping_presence(sender_user, &PresenceState::Online)?;
|
||||||
}
|
}
|
||||||
@@ -53,11 +52,11 @@ pub(crate) async fn set_displayname_route(
|
|||||||
/// - If user is on another server and we do not have a local copy already fetch
|
/// - If user is on another server and we do not have a local copy already fetch
|
||||||
/// displayname over federation
|
/// displayname over federation
|
||||||
pub(crate) async fn get_displayname_route(
|
pub(crate) async fn get_displayname_route(
|
||||||
body: Ruma<get_display_name::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_display_name::v3::Request>,
|
||||||
) -> Result<get_display_name::v3::Response> {
|
) -> Result<get_display_name::v3::Response> {
|
||||||
if !user_is_local(&body.user_id) {
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
// Create and update our local copy of the user
|
// Create and update our local copy of the user
|
||||||
if let Ok(response) = services()
|
if let Ok(response) = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
body.user_id.server_name(),
|
body.user_id.server_name(),
|
||||||
@@ -68,19 +67,19 @@ pub(crate) async fn get_displayname_route(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
if !services().users.exists(&body.user_id)? {
|
if !services.users.exists(&body.user_id)? {
|
||||||
services().users.create(&body.user_id, None)?;
|
services.users.create(&body.user_id, None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_displayname(&body.user_id, response.displayname.clone())
|
.set_displayname(&body.user_id, response.displayname.clone())
|
||||||
.await?;
|
.await?;
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_avatar_url(&body.user_id, response.avatar_url.clone())
|
.set_avatar_url(&body.user_id, response.avatar_url.clone())
|
||||||
.await?;
|
.await?;
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_blurhash(&body.user_id, response.blurhash.clone())
|
.set_blurhash(&body.user_id, response.blurhash.clone())
|
||||||
.await?;
|
.await?;
|
||||||
@@ -91,14 +90,14 @@ pub(crate) async fn get_displayname_route(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().users.exists(&body.user_id)? {
|
if !services.users.exists(&body.user_id)? {
|
||||||
// Return 404 if this user doesn't exist and we couldn't fetch it over
|
// Return 404 if this user doesn't exist and we couldn't fetch it over
|
||||||
// federation
|
// federation
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_display_name::v3::Response {
|
Ok(get_display_name::v3::Response {
|
||||||
displayname: services().users.displayname(&body.user_id)?,
|
displayname: services.users.displayname(&body.user_id)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,10 +107,10 @@ pub(crate) async fn get_displayname_route(
|
|||||||
///
|
///
|
||||||
/// - Also makes sure other users receive the update using presence EDUs
|
/// - Also makes sure other users receive the update using presence EDUs
|
||||||
pub(crate) async fn set_avatar_url_route(
|
pub(crate) async fn set_avatar_url_route(
|
||||||
body: Ruma<set_avatar_url::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<set_avatar_url::v3::Request>,
|
||||||
) -> Result<set_avatar_url::v3::Response> {
|
) -> Result<set_avatar_url::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let all_joined_rooms: Vec<OwnedRoomId> = services()
|
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(sender_user)
|
.rooms_joined(sender_user)
|
||||||
@@ -119,6 +118,7 @@ pub(crate) async fn set_avatar_url_route(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
update_avatar_url(
|
update_avatar_url(
|
||||||
|
&services,
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
body.avatar_url.clone(),
|
body.avatar_url.clone(),
|
||||||
body.blurhash.clone(),
|
body.blurhash.clone(),
|
||||||
@@ -126,9 +126,9 @@ pub(crate) async fn set_avatar_url_route(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if services().globals.allow_local_presence() {
|
if services.globals.allow_local_presence() {
|
||||||
// Presence update
|
// Presence update
|
||||||
services()
|
services
|
||||||
.presence
|
.presence
|
||||||
.ping_presence(sender_user, &PresenceState::Online)?;
|
.ping_presence(sender_user, &PresenceState::Online)?;
|
||||||
}
|
}
|
||||||
@@ -143,11 +143,11 @@ pub(crate) async fn set_avatar_url_route(
|
|||||||
/// - If user is on another server and we do not have a local copy already fetch
|
/// - If user is on another server and we do not have a local copy already fetch
|
||||||
/// `avatar_url` and blurhash over federation
|
/// `avatar_url` and blurhash over federation
|
||||||
pub(crate) async fn get_avatar_url_route(
|
pub(crate) async fn get_avatar_url_route(
|
||||||
body: Ruma<get_avatar_url::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_avatar_url::v3::Request>,
|
||||||
) -> Result<get_avatar_url::v3::Response> {
|
) -> Result<get_avatar_url::v3::Response> {
|
||||||
if !user_is_local(&body.user_id) {
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
// Create and update our local copy of the user
|
// Create and update our local copy of the user
|
||||||
if let Ok(response) = services()
|
if let Ok(response) = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
body.user_id.server_name(),
|
body.user_id.server_name(),
|
||||||
@@ -158,19 +158,19 @@ pub(crate) async fn get_avatar_url_route(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
if !services().users.exists(&body.user_id)? {
|
if !services.users.exists(&body.user_id)? {
|
||||||
services().users.create(&body.user_id, None)?;
|
services.users.create(&body.user_id, None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_displayname(&body.user_id, response.displayname.clone())
|
.set_displayname(&body.user_id, response.displayname.clone())
|
||||||
.await?;
|
.await?;
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_avatar_url(&body.user_id, response.avatar_url.clone())
|
.set_avatar_url(&body.user_id, response.avatar_url.clone())
|
||||||
.await?;
|
.await?;
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_blurhash(&body.user_id, response.blurhash.clone())
|
.set_blurhash(&body.user_id, response.blurhash.clone())
|
||||||
.await?;
|
.await?;
|
||||||
@@ -182,15 +182,15 @@ pub(crate) async fn get_avatar_url_route(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().users.exists(&body.user_id)? {
|
if !services.users.exists(&body.user_id)? {
|
||||||
// Return 404 if this user doesn't exist and we couldn't fetch it over
|
// Return 404 if this user doesn't exist and we couldn't fetch it over
|
||||||
// federation
|
// federation
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_avatar_url::v3::Response {
|
Ok(get_avatar_url::v3::Response {
|
||||||
avatar_url: services().users.avatar_url(&body.user_id)?,
|
avatar_url: services.users.avatar_url(&body.user_id)?,
|
||||||
blurhash: services().users.blurhash(&body.user_id)?,
|
blurhash: services.users.blurhash(&body.user_id)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,10 +200,12 @@ pub(crate) async fn get_avatar_url_route(
|
|||||||
///
|
///
|
||||||
/// - If user is on another server and we do not have a local copy already,
|
/// - If user is on another server and we do not have a local copy already,
|
||||||
/// fetch profile over federation.
|
/// fetch profile over federation.
|
||||||
pub(crate) async fn get_profile_route(body: Ruma<get_profile::v3::Request>) -> Result<get_profile::v3::Response> {
|
pub(crate) async fn get_profile_route(
|
||||||
if !user_is_local(&body.user_id) {
|
State(services): State<crate::State>, body: Ruma<get_profile::v3::Request>,
|
||||||
|
) -> Result<get_profile::v3::Response> {
|
||||||
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
// Create and update our local copy of the user
|
// Create and update our local copy of the user
|
||||||
if let Ok(response) = services()
|
if let Ok(response) = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
body.user_id.server_name(),
|
body.user_id.server_name(),
|
||||||
@@ -214,19 +216,19 @@ pub(crate) async fn get_profile_route(body: Ruma<get_profile::v3::Request>) -> R
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
if !services().users.exists(&body.user_id)? {
|
if !services.users.exists(&body.user_id)? {
|
||||||
services().users.create(&body.user_id, None)?;
|
services.users.create(&body.user_id, None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_displayname(&body.user_id, response.displayname.clone())
|
.set_displayname(&body.user_id, response.displayname.clone())
|
||||||
.await?;
|
.await?;
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_avatar_url(&body.user_id, response.avatar_url.clone())
|
.set_avatar_url(&body.user_id, response.avatar_url.clone())
|
||||||
.await?;
|
.await?;
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_blurhash(&body.user_id, response.blurhash.clone())
|
.set_blurhash(&body.user_id, response.blurhash.clone())
|
||||||
.await?;
|
.await?;
|
||||||
@@ -239,23 +241,23 @@ pub(crate) async fn get_profile_route(body: Ruma<get_profile::v3::Request>) -> R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().users.exists(&body.user_id)? {
|
if !services.users.exists(&body.user_id)? {
|
||||||
// Return 404 if this user doesn't exist and we couldn't fetch it over
|
// Return 404 if this user doesn't exist and we couldn't fetch it over
|
||||||
// federation
|
// federation
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_profile::v3::Response {
|
Ok(get_profile::v3::Response {
|
||||||
avatar_url: services().users.avatar_url(&body.user_id)?,
|
avatar_url: services.users.avatar_url(&body.user_id)?,
|
||||||
blurhash: services().users.blurhash(&body.user_id)?,
|
blurhash: services.users.blurhash(&body.user_id)?,
|
||||||
displayname: services().users.displayname(&body.user_id)?,
|
displayname: services.users.displayname(&body.user_id)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_displayname(
|
pub async fn update_displayname(
|
||||||
user_id: OwnedUserId, displayname: Option<String>, all_joined_rooms: Vec<OwnedRoomId>,
|
services: &Services, user_id: OwnedUserId, displayname: Option<String>, all_joined_rooms: Vec<OwnedRoomId>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_displayname(&user_id, displayname.clone())
|
.set_displayname(&user_id, displayname.clone())
|
||||||
.await?;
|
.await?;
|
||||||
@@ -271,7 +273,7 @@ pub async fn update_displayname(
|
|||||||
displayname: displayname.clone(),
|
displayname: displayname.clone(),
|
||||||
join_authorized_via_users_server: None,
|
join_authorized_via_users_server: None,
|
||||||
..serde_json::from_str(
|
..serde_json::from_str(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
|
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
|
||||||
@@ -294,19 +296,20 @@ pub async fn update_displayname(
|
|||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
update_all_rooms(all_joined_rooms, user_id).await;
|
update_all_rooms(services, all_joined_rooms, user_id).await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_avatar_url(
|
pub async fn update_avatar_url(
|
||||||
user_id: OwnedUserId, avatar_url: Option<OwnedMxcUri>, blurhash: Option<String>, all_joined_rooms: Vec<OwnedRoomId>,
|
services: &Services, user_id: OwnedUserId, avatar_url: Option<OwnedMxcUri>, blurhash: Option<String>,
|
||||||
|
all_joined_rooms: Vec<OwnedRoomId>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_avatar_url(&user_id, avatar_url.clone())
|
.set_avatar_url(&user_id, avatar_url.clone())
|
||||||
.await?;
|
.await?;
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.set_blurhash(&user_id, blurhash.clone())
|
.set_blurhash(&user_id, blurhash.clone())
|
||||||
.await?;
|
.await?;
|
||||||
@@ -323,7 +326,7 @@ pub async fn update_avatar_url(
|
|||||||
blurhash: blurhash.clone(),
|
blurhash: blurhash.clone(),
|
||||||
join_authorized_via_users_server: None,
|
join_authorized_via_users_server: None,
|
||||||
..serde_json::from_str(
|
..serde_json::from_str(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
|
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
|
||||||
@@ -346,15 +349,17 @@ pub async fn update_avatar_url(
|
|||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
update_all_rooms(all_joined_rooms, user_id).await;
|
update_all_rooms(services, all_joined_rooms, user_id).await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_all_rooms(all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: OwnedUserId) {
|
pub async fn update_all_rooms(
|
||||||
|
services: &Services, all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: OwnedUserId,
|
||||||
|
) {
|
||||||
for (pdu_builder, room_id) in all_joined_rooms {
|
for (pdu_builder, room_id) in all_joined_rooms {
|
||||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||||
if let Err(e) = services()
|
if let Err(e) = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(pdu_builder, &user_id, room_id, &state_lock)
|
.build_and_append_pdu(pdu_builder, &user_id, room_id, &state_lock)
|
||||||
|
|||||||
+35
-26
@@ -1,3 +1,4 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
@@ -10,18 +11,18 @@ use ruma::{
|
|||||||
push::{InsertPushRuleError, RemovePushRuleError, Ruleset},
|
push::{InsertPushRuleError, RemovePushRuleError, Ruleset},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/pushrules/`
|
/// # `GET /_matrix/client/r0/pushrules/`
|
||||||
///
|
///
|
||||||
/// Retrieves the push rules event for this user.
|
/// Retrieves the push rules event for this user.
|
||||||
pub(crate) async fn get_pushrules_all_route(
|
pub(crate) async fn get_pushrules_all_route(
|
||||||
body: Ruma<get_pushrules_all::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_pushrules_all::v3::Request>,
|
||||||
) -> Result<get_pushrules_all::v3::Response> {
|
) -> Result<get_pushrules_all::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event =
|
let event =
|
||||||
services()
|
services
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?;
|
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?;
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ pub(crate) async fn get_pushrules_all_route(
|
|||||||
global: account_data.global,
|
global: account_data.global,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
None,
|
None,
|
||||||
sender_user,
|
sender_user,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
@@ -55,10 +56,12 @@ pub(crate) async fn get_pushrules_all_route(
|
|||||||
/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
||||||
///
|
///
|
||||||
/// Retrieves a single specified push rule for this user.
|
/// Retrieves a single specified push rule for this user.
|
||||||
pub(crate) async fn get_pushrule_route(body: Ruma<get_pushrule::v3::Request>) -> Result<get_pushrule::v3::Response> {
|
pub(crate) async fn get_pushrule_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_pushrule::v3::Request>,
|
||||||
|
) -> Result<get_pushrule::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||||
@@ -84,7 +87,9 @@ pub(crate) async fn get_pushrule_route(body: Ruma<get_pushrule::v3::Request>) ->
|
|||||||
/// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
/// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
||||||
///
|
///
|
||||||
/// Creates a single specified push rule for this user.
|
/// Creates a single specified push rule for this user.
|
||||||
pub(crate) async fn set_pushrule_route(body: Ruma<set_pushrule::v3::Request>) -> Result<set_pushrule::v3::Response> {
|
pub(crate) async fn set_pushrule_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<set_pushrule::v3::Request>,
|
||||||
|
) -> Result<set_pushrule::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let body = body.body;
|
let body = body.body;
|
||||||
|
|
||||||
@@ -95,7 +100,7 @@ pub(crate) async fn set_pushrule_route(body: Ruma<set_pushrule::v3::Request>) ->
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||||
@@ -134,7 +139,7 @@ pub(crate) async fn set_pushrule_route(body: Ruma<set_pushrule::v3::Request>) ->
|
|||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
None,
|
None,
|
||||||
sender_user,
|
sender_user,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
@@ -148,7 +153,7 @@ pub(crate) async fn set_pushrule_route(body: Ruma<set_pushrule::v3::Request>) ->
|
|||||||
///
|
///
|
||||||
/// Gets the actions of a single specified push rule for this user.
|
/// Gets the actions of a single specified push rule for this user.
|
||||||
pub(crate) async fn get_pushrule_actions_route(
|
pub(crate) async fn get_pushrule_actions_route(
|
||||||
body: Ruma<get_pushrule_actions::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_pushrule_actions::v3::Request>,
|
||||||
) -> Result<get_pushrule_actions::v3::Response> {
|
) -> Result<get_pushrule_actions::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -159,7 +164,7 @@ pub(crate) async fn get_pushrule_actions_route(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||||
@@ -183,7 +188,7 @@ pub(crate) async fn get_pushrule_actions_route(
|
|||||||
///
|
///
|
||||||
/// Sets the actions of a single specified push rule for this user.
|
/// Sets the actions of a single specified push rule for this user.
|
||||||
pub(crate) async fn set_pushrule_actions_route(
|
pub(crate) async fn set_pushrule_actions_route(
|
||||||
body: Ruma<set_pushrule_actions::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<set_pushrule_actions::v3::Request>,
|
||||||
) -> Result<set_pushrule_actions::v3::Response> {
|
) -> Result<set_pushrule_actions::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -194,7 +199,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||||
@@ -211,7 +216,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
|||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
None,
|
None,
|
||||||
sender_user,
|
sender_user,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
@@ -225,7 +230,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
|||||||
///
|
///
|
||||||
/// Gets the enabled status of a single specified push rule for this user.
|
/// Gets the enabled status of a single specified push rule for this user.
|
||||||
pub(crate) async fn get_pushrule_enabled_route(
|
pub(crate) async fn get_pushrule_enabled_route(
|
||||||
body: Ruma<get_pushrule_enabled::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_pushrule_enabled::v3::Request>,
|
||||||
) -> Result<get_pushrule_enabled::v3::Response> {
|
) -> Result<get_pushrule_enabled::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -236,7 +241,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||||
@@ -259,7 +264,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|
|||||||
///
|
///
|
||||||
/// Sets the enabled status of a single specified push rule for this user.
|
/// Sets the enabled status of a single specified push rule for this user.
|
||||||
pub(crate) async fn set_pushrule_enabled_route(
|
pub(crate) async fn set_pushrule_enabled_route(
|
||||||
body: Ruma<set_pushrule_enabled::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<set_pushrule_enabled::v3::Request>,
|
||||||
) -> Result<set_pushrule_enabled::v3::Response> {
|
) -> Result<set_pushrule_enabled::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -270,7 +275,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||||
@@ -287,7 +292,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
|||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
None,
|
None,
|
||||||
sender_user,
|
sender_user,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
@@ -301,7 +306,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
|||||||
///
|
///
|
||||||
/// Deletes a single specified push rule for this user.
|
/// Deletes a single specified push rule for this user.
|
||||||
pub(crate) async fn delete_pushrule_route(
|
pub(crate) async fn delete_pushrule_route(
|
||||||
body: Ruma<delete_pushrule::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<delete_pushrule::v3::Request>,
|
||||||
) -> Result<delete_pushrule::v3::Response> {
|
) -> Result<delete_pushrule::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -312,7 +317,7 @@ pub(crate) async fn delete_pushrule_route(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||||
@@ -336,7 +341,7 @@ pub(crate) async fn delete_pushrule_route(
|
|||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
None,
|
None,
|
||||||
sender_user,
|
sender_user,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
@@ -349,11 +354,13 @@ pub(crate) async fn delete_pushrule_route(
|
|||||||
/// # `GET /_matrix/client/r0/pushers`
|
/// # `GET /_matrix/client/r0/pushers`
|
||||||
///
|
///
|
||||||
/// Gets all currently active pushers for the sender user.
|
/// Gets all currently active pushers for the sender user.
|
||||||
pub(crate) async fn get_pushers_route(body: Ruma<get_pushers::v3::Request>) -> Result<get_pushers::v3::Response> {
|
pub(crate) async fn get_pushers_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_pushers::v3::Request>,
|
||||||
|
) -> Result<get_pushers::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
Ok(get_pushers::v3::Response {
|
Ok(get_pushers::v3::Response {
|
||||||
pushers: services().pusher.get_pushers(sender_user)?,
|
pushers: services.pusher.get_pushers(sender_user)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,10 +369,12 @@ pub(crate) async fn get_pushers_route(body: Ruma<get_pushers::v3::Request>) -> R
|
|||||||
/// Adds a pusher for the sender user.
|
/// Adds a pusher for the sender user.
|
||||||
///
|
///
|
||||||
/// - TODO: Handle `append`
|
/// - TODO: Handle `append`
|
||||||
pub(crate) async fn set_pushers_route(body: Ruma<set_pusher::v3::Request>) -> Result<set_pusher::v3::Response> {
|
pub(crate) async fn set_pushers_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<set_pusher::v3::Request>,
|
||||||
|
) -> Result<set_pusher::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
services().pusher.set_pusher(sender_user, &body.action)?;
|
services.pusher.set_pusher(sender_user, &body.action)?;
|
||||||
|
|
||||||
Ok(set_pusher::v3::Response::default())
|
Ok(set_pusher::v3::Response::default())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use conduit::PduCount;
|
use conduit::PduCount;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{error::ErrorKind, read_marker::set_read_marker, receipt::create_receipt},
|
api::client::{error::ErrorKind, read_marker::set_read_marker, receipt::create_receipt},
|
||||||
@@ -10,7 +11,7 @@ use ruma::{
|
|||||||
MilliSecondsSinceUnixEpoch,
|
MilliSecondsSinceUnixEpoch,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers`
|
/// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers`
|
||||||
///
|
///
|
||||||
@@ -20,7 +21,7 @@ use crate::{services, Error, Result, Ruma};
|
|||||||
/// - If `read_receipt` is set: Update private marker and public read receipt
|
/// - If `read_receipt` is set: Update private marker and public read receipt
|
||||||
/// EDU
|
/// EDU
|
||||||
pub(crate) async fn set_read_marker_route(
|
pub(crate) async fn set_read_marker_route(
|
||||||
body: Ruma<set_read_marker::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<set_read_marker::v3::Request>,
|
||||||
) -> Result<set_read_marker::v3::Response> {
|
) -> Result<set_read_marker::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ pub(crate) async fn set_read_marker_route(
|
|||||||
event_id: fully_read.clone(),
|
event_id: fully_read.clone(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
Some(&body.room_id),
|
Some(&body.room_id),
|
||||||
sender_user,
|
sender_user,
|
||||||
RoomAccountDataEventType::FullyRead,
|
RoomAccountDataEventType::FullyRead,
|
||||||
@@ -39,14 +40,14 @@ pub(crate) async fn set_read_marker_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if body.private_read_receipt.is_some() || body.read_receipt.is_some() {
|
if body.private_read_receipt.is_some() || body.read_receipt.is_some() {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.reset_notification_counts(sender_user, &body.room_id)?;
|
.reset_notification_counts(sender_user, &body.room_id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(event) = &body.private_read_receipt {
|
if let Some(event) = &body.private_read_receipt {
|
||||||
let count = services()
|
let count = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_pdu_count(event)?
|
.get_pdu_count(event)?
|
||||||
@@ -60,7 +61,7 @@ pub(crate) async fn set_read_marker_route(
|
|||||||
},
|
},
|
||||||
PduCount::Normal(c) => c,
|
PduCount::Normal(c) => c,
|
||||||
};
|
};
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.read_receipt
|
.read_receipt
|
||||||
.private_read_set(&body.room_id, sender_user, count)?;
|
.private_read_set(&body.room_id, sender_user, count)?;
|
||||||
@@ -82,7 +83,7 @@ pub(crate) async fn set_read_marker_route(
|
|||||||
let mut receipt_content = BTreeMap::new();
|
let mut receipt_content = BTreeMap::new();
|
||||||
receipt_content.insert(event.to_owned(), receipts);
|
receipt_content.insert(event.to_owned(), receipts);
|
||||||
|
|
||||||
services().rooms.read_receipt.readreceipt_update(
|
services.rooms.read_receipt.readreceipt_update(
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&ruma::events::receipt::ReceiptEvent {
|
&ruma::events::receipt::ReceiptEvent {
|
||||||
@@ -99,7 +100,7 @@ pub(crate) async fn set_read_marker_route(
|
|||||||
///
|
///
|
||||||
/// Sets private read marker and public read receipt EDU.
|
/// Sets private read marker and public read receipt EDU.
|
||||||
pub(crate) async fn create_receipt_route(
|
pub(crate) async fn create_receipt_route(
|
||||||
body: Ruma<create_receipt::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<create_receipt::v3::Request>,
|
||||||
) -> Result<create_receipt::v3::Response> {
|
) -> Result<create_receipt::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ pub(crate) async fn create_receipt_route(
|
|||||||
&body.receipt_type,
|
&body.receipt_type,
|
||||||
create_receipt::v3::ReceiptType::Read | create_receipt::v3::ReceiptType::ReadPrivate
|
create_receipt::v3::ReceiptType::Read | create_receipt::v3::ReceiptType::ReadPrivate
|
||||||
) {
|
) {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.reset_notification_counts(sender_user, &body.room_id)?;
|
.reset_notification_counts(sender_user, &body.room_id)?;
|
||||||
@@ -120,7 +121,7 @@ pub(crate) async fn create_receipt_route(
|
|||||||
event_id: body.event_id.clone(),
|
event_id: body.event_id.clone(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
Some(&body.room_id),
|
Some(&body.room_id),
|
||||||
sender_user,
|
sender_user,
|
||||||
RoomAccountDataEventType::FullyRead,
|
RoomAccountDataEventType::FullyRead,
|
||||||
@@ -142,7 +143,7 @@ pub(crate) async fn create_receipt_route(
|
|||||||
let mut receipt_content = BTreeMap::new();
|
let mut receipt_content = BTreeMap::new();
|
||||||
receipt_content.insert(body.event_id.clone(), receipts);
|
receipt_content.insert(body.event_id.clone(), receipts);
|
||||||
|
|
||||||
services().rooms.read_receipt.readreceipt_update(
|
services.rooms.read_receipt.readreceipt_update(
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&ruma::events::receipt::ReceiptEvent {
|
&ruma::events::receipt::ReceiptEvent {
|
||||||
@@ -152,7 +153,7 @@ pub(crate) async fn create_receipt_route(
|
|||||||
)?;
|
)?;
|
||||||
},
|
},
|
||||||
create_receipt::v3::ReceiptType::ReadPrivate => {
|
create_receipt::v3::ReceiptType::ReadPrivate => {
|
||||||
let count = services()
|
let count = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_pdu_count(&body.event_id)?
|
.get_pdu_count(&body.event_id)?
|
||||||
@@ -166,7 +167,7 @@ pub(crate) async fn create_receipt_route(
|
|||||||
},
|
},
|
||||||
PduCount::Normal(c) => c,
|
PduCount::Normal(c) => c,
|
||||||
};
|
};
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.read_receipt
|
.read_receipt
|
||||||
.private_read_set(&body.room_id, sender_user, count)?;
|
.private_read_set(&body.room_id, sender_user, count)?;
|
||||||
|
|||||||
@@ -1,27 +1,26 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::redact::redact_event,
|
api::client::redact::redact_event,
|
||||||
events::{room::redaction::RoomRedactionEventContent, TimelineEventType},
|
events::{room::redaction::RoomRedactionEventContent, TimelineEventType},
|
||||||
};
|
};
|
||||||
use serde_json::value::to_raw_value;
|
use serde_json::value::to_raw_value;
|
||||||
|
|
||||||
use crate::{service::pdu::PduBuilder, services, Result, Ruma};
|
use crate::{service::pdu::PduBuilder, Result, Ruma};
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}`
|
/// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}`
|
||||||
///
|
///
|
||||||
/// Tries to send a redaction event into the room.
|
/// Tries to send a redaction event into the room.
|
||||||
///
|
///
|
||||||
/// - TODO: Handle txn id
|
/// - TODO: Handle txn id
|
||||||
pub(crate) async fn redact_event_route(body: Ruma<redact_event::v3::Request>) -> Result<redact_event::v3::Response> {
|
pub(crate) async fn redact_event_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<redact_event::v3::Request>,
|
||||||
|
) -> Result<redact_event::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let body = body.body;
|
let body = body.body;
|
||||||
|
|
||||||
let state_lock = services()
|
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||||
.globals
|
|
||||||
.roomid_mutex_state
|
|
||||||
.lock(&body.room_id)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let event_id = services()
|
let event_id = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::api::client::relations::{
|
use ruma::api::client::relations::{
|
||||||
get_relating_events, get_relating_events_with_rel_type, get_relating_events_with_rel_type_and_event_type,
|
get_relating_events, get_relating_events_with_rel_type, get_relating_events_with_rel_type_and_event_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Result, Ruma};
|
use crate::{Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
|
||||||
pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||||
body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
|
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
|
||||||
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
|
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let res = services()
|
let res = services.rooms.pdu_metadata.paginate_relations_with_filter(
|
||||||
.rooms
|
|
||||||
.pdu_metadata
|
|
||||||
.paginate_relations_with_filter(
|
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
@@ -36,14 +34,11 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
|||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}`
|
||||||
pub(crate) async fn get_relating_events_with_rel_type_route(
|
pub(crate) async fn get_relating_events_with_rel_type_route(
|
||||||
body: Ruma<get_relating_events_with_rel_type::v1::Request>,
|
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type::v1::Request>,
|
||||||
) -> Result<get_relating_events_with_rel_type::v1::Response> {
|
) -> Result<get_relating_events_with_rel_type::v1::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let res = services()
|
let res = services.rooms.pdu_metadata.paginate_relations_with_filter(
|
||||||
.rooms
|
|
||||||
.pdu_metadata
|
|
||||||
.paginate_relations_with_filter(
|
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
@@ -66,14 +61,11 @@ pub(crate) async fn get_relating_events_with_rel_type_route(
|
|||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}`
|
||||||
pub(crate) async fn get_relating_events_route(
|
pub(crate) async fn get_relating_events_route(
|
||||||
body: Ruma<get_relating_events::v1::Request>,
|
State(services): State<crate::State>, body: Ruma<get_relating_events::v1::Request>,
|
||||||
) -> Result<get_relating_events::v1::Response> {
|
) -> Result<get_relating_events::v1::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
services()
|
services.rooms.pdu_metadata.paginate_relations_with_filter(
|
||||||
.rooms
|
|
||||||
.pdu_metadata
|
|
||||||
.paginate_relations_with_filter(
|
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{error::ErrorKind, room::report_content},
|
api::client::{error::ErrorKind, room::report_content},
|
||||||
@@ -9,13 +10,18 @@ use ruma::{
|
|||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::{debug_info, service::pdu::PduEvent, services, utils::HtmlEscape, Error, Result, Ruma};
|
use crate::{
|
||||||
|
debug_info,
|
||||||
|
service::{pdu::PduEvent, Services},
|
||||||
|
utils::HtmlEscape,
|
||||||
|
Error, Result, Ruma,
|
||||||
|
};
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/rooms/{roomId}/report/{eventId}`
|
/// # `POST /_matrix/client/v3/rooms/{roomId}/report/{eventId}`
|
||||||
///
|
///
|
||||||
/// Reports an inappropriate event to homeserver admins
|
/// Reports an inappropriate event to homeserver admins
|
||||||
pub(crate) async fn report_event_route(
|
pub(crate) async fn report_event_route(
|
||||||
body: Ruma<report_content::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<report_content::v3::Request>,
|
||||||
) -> Result<report_content::v3::Response> {
|
) -> Result<report_content::v3::Response> {
|
||||||
// user authentication
|
// user authentication
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
@@ -26,18 +32,26 @@ pub(crate) async fn report_event_route(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// check if we know about the reported event ID or if it's invalid
|
// check if we know about the reported event ID or if it's invalid
|
||||||
let Some(pdu) = services().rooms.timeline.get_pdu(&body.event_id)? else {
|
let Some(pdu) = services.rooms.timeline.get_pdu(&body.event_id)? else {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::NotFound,
|
ErrorKind::NotFound,
|
||||||
"Event ID is not known to us or Event ID is invalid",
|
"Event ID is not known to us or Event ID is invalid",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
is_report_valid(&pdu.event_id, &body.room_id, sender_user, &body.reason, body.score, &pdu)?;
|
is_report_valid(
|
||||||
|
&services,
|
||||||
|
&pdu.event_id,
|
||||||
|
&body.room_id,
|
||||||
|
sender_user,
|
||||||
|
&body.reason,
|
||||||
|
body.score,
|
||||||
|
&pdu,
|
||||||
|
)?;
|
||||||
|
|
||||||
// send admin room message that we received the report with an @room ping for
|
// send admin room message that we received the report with an @room ping for
|
||||||
// urgency
|
// urgency
|
||||||
services()
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(message::RoomMessageEventContent::text_html(
|
.send_message(message::RoomMessageEventContent::text_html(
|
||||||
format!(
|
format!(
|
||||||
@@ -79,8 +93,8 @@ pub(crate) async fn report_event_route(
|
|||||||
/// check if score is in valid range
|
/// check if score is in valid range
|
||||||
/// check if report reasoning is less than or equal to 750 characters
|
/// check if report reasoning is less than or equal to 750 characters
|
||||||
fn is_report_valid(
|
fn is_report_valid(
|
||||||
event_id: &EventId, room_id: &RoomId, sender_user: &UserId, reason: &Option<String>, score: Option<ruma::Int>,
|
services: &Services, event_id: &EventId, room_id: &RoomId, sender_user: &UserId, reason: &Option<String>,
|
||||||
pdu: &std::sync::Arc<PduEvent>,
|
score: Option<ruma::Int>, pdu: &std::sync::Arc<PduEvent>,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
debug_info!("Checking if report from user {sender_user} for event {event_id} in room {room_id} is valid");
|
debug_info!("Checking if report from user {sender_user} for event {event_id} in room {room_id} is valid");
|
||||||
|
|
||||||
@@ -91,7 +105,7 @@ fn is_report_valid(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services()
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&pdu.room_id)
|
.room_members(&pdu.room_id)
|
||||||
|
|||||||
+83
-105
@@ -1,5 +1,6 @@
|
|||||||
use std::{cmp::max, collections::BTreeMap};
|
use std::{cmp::max, collections::BTreeMap};
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use conduit::{debug_info, debug_warn};
|
use conduit::{debug_info, debug_warn};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
@@ -30,8 +31,8 @@ use tracing::{error, info, warn};
|
|||||||
|
|
||||||
use super::invite_helper;
|
use super::invite_helper;
|
||||||
use crate::{
|
use crate::{
|
||||||
service::{appservice::RegistrationInfo, pdu::PduBuilder},
|
service::{appservice::RegistrationInfo, pdu::PduBuilder, Services},
|
||||||
services, Error, Result, Ruma,
|
Error, Result, Ruma,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Recommended transferable state events list from the spec
|
/// Recommended transferable state events list from the spec
|
||||||
@@ -63,44 +64,46 @@ const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
|
|||||||
/// - Send events listed in initial state
|
/// - Send events listed in initial state
|
||||||
/// - Send events implied by `name` and `topic`
|
/// - Send events implied by `name` and `topic`
|
||||||
/// - Send invite events
|
/// - Send invite events
|
||||||
pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> Result<create_room::v3::Response> {
|
pub(crate) async fn create_room_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<create_room::v3::Request>,
|
||||||
|
) -> Result<create_room::v3::Response> {
|
||||||
use create_room::v3::RoomPreset;
|
use create_room::v3::RoomPreset;
|
||||||
|
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services().globals.allow_room_creation()
|
if !services.globals.allow_room_creation()
|
||||||
&& body.appservice_info.is_none()
|
&& body.appservice_info.is_none()
|
||||||
&& !services().users.is_admin(sender_user)?
|
&& !services.users.is_admin(sender_user)?
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room creation has been disabled."));
|
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room creation has been disabled."));
|
||||||
}
|
}
|
||||||
|
|
||||||
let room_id: OwnedRoomId = if let Some(custom_room_id) = &body.room_id {
|
let room_id: OwnedRoomId = if let Some(custom_room_id) = &body.room_id {
|
||||||
custom_room_id_check(custom_room_id)?
|
custom_room_id_check(&services, custom_room_id)?
|
||||||
} else {
|
} else {
|
||||||
RoomId::new(&services().globals.config.server_name)
|
RoomId::new(&services.globals.config.server_name)
|
||||||
};
|
};
|
||||||
|
|
||||||
// check if room ID doesn't already exist instead of erroring on auth check
|
// check if room ID doesn't already exist instead of erroring on auth check
|
||||||
if services().rooms.short.get_shortroomid(&room_id)?.is_some() {
|
if services.rooms.short.get_shortroomid(&room_id)?.is_some() {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::RoomInUse,
|
ErrorKind::RoomInUse,
|
||||||
"Room with that custom room ID already exists",
|
"Room with that custom room ID already exists",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let _short_id = services().rooms.short.get_or_create_shortroomid(&room_id)?;
|
let _short_id = services.rooms.short.get_or_create_shortroomid(&room_id)?;
|
||||||
let state_lock = services().globals.roomid_mutex_state.lock(&room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(&room_id).await;
|
||||||
|
|
||||||
let alias: Option<OwnedRoomAliasId> = if let Some(alias) = &body.room_alias_name {
|
let alias: Option<OwnedRoomAliasId> = if let Some(alias) = &body.room_alias_name {
|
||||||
Some(room_alias_check(alias, &body.appservice_info).await?)
|
Some(room_alias_check(&services, alias, &body.appservice_info).await?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let room_version = match body.room_version.clone() {
|
let room_version = match body.room_version.clone() {
|
||||||
Some(room_version) => {
|
Some(room_version) => {
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.supported_room_versions()
|
.supported_room_versions()
|
||||||
.contains(&room_version)
|
.contains(&room_version)
|
||||||
@@ -113,11 +116,13 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => services().globals.default_room_version(),
|
None => services.globals.default_room_version(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let content = match &body.creation_content {
|
let content = match &body.creation_content {
|
||||||
Some(content) => {
|
Some(content) => {
|
||||||
|
use RoomVersionId::*;
|
||||||
|
|
||||||
let mut content = content
|
let mut content = content
|
||||||
.deserialize_as::<CanonicalJsonObject>()
|
.deserialize_as::<CanonicalJsonObject>()
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
@@ -125,16 +130,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
Error::bad_database("Failed to deserialise content as canonical JSON.")
|
Error::bad_database("Failed to deserialise content as canonical JSON.")
|
||||||
})?;
|
})?;
|
||||||
match room_version {
|
match room_version {
|
||||||
RoomVersionId::V1
|
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||||
| RoomVersionId::V2
|
|
||||||
| RoomVersionId::V3
|
|
||||||
| RoomVersionId::V4
|
|
||||||
| RoomVersionId::V5
|
|
||||||
| RoomVersionId::V6
|
|
||||||
| RoomVersionId::V7
|
|
||||||
| RoomVersionId::V8
|
|
||||||
| RoomVersionId::V9
|
|
||||||
| RoomVersionId::V10 => {
|
|
||||||
content.insert(
|
content.insert(
|
||||||
"creator".into(),
|
"creator".into(),
|
||||||
json!(&sender_user).try_into().map_err(|e| {
|
json!(&sender_user).try_into().map_err(|e| {
|
||||||
@@ -143,7 +139,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
})?,
|
})?,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
RoomVersionId::V11 => {}, // V11 removed the "creator" key
|
V11 => {}, // V11 removed the "creator" key
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Unexpected or unsupported room version {room_version}");
|
warn!("Unexpected or unsupported room version {room_version}");
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
@@ -152,7 +148,6 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
));
|
));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
content.insert(
|
content.insert(
|
||||||
"room_version".into(),
|
"room_version".into(),
|
||||||
json!(room_version.as_str())
|
json!(room_version.as_str())
|
||||||
@@ -162,18 +157,11 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
content
|
content
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
use RoomVersionId::*;
|
||||||
|
|
||||||
let content = match room_version {
|
let content = match room_version {
|
||||||
RoomVersionId::V1
|
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => RoomCreateEventContent::new_v1(sender_user.clone()),
|
||||||
| RoomVersionId::V2
|
V11 => RoomCreateEventContent::new_v11(),
|
||||||
| RoomVersionId::V3
|
|
||||||
| RoomVersionId::V4
|
|
||||||
| RoomVersionId::V5
|
|
||||||
| RoomVersionId::V6
|
|
||||||
| RoomVersionId::V7
|
|
||||||
| RoomVersionId::V8
|
|
||||||
| RoomVersionId::V9
|
|
||||||
| RoomVersionId::V10 => RoomCreateEventContent::new_v1(sender_user.clone()),
|
|
||||||
RoomVersionId::V11 => RoomCreateEventContent::new_v11(),
|
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Unexpected or unsupported room version {room_version}");
|
warn!("Unexpected or unsupported room version {room_version}");
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
@@ -199,7 +187,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 1. The room create event
|
// 1. The room create event
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -217,7 +205,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// 2. Let the room creator join
|
// 2. Let the room creator join
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -225,11 +213,11 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
event_type: TimelineEventType::RoomMember,
|
event_type: TimelineEventType::RoomMember,
|
||||||
content: to_raw_value(&RoomMemberEventContent {
|
content: to_raw_value(&RoomMemberEventContent {
|
||||||
membership: MembershipState::Join,
|
membership: MembershipState::Join,
|
||||||
displayname: services().users.displayname(sender_user)?,
|
displayname: services.users.displayname(sender_user)?,
|
||||||
avatar_url: services().users.avatar_url(sender_user)?,
|
avatar_url: services.users.avatar_url(sender_user)?,
|
||||||
is_direct: Some(body.is_direct),
|
is_direct: Some(body.is_direct),
|
||||||
third_party_invite: None,
|
third_party_invite: None,
|
||||||
blurhash: services().users.blurhash(sender_user)?,
|
blurhash: services.users.blurhash(sender_user)?,
|
||||||
reason: None,
|
reason: None,
|
||||||
join_authorized_via_users_server: None,
|
join_authorized_via_users_server: None,
|
||||||
})
|
})
|
||||||
@@ -264,7 +252,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
let power_levels_content =
|
let power_levels_content =
|
||||||
default_power_levels_content(&body.power_level_content_override, &body.visibility, users)?;
|
default_power_levels_content(&body.power_level_content_override, &body.visibility, users)?;
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -283,7 +271,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
|
|
||||||
// 4. Canonical room alias
|
// 4. Canonical room alias
|
||||||
if let Some(room_alias_id) = &alias {
|
if let Some(room_alias_id) = &alias {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -308,7 +296,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
// 5. Events set by preset
|
// 5. Events set by preset
|
||||||
|
|
||||||
// 5.1 Join Rules
|
// 5.1 Join Rules
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -331,7 +319,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// 5.2 History Visibility
|
// 5.2 History Visibility
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -350,7 +338,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// 5.3 Guest Access
|
// 5.3 Guest Access
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -393,11 +381,11 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
pdu_builder.state_key.get_or_insert_with(String::new);
|
pdu_builder.state_key.get_or_insert_with(String::new);
|
||||||
|
|
||||||
// Silently skip encryption events if they are not allowed
|
// Silently skip encryption events if they are not allowed
|
||||||
if pdu_builder.event_type == TimelineEventType::RoomEncryption && !services().globals.allow_encryption() {
|
if pdu_builder.event_type == TimelineEventType::RoomEncryption && !services.globals.allow_encryption() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock)
|
.build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock)
|
||||||
@@ -406,7 +394,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
|
|
||||||
// 7. Events implied by name and topic
|
// 7. Events implied by name and topic
|
||||||
if let Some(name) = &body.name {
|
if let Some(name) = &body.name {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -426,7 +414,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(topic) = &body.topic {
|
if let Some(topic) = &body.topic {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -450,21 +438,21 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
// 8. Events implied by invite (and TODO: invite_3pid)
|
// 8. Events implied by invite (and TODO: invite_3pid)
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
for user_id in &body.invite {
|
for user_id in &body.invite {
|
||||||
if let Err(e) = invite_helper(sender_user, user_id, &room_id, None, body.is_direct).await {
|
if let Err(e) = invite_helper(&services, sender_user, user_id, &room_id, None, body.is_direct).await {
|
||||||
warn!(%e, "Failed to send invite");
|
warn!(%e, "Failed to send invite");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Homeserver specific stuff
|
// Homeserver specific stuff
|
||||||
if let Some(alias) = alias {
|
if let Some(alias) = alias {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.set_alias(&alias, &room_id, sender_user)?;
|
.set_alias(&alias, &room_id, sender_user)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.visibility == room::Visibility::Public {
|
if body.visibility == room::Visibility::Public {
|
||||||
services().rooms.directory.set_public(&room_id)?;
|
services.rooms.directory.set_public(&room_id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("{sender_user} created a room with room ID {room_id}");
|
info!("{sender_user} created a room with room ID {room_id}");
|
||||||
@@ -479,11 +467,11 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
/// - You have to currently be joined to the room (TODO: Respect history
|
/// - You have to currently be joined to the room (TODO: Respect history
|
||||||
/// visibility)
|
/// visibility)
|
||||||
pub(crate) async fn get_room_event_route(
|
pub(crate) async fn get_room_event_route(
|
||||||
body: Ruma<get_room_event::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_room_event::v3::Request>,
|
||||||
) -> Result<get_room_event::v3::Response> {
|
) -> Result<get_room_event::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_pdu(&body.event_id)?
|
.get_pdu(&body.event_id)?
|
||||||
@@ -492,7 +480,7 @@ pub(crate) async fn get_room_event_route(
|
|||||||
Error::BadRequest(ErrorKind::NotFound, "Event not found.")
|
Error::BadRequest(ErrorKind::NotFound, "Event not found.")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !services()
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(sender_user, &event.room_id, &body.event_id)?
|
.user_can_see_event(sender_user, &event.room_id, &body.event_id)?
|
||||||
@@ -517,10 +505,12 @@ pub(crate) async fn get_room_event_route(
|
|||||||
///
|
///
|
||||||
/// - Only users joined to the room are allowed to call this, or if
|
/// - Only users joined to the room are allowed to call this, or if
|
||||||
/// `history_visibility` is world readable in the room
|
/// `history_visibility` is world readable in the room
|
||||||
pub(crate) async fn get_room_aliases_route(body: Ruma<aliases::v3::Request>) -> Result<aliases::v3::Response> {
|
pub(crate) async fn get_room_aliases_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<aliases::v3::Request>,
|
||||||
|
) -> Result<aliases::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services()
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(sender_user, &body.room_id)?
|
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||||
@@ -532,7 +522,7 @@ pub(crate) async fn get_room_aliases_route(body: Ruma<aliases::v3::Request>) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(aliases::v3::Response {
|
Ok(aliases::v3::Response {
|
||||||
aliases: services()
|
aliases: services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.local_aliases_for_room(&body.room_id)
|
.local_aliases_for_room(&body.room_id)
|
||||||
@@ -551,10 +541,12 @@ pub(crate) async fn get_room_aliases_route(body: Ruma<aliases::v3::Request>) ->
|
|||||||
/// - Transfers some state events
|
/// - Transfers some state events
|
||||||
/// - Moves local aliases
|
/// - Moves local aliases
|
||||||
/// - Modifies old room power levels to prevent users from speaking
|
/// - Modifies old room power levels to prevent users from speaking
|
||||||
pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) -> Result<upgrade_room::v3::Response> {
|
pub(crate) async fn upgrade_room_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<upgrade_room::v3::Request>,
|
||||||
|
) -> Result<upgrade_room::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services()
|
if !services
|
||||||
.globals
|
.globals
|
||||||
.supported_room_versions()
|
.supported_room_versions()
|
||||||
.contains(&body.new_version)
|
.contains(&body.new_version)
|
||||||
@@ -566,23 +558,19 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a replacement room
|
// Create a replacement room
|
||||||
let replacement_room = RoomId::new(services().globals.server_name());
|
let replacement_room = RoomId::new(services.globals.server_name());
|
||||||
|
|
||||||
let _short_id = services()
|
let _short_id = services
|
||||||
.rooms
|
.rooms
|
||||||
.short
|
.short
|
||||||
.get_or_create_shortroomid(&replacement_room)?;
|
.get_or_create_shortroomid(&replacement_room)?;
|
||||||
|
|
||||||
let state_lock = services()
|
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||||
.globals
|
|
||||||
.roomid_mutex_state
|
|
||||||
.lock(&body.room_id)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Send a m.room.tombstone event to the old room to indicate that it is not
|
// Send a m.room.tombstone event to the old room to indicate that it is not
|
||||||
// intended to be used any further Fail if the sender does not have the required
|
// intended to be used any further Fail if the sender does not have the required
|
||||||
// permissions
|
// permissions
|
||||||
let tombstone_event_id = services()
|
let tombstone_event_id = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -605,15 +593,11 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
|
|
||||||
// Change lock to replacement room
|
// Change lock to replacement room
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
let state_lock = services()
|
let state_lock = services.rooms.state.mutex.lock(&replacement_room).await;
|
||||||
.globals
|
|
||||||
.roomid_mutex_state
|
|
||||||
.lock(&replacement_room)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Get the old room creation event
|
// Get the old room creation event
|
||||||
let mut create_event_content = serde_json::from_str::<CanonicalJsonObject>(
|
let mut create_event_content = serde_json::from_str::<CanonicalJsonObject>(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&body.room_id, &StateEventType::RoomCreate, "")?
|
.room_state_get(&body.room_id, &StateEventType::RoomCreate, "")?
|
||||||
@@ -631,17 +615,10 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
|
|
||||||
// Send a m.room.create event containing a predecessor field and the applicable
|
// Send a m.room.create event containing a predecessor field and the applicable
|
||||||
// room_version
|
// room_version
|
||||||
|
{
|
||||||
|
use RoomVersionId::*;
|
||||||
match body.new_version {
|
match body.new_version {
|
||||||
RoomVersionId::V1
|
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||||
| RoomVersionId::V2
|
|
||||||
| RoomVersionId::V3
|
|
||||||
| RoomVersionId::V4
|
|
||||||
| RoomVersionId::V5
|
|
||||||
| RoomVersionId::V6
|
|
||||||
| RoomVersionId::V7
|
|
||||||
| RoomVersionId::V8
|
|
||||||
| RoomVersionId::V9
|
|
||||||
| RoomVersionId::V10 => {
|
|
||||||
create_event_content.insert(
|
create_event_content.insert(
|
||||||
"creator".into(),
|
"creator".into(),
|
||||||
json!(&sender_user).try_into().map_err(|e| {
|
json!(&sender_user).try_into().map_err(|e| {
|
||||||
@@ -650,7 +627,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
})?,
|
})?,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
RoomVersionId::V11 => {
|
V11 => {
|
||||||
// "creator" key no longer exists in V11 rooms
|
// "creator" key no longer exists in V11 rooms
|
||||||
create_event_content.remove("creator");
|
create_event_content.remove("creator");
|
||||||
},
|
},
|
||||||
@@ -662,6 +639,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
));
|
));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
create_event_content.insert(
|
create_event_content.insert(
|
||||||
"room_version".into(),
|
"room_version".into(),
|
||||||
@@ -687,7 +665,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
|
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -705,7 +683,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Join the new room
|
// Join the new room
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -713,11 +691,11 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
event_type: TimelineEventType::RoomMember,
|
event_type: TimelineEventType::RoomMember,
|
||||||
content: to_raw_value(&RoomMemberEventContent {
|
content: to_raw_value(&RoomMemberEventContent {
|
||||||
membership: MembershipState::Join,
|
membership: MembershipState::Join,
|
||||||
displayname: services().users.displayname(sender_user)?,
|
displayname: services.users.displayname(sender_user)?,
|
||||||
avatar_url: services().users.avatar_url(sender_user)?,
|
avatar_url: services.users.avatar_url(sender_user)?,
|
||||||
is_direct: None,
|
is_direct: None,
|
||||||
third_party_invite: None,
|
third_party_invite: None,
|
||||||
blurhash: services().users.blurhash(sender_user)?,
|
blurhash: services.users.blurhash(sender_user)?,
|
||||||
reason: None,
|
reason: None,
|
||||||
join_authorized_via_users_server: None,
|
join_authorized_via_users_server: None,
|
||||||
})
|
})
|
||||||
@@ -734,7 +712,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
|
|
||||||
// Replicate transferable state events to the new room
|
// Replicate transferable state events to the new room
|
||||||
for event_type in TRANSFERABLE_STATE_EVENTS {
|
for event_type in TRANSFERABLE_STATE_EVENTS {
|
||||||
let event_content = match services()
|
let event_content = match services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&body.room_id, event_type, "")?
|
.room_state_get(&body.room_id, event_type, "")?
|
||||||
@@ -743,7 +721,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
None => continue, // Skipping missing events.
|
None => continue, // Skipping missing events.
|
||||||
};
|
};
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -762,13 +740,13 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Moves any local aliases to the new room
|
// Moves any local aliases to the new room
|
||||||
for alias in services()
|
for alias in services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.local_aliases_for_room(&body.room_id)
|
.local_aliases_for_room(&body.room_id)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
{
|
{
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.set_alias(&alias, &replacement_room, sender_user)?;
|
.set_alias(&alias, &replacement_room, sender_user)?;
|
||||||
@@ -776,7 +754,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
|
|
||||||
// Get the old room power levels
|
// Get the old room power levels
|
||||||
let mut power_levels_event_content: RoomPowerLevelsEventContent = serde_json::from_str(
|
let mut power_levels_event_content: RoomPowerLevelsEventContent = serde_json::from_str(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&body.room_id, &StateEventType::RoomPowerLevels, "")?
|
.room_state_get(&body.room_id, &StateEventType::RoomPowerLevels, "")?
|
||||||
@@ -801,7 +779,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
|
|
||||||
// Modify the power levels in the old room to prevent sending of events and
|
// Modify the power levels in the old room to prevent sending of events and
|
||||||
// inviting new users
|
// inviting new users
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -870,7 +848,7 @@ fn default_power_levels_content(
|
|||||||
|
|
||||||
/// if a room is being created with a room alias, run our checks
|
/// if a room is being created with a room alias, run our checks
|
||||||
async fn room_alias_check(
|
async fn room_alias_check(
|
||||||
room_alias_name: &str, appservice_info: &Option<RegistrationInfo>,
|
services: &Services, room_alias_name: &str, appservice_info: &Option<RegistrationInfo>,
|
||||||
) -> Result<OwnedRoomAliasId> {
|
) -> Result<OwnedRoomAliasId> {
|
||||||
// Basic checks on the room alias validity
|
// Basic checks on the room alias validity
|
||||||
if room_alias_name.contains(':') {
|
if room_alias_name.contains(':') {
|
||||||
@@ -887,7 +865,7 @@ async fn room_alias_check(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if room alias is forbidden
|
// check if room alias is forbidden
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.forbidden_alias_names()
|
.forbidden_alias_names()
|
||||||
.is_match(room_alias_name)
|
.is_match(room_alias_name)
|
||||||
@@ -895,13 +873,13 @@ async fn room_alias_check(
|
|||||||
return Err(Error::BadRequest(ErrorKind::Unknown, "Room alias name is forbidden."));
|
return Err(Error::BadRequest(ErrorKind::Unknown, "Room alias name is forbidden."));
|
||||||
}
|
}
|
||||||
|
|
||||||
let full_room_alias = RoomAliasId::parse(format!("#{}:{}", room_alias_name, services().globals.config.server_name))
|
let full_room_alias = RoomAliasId::parse(format!("#{}:{}", room_alias_name, services.globals.config.server_name))
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
info!("Failed to parse room alias {room_alias_name}: {e}");
|
info!("Failed to parse room alias {room_alias_name}: {e}");
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Invalid room alias specified.")
|
Error::BadRequest(ErrorKind::InvalidParam, "Invalid room alias specified.")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if services()
|
if services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_local_alias(&full_room_alias)?
|
.resolve_local_alias(&full_room_alias)?
|
||||||
@@ -914,7 +892,7 @@ async fn room_alias_check(
|
|||||||
if !info.aliases.is_match(full_room_alias.as_str()) {
|
if !info.aliases.is_match(full_room_alias.as_str()) {
|
||||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
|
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
|
||||||
}
|
}
|
||||||
} else if services()
|
} else if services
|
||||||
.appservice
|
.appservice
|
||||||
.is_exclusive_alias(&full_room_alias)
|
.is_exclusive_alias(&full_room_alias)
|
||||||
.await
|
.await
|
||||||
@@ -928,9 +906,9 @@ async fn room_alias_check(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// if a room is being created with a custom room ID, run our checks against it
|
/// if a room is being created with a custom room ID, run our checks against it
|
||||||
fn custom_room_id_check(custom_room_id: &str) -> Result<OwnedRoomId> {
|
fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<OwnedRoomId> {
|
||||||
// apply forbidden room alias checks to custom room IDs too
|
// apply forbidden room alias checks to custom room IDs too
|
||||||
if services()
|
if services
|
||||||
.globals
|
.globals
|
||||||
.forbidden_alias_names()
|
.forbidden_alias_names()
|
||||||
.is_match(custom_room_id)
|
.is_match(custom_room_id)
|
||||||
@@ -951,7 +929,7 @@ fn custom_room_id_check(custom_room_id: &str) -> Result<OwnedRoomId> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let full_room_id = format!("!{}:{}", custom_room_id, services().globals.config.server_name);
|
let full_room_id = format!("!{}:{}", custom_room_id, services.globals.config.server_name);
|
||||||
|
|
||||||
debug_info!("Full custom room ID: {full_room_id}");
|
debug_info!("Full custom room ID: {full_room_id}");
|
||||||
|
|
||||||
|
|||||||
+13
-18
@@ -1,5 +1,6 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
@@ -14,7 +15,7 @@ use ruma::{
|
|||||||
};
|
};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/search`
|
/// # `POST /_matrix/client/r0/search`
|
||||||
///
|
///
|
||||||
@@ -22,7 +23,9 @@ use crate::{services, Error, Result, Ruma};
|
|||||||
///
|
///
|
||||||
/// - Only works if the user is currently joined to the room (TODO: Respect
|
/// - Only works if the user is currently joined to the room (TODO: Respect
|
||||||
/// history visibility)
|
/// history visibility)
|
||||||
pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>) -> Result<search_events::v3::Response> {
|
pub(crate) async fn search_events_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<search_events::v3::Request>,
|
||||||
|
) -> Result<search_events::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let search_criteria = body.search_categories.room_events.as_ref().unwrap();
|
let search_criteria = body.search_categories.room_events.as_ref().unwrap();
|
||||||
@@ -30,7 +33,7 @@ pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>)
|
|||||||
let include_state = &search_criteria.include_state;
|
let include_state = &search_criteria.include_state;
|
||||||
|
|
||||||
let room_ids = filter.rooms.clone().unwrap_or_else(|| {
|
let room_ids = filter.rooms.clone().unwrap_or_else(|| {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(sender_user)
|
.rooms_joined(sender_user)
|
||||||
@@ -50,11 +53,7 @@ pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>)
|
|||||||
|
|
||||||
if include_state.is_some_and(|include_state| include_state) {
|
if include_state.is_some_and(|include_state| include_state) {
|
||||||
for room_id in &room_ids {
|
for room_id in &room_ids {
|
||||||
if !services()
|
if !services.rooms.state_cache.is_joined(sender_user, room_id)? {
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.is_joined(sender_user, room_id)?
|
|
||||||
{
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::forbidden(),
|
ErrorKind::forbidden(),
|
||||||
"You don't have permission to view this room.",
|
"You don't have permission to view this room.",
|
||||||
@@ -62,12 +61,12 @@ pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if sender_user can see state events
|
// check if sender_user can see state events
|
||||||
if services()
|
if services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(sender_user, room_id)?
|
.user_can_see_state_events(sender_user, room_id)?
|
||||||
{
|
{
|
||||||
let room_state = services()
|
let room_state = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_full(room_id)
|
.room_state_full(room_id)
|
||||||
@@ -91,18 +90,14 @@ pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>)
|
|||||||
let mut searches = Vec::new();
|
let mut searches = Vec::new();
|
||||||
|
|
||||||
for room_id in &room_ids {
|
for room_id in &room_ids {
|
||||||
if !services()
|
if !services.rooms.state_cache.is_joined(sender_user, room_id)? {
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.is_joined(sender_user, room_id)?
|
|
||||||
{
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::forbidden(),
|
ErrorKind::forbidden(),
|
||||||
"You don't have permission to view this room.",
|
"You don't have permission to view this room.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(search) = services()
|
if let Some(search) = services
|
||||||
.rooms
|
.rooms
|
||||||
.search
|
.search
|
||||||
.search_pdus(room_id, &search_criteria.search_term)?
|
.search_pdus(room_id, &search_criteria.search_term)?
|
||||||
@@ -135,14 +130,14 @@ pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>)
|
|||||||
.iter()
|
.iter()
|
||||||
.skip(skip)
|
.skip(skip)
|
||||||
.filter_map(|result| {
|
.filter_map(|result| {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_pdu_from_id(result)
|
.get_pdu_from_id(result)
|
||||||
.ok()?
|
.ok()?
|
||||||
.filter(|pdu| {
|
.filter(|pdu| {
|
||||||
!pdu.is_redacted()
|
!pdu.is_redacted()
|
||||||
&& services()
|
&& services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(sender_user, &pdu.room_id, &pdu.event_id)
|
.user_can_see_event(sender_user, &pdu.room_id, &pdu.event_id)
|
||||||
|
|||||||
+26
-19
@@ -1,3 +1,4 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
@@ -20,7 +21,7 @@ use serde::Deserialize;
|
|||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
|
|
||||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||||
use crate::{services, utils, utils::hash, Error, Result, Ruma};
|
use crate::{utils, utils::hash, Error, Result, Ruma};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
@@ -55,7 +56,9 @@ pub(crate) async fn get_login_types_route(
|
|||||||
/// Note: You can use [`GET
|
/// Note: You can use [`GET
|
||||||
/// /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
|
/// /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
|
||||||
/// supported login types.
|
/// supported login types.
|
||||||
pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Response> {
|
pub(crate) async fn login_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<login::v3::Request>,
|
||||||
|
) -> Result<login::v3::Response> {
|
||||||
// Validate login method
|
// Validate login method
|
||||||
// TODO: Other login methods
|
// TODO: Other login methods
|
||||||
let user_id = match &body.login_info {
|
let user_id = match &body.login_info {
|
||||||
@@ -68,7 +71,7 @@ pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login:
|
|||||||
}) => {
|
}) => {
|
||||||
debug!("Got password login type");
|
debug!("Got password login type");
|
||||||
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||||
UserId::parse_with_server_name(user_id.to_lowercase(), services().globals.server_name())
|
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
|
||||||
} else if let Some(user) = user {
|
} else if let Some(user) = user {
|
||||||
UserId::parse(user)
|
UserId::parse(user)
|
||||||
} else {
|
} else {
|
||||||
@@ -77,7 +80,7 @@ pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login:
|
|||||||
}
|
}
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
||||||
|
|
||||||
let hash = services()
|
let hash = services
|
||||||
.users
|
.users
|
||||||
.password_hash(&user_id)?
|
.password_hash(&user_id)?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::forbidden(), "Wrong username or password."))?;
|
.ok_or(Error::BadRequest(ErrorKind::forbidden(), "Wrong username or password."))?;
|
||||||
@@ -96,7 +99,7 @@ pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login:
|
|||||||
token,
|
token,
|
||||||
}) => {
|
}) => {
|
||||||
debug!("Got token login type");
|
debug!("Got token login type");
|
||||||
if let Some(jwt_decoding_key) = services().globals.jwt_decoding_key() {
|
if let Some(jwt_decoding_key) = services.globals.jwt_decoding_key() {
|
||||||
let token =
|
let token =
|
||||||
jsonwebtoken::decode::<Claims>(token, jwt_decoding_key, &jsonwebtoken::Validation::default())
|
jsonwebtoken::decode::<Claims>(token, jwt_decoding_key, &jsonwebtoken::Validation::default())
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
@@ -106,7 +109,7 @@ pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login:
|
|||||||
|
|
||||||
let username = token.claims.sub.to_lowercase();
|
let username = token.claims.sub.to_lowercase();
|
||||||
|
|
||||||
UserId::parse_with_server_name(username, services().globals.server_name()).map_err(|e| {
|
UserId::parse_with_server_name(username, services.globals.server_name()).map_err(|e| {
|
||||||
warn!("Failed to parse username from user logging in: {e}");
|
warn!("Failed to parse username from user logging in: {e}");
|
||||||
Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.")
|
Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.")
|
||||||
})?
|
})?
|
||||||
@@ -124,7 +127,7 @@ pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login:
|
|||||||
}) => {
|
}) => {
|
||||||
debug!("Got appservice login type");
|
debug!("Got appservice login type");
|
||||||
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||||
UserId::parse_with_server_name(user_id.to_lowercase(), services().globals.server_name())
|
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
|
||||||
} else if let Some(user) = user {
|
} else if let Some(user) = user {
|
||||||
UserId::parse(user)
|
UserId::parse(user)
|
||||||
} else {
|
} else {
|
||||||
@@ -164,22 +167,22 @@ pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login:
|
|||||||
|
|
||||||
// Determine if device_id was provided and exists in the db for this user
|
// Determine if device_id was provided and exists in the db for this user
|
||||||
let device_exists = body.device_id.as_ref().map_or(false, |device_id| {
|
let device_exists = body.device_id.as_ref().map_or(false, |device_id| {
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.all_device_ids(&user_id)
|
.all_device_ids(&user_id)
|
||||||
.any(|x| x.as_ref().map_or(false, |v| v == device_id))
|
.any(|x| x.as_ref().map_or(false, |v| v == device_id))
|
||||||
});
|
});
|
||||||
|
|
||||||
if device_exists {
|
if device_exists {
|
||||||
services().users.set_token(&user_id, &device_id, &token)?;
|
services.users.set_token(&user_id, &device_id, &token)?;
|
||||||
} else {
|
} else {
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
|
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// send client well-known if specified so the client knows to reconfigure itself
|
// send client well-known if specified so the client knows to reconfigure itself
|
||||||
let client_discovery_info: Option<DiscoveryInfo> = services()
|
let client_discovery_info: Option<DiscoveryInfo> = services
|
||||||
.globals
|
.globals
|
||||||
.well_known_client()
|
.well_known_client()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -197,7 +200,7 @@ pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login:
|
|||||||
device_id,
|
device_id,
|
||||||
well_known: client_discovery_info,
|
well_known: client_discovery_info,
|
||||||
expires_in: None,
|
expires_in: None,
|
||||||
home_server: Some(services().globals.server_name().to_owned()),
|
home_server: Some(services.globals.server_name().to_owned()),
|
||||||
refresh_token: None,
|
refresh_token: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -211,14 +214,16 @@ pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login:
|
|||||||
/// last seen ts)
|
/// last seen ts)
|
||||||
/// - Forgets to-device events
|
/// - Forgets to-device events
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
pub(crate) async fn logout_route(body: Ruma<logout::v3::Request>) -> Result<logout::v3::Response> {
|
pub(crate) async fn logout_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<logout::v3::Request>,
|
||||||
|
) -> Result<logout::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
services().users.remove_device(sender_user, sender_device)?;
|
services.users.remove_device(sender_user, sender_device)?;
|
||||||
|
|
||||||
// send device list update for user after logout
|
// send device list update for user after logout
|
||||||
services().users.mark_device_key_update(sender_user)?;
|
services.users.mark_device_key_update(sender_user)?;
|
||||||
|
|
||||||
Ok(logout::v3::Response::new())
|
Ok(logout::v3::Response::new())
|
||||||
}
|
}
|
||||||
@@ -236,15 +241,17 @@ pub(crate) async fn logout_route(body: Ruma<logout::v3::Request>) -> Result<logo
|
|||||||
/// Note: This is equivalent to calling [`GET
|
/// Note: This is equivalent to calling [`GET
|
||||||
/// /_matrix/client/r0/logout`](fn.logout_route.html) from each device of this
|
/// /_matrix/client/r0/logout`](fn.logout_route.html) from each device of this
|
||||||
/// user.
|
/// user.
|
||||||
pub(crate) async fn logout_all_route(body: Ruma<logout_all::v3::Request>) -> Result<logout_all::v3::Response> {
|
pub(crate) async fn logout_all_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<logout_all::v3::Request>,
|
||||||
|
) -> Result<logout_all::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
for device_id in services().users.all_device_ids(sender_user).flatten() {
|
for device_id in services.users.all_device_ids(sender_user).flatten() {
|
||||||
services().users.remove_device(sender_user, &device_id)?;
|
services.users.remove_device(sender_user, &device_id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// send device list update for user after logout
|
// send device list update for user after logout
|
||||||
services().users.mark_device_key_update(sender_user)?;
|
services.users.mark_device_key_update(sender_user)?;
|
||||||
|
|
||||||
Ok(logout_all::v3::Response::new())
|
Ok(logout_all::v3::Response::new())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{error::ErrorKind, space::get_hierarchy},
|
api::client::{error::ErrorKind, space::get_hierarchy},
|
||||||
UInt,
|
UInt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{service::rooms::spaces::PaginationToken, services, Error, Result, Ruma};
|
use crate::{service::rooms::spaces::PaginationToken, Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`
|
/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`
|
||||||
///
|
///
|
||||||
/// Paginates over the space tree in a depth-first manner to locate child rooms
|
/// Paginates over the space tree in a depth-first manner to locate child rooms
|
||||||
/// of a given space.
|
/// of a given space.
|
||||||
pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>) -> Result<get_hierarchy::v1::Response> {
|
pub(crate) async fn get_hierarchy_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_hierarchy::v1::Request>,
|
||||||
|
) -> Result<get_hierarchy::v1::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let limit = body
|
let limit = body
|
||||||
@@ -39,7 +42,7 @@ pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.spaces
|
.spaces
|
||||||
.get_client_hierarchy(
|
.get_client_hierarchy(
|
||||||
@@ -47,7 +50,7 @@ pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>)
|
|||||||
&body.room_id,
|
&body.room_id,
|
||||||
limit.try_into().unwrap_or(10),
|
limit.try_into().unwrap_or(10),
|
||||||
key.map_or(vec![], |token| token.short_room_ids),
|
key.map_or(vec![], |token| token.short_room_ids),
|
||||||
max_depth.try_into().unwrap_or(3),
|
max_depth.into(),
|
||||||
body.suggested_only,
|
body.suggested_only,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
+36
-33
@@ -1,6 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use conduit::{error, warn};
|
use axum::extract::State;
|
||||||
|
use conduit::{debug_info, error, pdu::PduBuilder, Error, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
@@ -17,11 +18,9 @@ use ruma::{
|
|||||||
serde::Raw,
|
serde::Raw,
|
||||||
EventId, RoomId, UserId,
|
EventId, RoomId, UserId,
|
||||||
};
|
};
|
||||||
|
use service::Services;
|
||||||
|
|
||||||
use crate::{
|
use crate::{Ruma, RumaResponse};
|
||||||
service::{pdu::PduBuilder, server_is_ours},
|
|
||||||
services, Error, Result, Ruma, RumaResponse,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}`
|
/// # `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}`
|
||||||
///
|
///
|
||||||
@@ -32,22 +31,21 @@ use crate::{
|
|||||||
/// allowed
|
/// allowed
|
||||||
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||||
pub(crate) async fn send_state_event_for_key_route(
|
pub(crate) async fn send_state_event_for_key_route(
|
||||||
body: Ruma<send_state_event::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
|
||||||
) -> Result<send_state_event::v3::Response> {
|
) -> Result<send_state_event::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event_id = send_state_event_for_key_helper(
|
Ok(send_state_event::v3::Response {
|
||||||
|
event_id: send_state_event_for_key_helper(
|
||||||
|
&services,
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_type,
|
&body.event_type,
|
||||||
&body.body.body,
|
&body.body.body,
|
||||||
body.state_key.clone(),
|
body.state_key.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?
|
||||||
|
.into(),
|
||||||
let event_id = (*event_id).to_owned();
|
|
||||||
Ok(send_state_event::v3::Response {
|
|
||||||
event_id,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,9 +58,11 @@ pub(crate) async fn send_state_event_for_key_route(
|
|||||||
/// allowed
|
/// allowed
|
||||||
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||||
pub(crate) async fn send_state_event_for_empty_key_route(
|
pub(crate) async fn send_state_event_for_empty_key_route(
|
||||||
body: Ruma<send_state_event::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
|
||||||
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
||||||
send_state_event_for_key_route(body).await.map(RumaResponse)
|
send_state_event_for_key_route(State(services), body)
|
||||||
|
.await
|
||||||
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/v3/rooms/{roomid}/state`
|
/// # `GET /_matrix/client/v3/rooms/{roomid}/state`
|
||||||
@@ -72,11 +72,11 @@ pub(crate) async fn send_state_event_for_empty_key_route(
|
|||||||
/// - If not joined: Only works if current room history visibility is world
|
/// - If not joined: Only works if current room history visibility is world
|
||||||
/// readable
|
/// readable
|
||||||
pub(crate) async fn get_state_events_route(
|
pub(crate) async fn get_state_events_route(
|
||||||
body: Ruma<get_state_events::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_state_events::v3::Request>,
|
||||||
) -> Result<get_state_events::v3::Response> {
|
) -> Result<get_state_events::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services()
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(sender_user, &body.room_id)?
|
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||||
@@ -88,7 +88,7 @@ pub(crate) async fn get_state_events_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_state_events::v3::Response {
|
Ok(get_state_events::v3::Response {
|
||||||
room_state: services()
|
room_state: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_full(&body.room_id)
|
.room_state_full(&body.room_id)
|
||||||
@@ -108,11 +108,11 @@ pub(crate) async fn get_state_events_route(
|
|||||||
/// - If not joined: Only works if current room history visibility is world
|
/// - If not joined: Only works if current room history visibility is world
|
||||||
/// readable
|
/// readable
|
||||||
pub(crate) async fn get_state_events_for_key_route(
|
pub(crate) async fn get_state_events_for_key_route(
|
||||||
body: Ruma<get_state_events_for_key::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
|
||||||
) -> Result<get_state_events_for_key::v3::Response> {
|
) -> Result<get_state_events_for_key::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services()
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(sender_user, &body.room_id)?
|
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||||
@@ -123,12 +123,12 @@ pub(crate) async fn get_state_events_for_key_route(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&body.room_id, &body.event_type, &body.state_key)?
|
.room_state_get(&body.room_id, &body.event_type, &body.state_key)?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
warn!("State event {:?} not found in room {:?}", &body.event_type, &body.room_id);
|
debug_info!("State event {:?} not found in room {:?}", &body.event_type, &body.room_id);
|
||||||
Error::BadRequest(ErrorKind::NotFound, "State event not found.")
|
Error::BadRequest(ErrorKind::NotFound, "State event not found.")
|
||||||
})?;
|
})?;
|
||||||
if body
|
if body
|
||||||
@@ -163,17 +163,20 @@ pub(crate) async fn get_state_events_for_key_route(
|
|||||||
/// - If not joined: Only works if current room history visibility is world
|
/// - If not joined: Only works if current room history visibility is world
|
||||||
/// readable
|
/// readable
|
||||||
pub(crate) async fn get_state_events_for_empty_key_route(
|
pub(crate) async fn get_state_events_for_empty_key_route(
|
||||||
body: Ruma<get_state_events_for_key::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
|
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
|
||||||
get_state_events_for_key_route(body).await.map(RumaResponse)
|
get_state_events_for_key_route(State(services), body)
|
||||||
|
.await
|
||||||
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_state_event_for_key_helper(
|
async fn send_state_event_for_key_helper(
|
||||||
sender: &UserId, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>, state_key: String,
|
services: &Services, sender: &UserId, room_id: &RoomId, event_type: &StateEventType,
|
||||||
|
json: &Raw<AnyStateEventContent>, state_key: String,
|
||||||
) -> Result<Arc<EventId>> {
|
) -> Result<Arc<EventId>> {
|
||||||
allowed_to_send_state_event(room_id, event_type, json).await?;
|
allowed_to_send_state_event(services, room_id, event_type, json).await?;
|
||||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||||
let event_id = services()
|
let event_id = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
@@ -194,18 +197,18 @@ async fn send_state_event_for_key_helper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn allowed_to_send_state_event(
|
async fn allowed_to_send_state_event(
|
||||||
room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>,
|
services: &Services, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match event_type {
|
match event_type {
|
||||||
// Forbid m.room.encryption if encryption is disabled
|
// Forbid m.room.encryption if encryption is disabled
|
||||||
StateEventType::RoomEncryption => {
|
StateEventType::RoomEncryption => {
|
||||||
if !services().globals.allow_encryption() {
|
if !services.globals.allow_encryption() {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Encryption has been disabled"));
|
return Err(Error::BadRequest(ErrorKind::forbidden(), "Encryption has been disabled"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// admin room is a sensitive room, it should not ever be made public
|
// admin room is a sensitive room, it should not ever be made public
|
||||||
StateEventType::RoomJoinRules => {
|
StateEventType::RoomJoinRules => {
|
||||||
if let Some(admin_room_id) = service::admin::Service::get_admin_room()? {
|
if let Some(admin_room_id) = services.admin.get_admin_room()? {
|
||||||
if admin_room_id == room_id {
|
if admin_room_id == room_id {
|
||||||
if let Ok(join_rule) = serde_json::from_str::<RoomJoinRulesEventContent>(json.json().get()) {
|
if let Ok(join_rule) = serde_json::from_str::<RoomJoinRulesEventContent>(json.json().get()) {
|
||||||
if join_rule.join_rule == JoinRule::Public {
|
if join_rule.join_rule == JoinRule::Public {
|
||||||
@@ -220,7 +223,7 @@ async fn allowed_to_send_state_event(
|
|||||||
},
|
},
|
||||||
// admin room is a sensitive room, it should not ever be made world readable
|
// admin room is a sensitive room, it should not ever be made world readable
|
||||||
StateEventType::RoomHistoryVisibility => {
|
StateEventType::RoomHistoryVisibility => {
|
||||||
if let Some(admin_room_id) = service::admin::Service::get_admin_room()? {
|
if let Some(admin_room_id) = services.admin.get_admin_room()? {
|
||||||
if admin_room_id == room_id {
|
if admin_room_id == room_id {
|
||||||
if let Ok(visibility_content) =
|
if let Ok(visibility_content) =
|
||||||
serde_json::from_str::<RoomHistoryVisibilityEventContent>(json.json().get())
|
serde_json::from_str::<RoomHistoryVisibilityEventContent>(json.json().get())
|
||||||
@@ -245,8 +248,8 @@ async fn allowed_to_send_state_event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for alias in aliases {
|
for alias in aliases {
|
||||||
if !server_is_ours(alias.server_name())
|
if !services.globals.server_is_ours(alias.server_name())
|
||||||
|| services()
|
|| services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_local_alias(&alias)?
|
.resolve_local_alias(&alias)?
|
||||||
|
|||||||
+172
-167
File diff suppressed because it is too large
Load Diff
+16
-9
@@ -1,5 +1,6 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::tag::{create_tag, delete_tag, get_tags},
|
api::client::tag::{create_tag, delete_tag, get_tags},
|
||||||
events::{
|
events::{
|
||||||
@@ -8,17 +9,19 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}`
|
/// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}`
|
||||||
///
|
///
|
||||||
/// Adds a tag to the room.
|
/// Adds a tag to the room.
|
||||||
///
|
///
|
||||||
/// - Inserts the tag into the tag event of the room account data.
|
/// - Inserts the tag into the tag event of the room account data.
|
||||||
pub(crate) async fn update_tag_route(body: Ruma<create_tag::v3::Request>) -> Result<create_tag::v3::Response> {
|
pub(crate) async fn update_tag_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<create_tag::v3::Request>,
|
||||||
|
) -> Result<create_tag::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
|
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
|
||||||
|
|
||||||
@@ -38,7 +41,7 @@ pub(crate) async fn update_tag_route(body: Ruma<create_tag::v3::Request>) -> Res
|
|||||||
.tags
|
.tags
|
||||||
.insert(body.tag.clone().into(), body.tag_info.clone());
|
.insert(body.tag.clone().into(), body.tag_info.clone());
|
||||||
|
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
Some(&body.room_id),
|
Some(&body.room_id),
|
||||||
sender_user,
|
sender_user,
|
||||||
RoomAccountDataEventType::Tag,
|
RoomAccountDataEventType::Tag,
|
||||||
@@ -53,10 +56,12 @@ pub(crate) async fn update_tag_route(body: Ruma<create_tag::v3::Request>) -> Res
|
|||||||
/// Deletes a tag from the room.
|
/// Deletes a tag from the room.
|
||||||
///
|
///
|
||||||
/// - Removes the tag from the tag event of the room account data.
|
/// - Removes the tag from the tag event of the room account data.
|
||||||
pub(crate) async fn delete_tag_route(body: Ruma<delete_tag::v3::Request>) -> Result<delete_tag::v3::Response> {
|
pub(crate) async fn delete_tag_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<delete_tag::v3::Request>,
|
||||||
|
) -> Result<delete_tag::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
|
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
|
||||||
|
|
||||||
@@ -73,7 +78,7 @@ pub(crate) async fn delete_tag_route(body: Ruma<delete_tag::v3::Request>) -> Res
|
|||||||
|
|
||||||
tags_event.content.tags.remove(&body.tag.clone().into());
|
tags_event.content.tags.remove(&body.tag.clone().into());
|
||||||
|
|
||||||
services().account_data.update(
|
services.account_data.update(
|
||||||
Some(&body.room_id),
|
Some(&body.room_id),
|
||||||
sender_user,
|
sender_user,
|
||||||
RoomAccountDataEventType::Tag,
|
RoomAccountDataEventType::Tag,
|
||||||
@@ -88,10 +93,12 @@ pub(crate) async fn delete_tag_route(body: Ruma<delete_tag::v3::Request>) -> Res
|
|||||||
/// Returns tags on the room.
|
/// Returns tags on the room.
|
||||||
///
|
///
|
||||||
/// - Gets the tag event of the room account data.
|
/// - Gets the tag event of the room account data.
|
||||||
pub(crate) async fn get_tags_route(body: Ruma<get_tags::v3::Request>) -> Result<get_tags::v3::Response> {
|
pub(crate) async fn get_tags_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_tags::v3::Request>,
|
||||||
|
) -> Result<get_tags::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event = services()
|
let event = services
|
||||||
.account_data
|
.account_data
|
||||||
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
|
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use ruma::api::client::thirdparty::get_protocols;
|
use ruma::api::client::thirdparty::get_protocols;
|
||||||
|
|
||||||
use crate::{Result, Ruma};
|
use crate::{Result, Ruma, RumaResponse};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/thirdparty/protocols`
|
/// # `GET /_matrix/client/r0/thirdparty/protocols`
|
||||||
///
|
///
|
||||||
@@ -15,3 +15,13 @@ pub(crate) async fn get_protocols_route(
|
|||||||
protocols: BTreeMap::new(),
|
protocols: BTreeMap::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # `GET /_matrix/client/unstable/thirdparty/protocols`
|
||||||
|
///
|
||||||
|
/// Same as `get_protocols_route`, except for some reason Element Android legacy
|
||||||
|
/// calls this
|
||||||
|
pub(crate) async fn get_protocols_route_unstable(
|
||||||
|
body: Ruma<get_protocols::v3::Request>,
|
||||||
|
) -> Result<RumaResponse<get_protocols::v3::Response>> {
|
||||||
|
get_protocols_route(body).await.map(RumaResponse)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{error::ErrorKind, threads::get_threads},
|
api::client::{error::ErrorKind, threads::get_threads},
|
||||||
uint,
|
uint,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/threads`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/threads`
|
||||||
pub(crate) async fn get_threads_route(body: Ruma<get_threads::v1::Request>) -> Result<get_threads::v1::Response> {
|
pub(crate) async fn get_threads_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<get_threads::v1::Request>,
|
||||||
|
) -> Result<get_threads::v1::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
// Use limit or else 10, with maximum 100
|
// Use limit or else 10, with maximum 100
|
||||||
@@ -24,14 +27,14 @@ pub(crate) async fn get_threads_route(body: Ruma<get_threads::v1::Request>) -> R
|
|||||||
u64::MAX
|
u64::MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
let threads = services()
|
let threads = services
|
||||||
.rooms
|
.rooms
|
||||||
.threads
|
.threads
|
||||||
.threads_until(sender_user, &body.room_id, from, &body.include)?
|
.threads_until(sender_user, &body.room_id, from, &body.include)?
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.filter(|(_, pdu)| {
|
.filter(|(_, pdu)| {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(sender_user, &body.room_id, &pdu.event_id)
|
.user_can_see_event(sender_user, &body.room_id, &pdu.event_id)
|
||||||
|
|||||||
+12
-10
@@ -1,5 +1,7 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
|
use conduit::{Error, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::{error::ErrorKind, to_device::send_event_to_device},
|
client::{error::ErrorKind, to_device::send_event_to_device},
|
||||||
@@ -8,19 +10,19 @@ use ruma::{
|
|||||||
to_device::DeviceIdOrAllDevices,
|
to_device::DeviceIdOrAllDevices,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, user_is_local, Error, Result, Ruma};
|
use crate::Ruma;
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}`
|
/// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}`
|
||||||
///
|
///
|
||||||
/// Send a to-device event to a set of client devices.
|
/// Send a to-device event to a set of client devices.
|
||||||
pub(crate) async fn send_event_to_device_route(
|
pub(crate) async fn send_event_to_device_route(
|
||||||
body: Ruma<send_event_to_device::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<send_event_to_device::v3::Request>,
|
||||||
) -> Result<send_event_to_device::v3::Response> {
|
) -> Result<send_event_to_device::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_deref();
|
let sender_device = body.sender_device.as_deref();
|
||||||
|
|
||||||
// Check if this is a new transaction id
|
// Check if this is a new transaction id
|
||||||
if services()
|
if services
|
||||||
.transaction_ids
|
.transaction_ids
|
||||||
.existing_txnid(sender_user, sender_device, &body.txn_id)?
|
.existing_txnid(sender_user, sender_device, &body.txn_id)?
|
||||||
.is_some()
|
.is_some()
|
||||||
@@ -30,14 +32,14 @@ pub(crate) async fn send_event_to_device_route(
|
|||||||
|
|
||||||
for (target_user_id, map) in &body.messages {
|
for (target_user_id, map) in &body.messages {
|
||||||
for (target_device_id_maybe, event) in map {
|
for (target_device_id_maybe, event) in map {
|
||||||
if !user_is_local(target_user_id) {
|
if !services.globals.user_is_local(target_user_id) {
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
map.insert(target_device_id_maybe.clone(), event.clone());
|
map.insert(target_device_id_maybe.clone(), event.clone());
|
||||||
let mut messages = BTreeMap::new();
|
let mut messages = BTreeMap::new();
|
||||||
messages.insert(target_user_id.clone(), map);
|
messages.insert(target_user_id.clone(), map);
|
||||||
let count = services().globals.next_count()?;
|
let count = services.globals.next_count()?;
|
||||||
|
|
||||||
services().sending.send_edu_server(
|
services.sending.send_edu_server(
|
||||||
target_user_id.server_name(),
|
target_user_id.server_name(),
|
||||||
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(DirectDeviceContent {
|
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(DirectDeviceContent {
|
||||||
sender: sender_user.clone(),
|
sender: sender_user.clone(),
|
||||||
@@ -53,7 +55,7 @@ pub(crate) async fn send_event_to_device_route(
|
|||||||
|
|
||||||
match target_device_id_maybe {
|
match target_device_id_maybe {
|
||||||
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||||
services().users.add_to_device_event(
|
services.users.add_to_device_event(
|
||||||
sender_user,
|
sender_user,
|
||||||
target_user_id,
|
target_user_id,
|
||||||
target_device_id,
|
target_device_id,
|
||||||
@@ -65,8 +67,8 @@ pub(crate) async fn send_event_to_device_route(
|
|||||||
},
|
},
|
||||||
|
|
||||||
DeviceIdOrAllDevices::AllDevices => {
|
DeviceIdOrAllDevices::AllDevices => {
|
||||||
for target_device_id in services().users.all_device_ids(target_user_id) {
|
for target_device_id in services.users.all_device_ids(target_user_id) {
|
||||||
services().users.add_to_device_event(
|
services.users.add_to_device_event(
|
||||||
sender_user,
|
sender_user,
|
||||||
target_user_id,
|
target_user_id,
|
||||||
&target_device_id?,
|
&target_device_id?,
|
||||||
@@ -82,7 +84,7 @@ pub(crate) async fn send_event_to_device_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save transaction id with empty data
|
// Save transaction id with empty data
|
||||||
services()
|
services
|
||||||
.transaction_ids
|
.transaction_ids
|
||||||
.add_txnid(sender_user, sender_device, &body.txn_id, &[])?;
|
.add_txnid(sender_user, sender_device, &body.txn_id, &[])?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::api::client::{error::ErrorKind, typing::create_typing_event};
|
use ruma::api::client::{error::ErrorKind, typing::create_typing_event};
|
||||||
|
|
||||||
use crate::{services, utils, Error, Result, Ruma};
|
use crate::{utils, Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}`
|
/// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}`
|
||||||
///
|
///
|
||||||
/// Sets the typing state of the sender user.
|
/// Sets the typing state of the sender user.
|
||||||
pub(crate) async fn create_typing_event_route(
|
pub(crate) async fn create_typing_event_route(
|
||||||
body: Ruma<create_typing_event::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<create_typing_event::v3::Request>,
|
||||||
) -> Result<create_typing_event::v3::Response> {
|
) -> Result<create_typing_event::v3::Response> {
|
||||||
use create_typing_event::v3::Typing;
|
use create_typing_event::v3::Typing;
|
||||||
|
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services()
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.is_joined(sender_user, &body.room_id)?
|
.is_joined(sender_user, &body.room_id)?
|
||||||
@@ -23,20 +24,20 @@ pub(crate) async fn create_typing_event_route(
|
|||||||
if let Typing::Yes(duration) = body.state {
|
if let Typing::Yes(duration) = body.state {
|
||||||
let duration = utils::clamp(
|
let duration = utils::clamp(
|
||||||
duration.as_millis().try_into().unwrap_or(u64::MAX),
|
duration.as_millis().try_into().unwrap_or(u64::MAX),
|
||||||
services()
|
services
|
||||||
.globals
|
.globals
|
||||||
.config
|
.config
|
||||||
.typing_client_timeout_min_s
|
.typing_client_timeout_min_s
|
||||||
.checked_mul(1000)
|
.checked_mul(1000)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
services()
|
services
|
||||||
.globals
|
.globals
|
||||||
.config
|
.config
|
||||||
.typing_client_timeout_max_s
|
.typing_client_timeout_max_s
|
||||||
.checked_mul(1000)
|
.checked_mul(1000)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.typing
|
.typing
|
||||||
.typing_add(
|
.typing_add(
|
||||||
@@ -48,7 +49,7 @@ pub(crate) async fn create_typing_event_route(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.typing
|
.typing
|
||||||
.typing_remove(sender_user, &body.room_id)
|
.typing_remove(sender_user, &body.room_id)
|
||||||
|
|||||||
+27
-27
@@ -1,12 +1,13 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduit::{warn, RumaResponse};
|
use conduit::warn;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{error::ErrorKind, membership::mutual_rooms, room::get_summary},
|
api::client::{error::ErrorKind, membership::mutual_rooms, room::get_summary},
|
||||||
events::room::member::MembershipState,
|
events::room::member::MembershipState,
|
||||||
OwnedRoomId,
|
OwnedRoomId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma, RumaResponse};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`
|
/// # `GET /_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`
|
||||||
///
|
///
|
||||||
@@ -17,7 +18,8 @@ use crate::{services, Error, Result, Ruma};
|
|||||||
/// An implementation of [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
|
/// An implementation of [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "mutual_rooms")]
|
#[tracing::instrument(skip_all, fields(%client), name = "mutual_rooms")]
|
||||||
pub(crate) async fn get_mutual_rooms_route(
|
pub(crate) async fn get_mutual_rooms_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<mutual_rooms::unstable::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<mutual_rooms::unstable::Request>,
|
||||||
) -> Result<mutual_rooms::unstable::Response> {
|
) -> Result<mutual_rooms::unstable::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
@@ -28,14 +30,14 @@ pub(crate) async fn get_mutual_rooms_route(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().users.exists(&body.user_id)? {
|
if !services.users.exists(&body.user_id)? {
|
||||||
return Ok(mutual_rooms::unstable::Response {
|
return Ok(mutual_rooms::unstable::Response {
|
||||||
joined: vec![],
|
joined: vec![],
|
||||||
next_batch_token: None,
|
next_batch_token: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mutual_rooms: Vec<OwnedRoomId> = services()
|
let mutual_rooms: Vec<OwnedRoomId> = services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])?
|
.get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])?
|
||||||
@@ -58,9 +60,10 @@ pub(crate) async fn get_mutual_rooms_route(
|
|||||||
///
|
///
|
||||||
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
||||||
pub(crate) async fn get_room_summary_legacy(
|
pub(crate) async fn get_room_summary_legacy(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_summary::msc3266::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_summary::msc3266::Request>,
|
||||||
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
|
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
|
||||||
get_room_summary(InsecureClientIp(client), body)
|
get_room_summary(State(services), InsecureClientIp(client), body)
|
||||||
.await
|
.await
|
||||||
.map(RumaResponse)
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
@@ -74,22 +77,19 @@ pub(crate) async fn get_room_summary_legacy(
|
|||||||
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
|
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
|
||||||
pub(crate) async fn get_room_summary(
|
pub(crate) async fn get_room_summary(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_summary::msc3266::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_summary::msc3266::Request>,
|
||||||
) -> Result<get_summary::msc3266::Response> {
|
) -> Result<get_summary::msc3266::Response> {
|
||||||
let sender_user = body.sender_user.as_ref();
|
let sender_user = body.sender_user.as_ref();
|
||||||
|
|
||||||
let room_id = services()
|
let room_id = services.rooms.alias.resolve(&body.room_id_or_alias).await?;
|
||||||
.rooms
|
|
||||||
.alias
|
|
||||||
.resolve(&body.room_id_or_alias)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if !services().rooms.metadata.exists(&room_id)? {
|
if !services.rooms.metadata.exists(&room_id)? {
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room is unknown to this server"));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Room is unknown to this server"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if sender_user.is_none()
|
if sender_user.is_none()
|
||||||
&& !services()
|
&& !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.is_world_readable(&room_id)
|
.is_world_readable(&room_id)
|
||||||
@@ -103,25 +103,25 @@ pub(crate) async fn get_room_summary(
|
|||||||
|
|
||||||
Ok(get_summary::msc3266::Response {
|
Ok(get_summary::msc3266::Response {
|
||||||
room_id: room_id.clone(),
|
room_id: room_id.clone(),
|
||||||
canonical_alias: services()
|
canonical_alias: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_canonical_alias(&room_id)
|
.get_canonical_alias(&room_id)
|
||||||
.unwrap_or(None),
|
.unwrap_or(None),
|
||||||
avatar_url: services()
|
avatar_url: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_avatar(&room_id)?
|
.get_avatar(&room_id)?
|
||||||
.into_option()
|
.into_option()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.url,
|
.url,
|
||||||
guest_can_join: services().rooms.state_accessor.guest_can_join(&room_id)?,
|
guest_can_join: services.rooms.state_accessor.guest_can_join(&room_id)?,
|
||||||
name: services()
|
name: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_name(&room_id)
|
.get_name(&room_id)
|
||||||
.unwrap_or(None),
|
.unwrap_or(None),
|
||||||
num_joined_members: services()
|
num_joined_members: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_joined_count(&room_id)
|
.room_joined_count(&room_id)
|
||||||
@@ -132,21 +132,21 @@ pub(crate) async fn get_room_summary(
|
|||||||
})
|
})
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("user count should not be that big"),
|
.expect("user count should not be that big"),
|
||||||
topic: services()
|
topic: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_room_topic(&room_id)
|
.get_room_topic(&room_id)
|
||||||
.unwrap_or(None),
|
.unwrap_or(None),
|
||||||
world_readable: services()
|
world_readable: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.is_world_readable(&room_id)
|
.is_world_readable(&room_id)
|
||||||
.unwrap_or(false),
|
.unwrap_or(false),
|
||||||
join_rule: services().rooms.state_accessor.get_join_rule(&room_id)?.0,
|
join_rule: services.rooms.state_accessor.get_join_rule(&room_id)?.0,
|
||||||
room_type: services().rooms.state_accessor.get_room_type(&room_id)?,
|
room_type: services.rooms.state_accessor.get_room_type(&room_id)?,
|
||||||
room_version: Some(services().rooms.state.get_room_version(&room_id)?),
|
room_version: Some(services.rooms.state.get_room_version(&room_id)?),
|
||||||
membership: if let Some(sender_user) = sender_user {
|
membership: if let Some(sender_user) = sender_user {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_member(&room_id, sender_user)?
|
.get_member(&room_id, sender_user)?
|
||||||
@@ -154,7 +154,7 @@ pub(crate) async fn get_room_summary(
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
encryption: services()
|
encryption: services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_room_encryption(&room_id)
|
.get_room_encryption(&room_id)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use axum::{response::IntoResponse, Json};
|
use axum::{extract::State, response::IntoResponse, Json};
|
||||||
use ruma::api::client::{
|
use ruma::api::client::{
|
||||||
discovery::{
|
discovery::{
|
||||||
discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo},
|
discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo},
|
||||||
@@ -10,7 +10,7 @@ use ruma::api::client::{
|
|||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Error, Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/versions`
|
/// # `GET /_matrix/client/versions`
|
||||||
///
|
///
|
||||||
@@ -62,9 +62,9 @@ pub(crate) async fn get_supported_versions_route(
|
|||||||
///
|
///
|
||||||
/// Returns the .well-known URL if it is configured, otherwise returns 404.
|
/// Returns the .well-known URL if it is configured, otherwise returns 404.
|
||||||
pub(crate) async fn well_known_client(
|
pub(crate) async fn well_known_client(
|
||||||
_body: Ruma<discover_homeserver::Request>,
|
State(services): State<crate::State>, _body: Ruma<discover_homeserver::Request>,
|
||||||
) -> Result<discover_homeserver::Response> {
|
) -> Result<discover_homeserver::Response> {
|
||||||
let client_url = match services().globals.well_known_client() {
|
let client_url = match services.globals.well_known_client() {
|
||||||
Some(url) => url.to_string(),
|
Some(url) => url.to_string(),
|
||||||
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||||
};
|
};
|
||||||
@@ -84,22 +84,24 @@ pub(crate) async fn well_known_client(
|
|||||||
/// # `GET /.well-known/matrix/support`
|
/// # `GET /.well-known/matrix/support`
|
||||||
///
|
///
|
||||||
/// Server support contact and support page of a homeserver's domain.
|
/// Server support contact and support page of a homeserver's domain.
|
||||||
pub(crate) async fn well_known_support(_body: Ruma<discover_support::Request>) -> Result<discover_support::Response> {
|
pub(crate) async fn well_known_support(
|
||||||
let support_page = services()
|
State(services): State<crate::State>, _body: Ruma<discover_support::Request>,
|
||||||
|
) -> Result<discover_support::Response> {
|
||||||
|
let support_page = services
|
||||||
.globals
|
.globals
|
||||||
.well_known_support_page()
|
.well_known_support_page()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(ToString::to_string);
|
.map(ToString::to_string);
|
||||||
|
|
||||||
let role = services().globals.well_known_support_role().clone();
|
let role = services.globals.well_known_support_role().clone();
|
||||||
|
|
||||||
// support page or role must be either defined for this to be valid
|
// support page or role must be either defined for this to be valid
|
||||||
if support_page.is_none() && role.is_none() {
|
if support_page.is_none() && role.is_none() {
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Not found."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
let email_address = services().globals.well_known_support_email().clone();
|
let email_address = services.globals.well_known_support_email().clone();
|
||||||
let matrix_id = services().globals.well_known_support_mxid().clone();
|
let matrix_id = services.globals.well_known_support_mxid().clone();
|
||||||
|
|
||||||
// if a role is specified, an email address or matrix id is required
|
// if a role is specified, an email address or matrix id is required
|
||||||
if role.is_some() && (email_address.is_none() && matrix_id.is_none()) {
|
if role.is_some() && (email_address.is_none() && matrix_id.is_none()) {
|
||||||
@@ -134,10 +136,10 @@ pub(crate) async fn well_known_support(_body: Ruma<discover_support::Request>) -
|
|||||||
///
|
///
|
||||||
/// Endpoint provided by sliding sync proxy used by some clients such as Element
|
/// Endpoint provided by sliding sync proxy used by some clients such as Element
|
||||||
/// Web as a non-standard health check.
|
/// Web as a non-standard health check.
|
||||||
pub(crate) async fn syncv3_client_server_json() -> Result<impl IntoResponse> {
|
pub(crate) async fn syncv3_client_server_json(State(services): State<crate::State>) -> Result<impl IntoResponse> {
|
||||||
let server_url = match services().globals.well_known_client() {
|
let server_url = match services.globals.well_known_client() {
|
||||||
Some(url) => url.to_string(),
|
Some(url) => url.to_string(),
|
||||||
None => match services().globals.well_known_server() {
|
None => match services.globals.well_known_server() {
|
||||||
Some(url) => url.to_string(),
|
Some(url) => url.to_string(),
|
||||||
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||||
},
|
},
|
||||||
@@ -165,8 +167,8 @@ pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
|
|||||||
/// conduwuit-specific API to return the amount of users registered on this
|
/// conduwuit-specific API to return the amount of users registered on this
|
||||||
/// homeserver. Endpoint is disabled if federation is disabled for privacy. This
|
/// homeserver. Endpoint is disabled if federation is disabled for privacy. This
|
||||||
/// only includes active users (not deactivated, no guests, etc)
|
/// only includes active users (not deactivated, no guests, etc)
|
||||||
pub(crate) async fn conduwuit_local_user_count() -> Result<impl IntoResponse> {
|
pub(crate) async fn conduwuit_local_user_count(State(services): State<crate::State>) -> Result<impl IntoResponse> {
|
||||||
let user_count = services().users.list_local_users()?.len();
|
let user_count = services.users.list_local_users()?.len();
|
||||||
|
|
||||||
Ok(Json(serde_json::json!({
|
Ok(Json(serde_json::json!({
|
||||||
"count": user_count
|
"count": user_count
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::user_directory::search_users,
|
api::client::user_directory::search_users,
|
||||||
events::{
|
events::{
|
||||||
@@ -6,7 +7,7 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Result, Ruma};
|
use crate::{Result, Ruma};
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/user_directory/search`
|
/// # `POST /_matrix/client/r0/user_directory/search`
|
||||||
///
|
///
|
||||||
@@ -14,18 +15,20 @@ use crate::{services, Result, Ruma};
|
|||||||
///
|
///
|
||||||
/// - Hides any local users that aren't in any public rooms (i.e. those that
|
/// - Hides any local users that aren't in any public rooms (i.e. those that
|
||||||
/// have the join rule set to public) and don't share a room with the sender
|
/// have the join rule set to public) and don't share a room with the sender
|
||||||
pub(crate) async fn search_users_route(body: Ruma<search_users::v3::Request>) -> Result<search_users::v3::Response> {
|
pub(crate) async fn search_users_route(
|
||||||
|
State(services): State<crate::State>, body: Ruma<search_users::v3::Request>,
|
||||||
|
) -> Result<search_users::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let limit = usize::try_from(body.limit).unwrap_or(10); // default limit is 10
|
let limit = usize::try_from(body.limit).unwrap_or(10); // default limit is 10
|
||||||
|
|
||||||
let mut users = services().users.iter().filter_map(|user_id| {
|
let mut users = services.users.iter().filter_map(|user_id| {
|
||||||
// Filter out buggy users (they should not exist, but you never know...)
|
// Filter out buggy users (they should not exist, but you never know...)
|
||||||
let user_id = user_id.ok()?;
|
let user_id = user_id.ok()?;
|
||||||
|
|
||||||
let user = search_users::v3::User {
|
let user = search_users::v3::User {
|
||||||
user_id: user_id.clone(),
|
user_id: user_id.clone(),
|
||||||
display_name: services().users.displayname(&user_id).ok()?,
|
display_name: services.users.displayname(&user_id).ok()?,
|
||||||
avatar_url: services().users.avatar_url(&user_id).ok()?,
|
avatar_url: services.users.avatar_url(&user_id).ok()?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_id_matches = user
|
let user_id_matches = user
|
||||||
@@ -50,13 +53,13 @@ pub(crate) async fn search_users_route(body: Ruma<search_users::v3::Request>) ->
|
|||||||
// It's a matching user, but is the sender allowed to see them?
|
// It's a matching user, but is the sender allowed to see them?
|
||||||
let mut user_visible = false;
|
let mut user_visible = false;
|
||||||
|
|
||||||
let user_is_in_public_rooms = services()
|
let user_is_in_public_rooms = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(&user_id)
|
.rooms_joined(&user_id)
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.any(|room| {
|
.any(|room| {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&room, &StateEventType::RoomJoinRules, "")
|
.room_state_get(&room, &StateEventType::RoomJoinRules, "")
|
||||||
@@ -71,7 +74,7 @@ pub(crate) async fn search_users_route(body: Ruma<search_users::v3::Request>) ->
|
|||||||
if user_is_in_public_rooms {
|
if user_is_in_public_rooms {
|
||||||
user_visible = true;
|
user_visible = true;
|
||||||
} else {
|
} else {
|
||||||
let user_is_in_shared_rooms = services()
|
let user_is_in_shared_rooms = services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_shared_rooms(vec![sender_user.clone(), user_id])
|
.get_shared_rooms(vec![sender_user.clone(), user_id])
|
||||||
|
|||||||
+10
-9
@@ -1,12 +1,13 @@
|
|||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
use conduit::utils;
|
use conduit::utils;
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch, UserId};
|
use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch, UserId};
|
||||||
use sha1::Sha1;
|
use sha1::Sha1;
|
||||||
|
|
||||||
use crate::{services, Result, Ruma};
|
use crate::{Result, Ruma};
|
||||||
|
|
||||||
const RANDOM_USER_ID_LENGTH: usize = 10;
|
const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||||
|
|
||||||
@@ -16,14 +17,14 @@ type HmacSha1 = Hmac<Sha1>;
|
|||||||
///
|
///
|
||||||
/// TODO: Returns information about the recommended turn server.
|
/// TODO: Returns information about the recommended turn server.
|
||||||
pub(crate) async fn turn_server_route(
|
pub(crate) async fn turn_server_route(
|
||||||
body: Ruma<get_turn_server_info::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<get_turn_server_info::v3::Request>,
|
||||||
) -> Result<get_turn_server_info::v3::Response> {
|
) -> Result<get_turn_server_info::v3::Response> {
|
||||||
let turn_secret = services().globals.turn_secret().clone();
|
let turn_secret = services.globals.turn_secret().clone();
|
||||||
|
|
||||||
let (username, password) = if !turn_secret.is_empty() {
|
let (username, password) = if !turn_secret.is_empty() {
|
||||||
let expiry = SecondsSinceUnixEpoch::from_system_time(
|
let expiry = SecondsSinceUnixEpoch::from_system_time(
|
||||||
SystemTime::now()
|
SystemTime::now()
|
||||||
.checked_add(Duration::from_secs(services().globals.turn_ttl()))
|
.checked_add(Duration::from_secs(services.globals.turn_ttl()))
|
||||||
.expect("TURN TTL should not get this high"),
|
.expect("TURN TTL should not get this high"),
|
||||||
)
|
)
|
||||||
.expect("time is valid");
|
.expect("time is valid");
|
||||||
@@ -31,7 +32,7 @@ pub(crate) async fn turn_server_route(
|
|||||||
let user = body.sender_user.unwrap_or_else(|| {
|
let user = body.sender_user.unwrap_or_else(|| {
|
||||||
UserId::parse_with_server_name(
|
UserId::parse_with_server_name(
|
||||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
||||||
&services().globals.config.server_name,
|
&services.globals.config.server_name,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
@@ -46,15 +47,15 @@ pub(crate) async fn turn_server_route(
|
|||||||
(username, password)
|
(username, password)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
services().globals.turn_username().clone(),
|
services.globals.turn_username().clone(),
|
||||||
services().globals.turn_password().clone(),
|
services.globals.turn_password().clone(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(get_turn_server_info::v3::Response {
|
Ok(get_turn_server_info::v3::Response {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
uris: services().globals.turn_uris().to_vec(),
|
uris: services.globals.turn_uris().to_vec(),
|
||||||
ttl: Duration::from_secs(services().globals.turn_ttl()),
|
ttl: Duration::from_secs(services.globals.turn_ttl()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user